三:我们如何将一个局部加载的网格插入由三个生成的画布中来显示它?

Eno*_*noy 7 javascript integration three.js reactjs

您好,感谢您阅读此问题:

我正在学习Threejs,目前我有一个奇怪的困难:

我已经学会了如何使用加载器加载格式为NRRD的本地文件,格式为HTML/JAVASCRIPT:这里是repo:https://github.com/YoneMoreno/LoadNRRDInThreeJSExample

作为它的样子的一个例子:

在此输入图像描述

但是,我想将前面的例子与React集成.

我已经研究了如何使用这个SO线程将React和Three联系起来: 在React中渲染three.js元素?

现在我的代码看起来像:

/*global THREE */

import React from 'react';

class LoadNRRD extends React.Component {

    constructor(props) {
        super(props)

        this.start = this.start.bind(this)
        this.stop = this.stop.bind(this)
        this.animate = this.animate.bind(this)
    }

    componentDidMount() {
        const width = this.mount.clientWidth
        const height = this.mount.clientHeight

        const scene = new THREE.Scene()
        const camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        )

        var loader = new THREE.NRRDLoader();
        loader.load("models/columnasegmentado01.nrrd", function (volume) {
            var sliceZ;


            //z plane

            var indexZ = 0;
            sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4))
            console.log(sliceZ);
            scene.add(sliceZ.mesh);

        });

        const renderer = new THREE.WebGLRenderer({antialias: true})
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);


        renderer.setClearColor('#000000')


        this.scene = scene
        this.camera = camera
        this.renderer = renderer

        let controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.rotateSpeed = 0;
        controls.zoomSpeed = 5;
        controls.panSpeed = 2;
        controls.noZoom = false;
        controls.noPan = false;
        controls.staticMoving = true;
        controls.dynamicDampingFactor = 0.3;


        this.mount.appendChild(this.renderer.domElement)
        this.start()
    }

    componentWillUnmount() {
        this.stop()
        this.mount.removeChild(this.renderer.domElement)
    }

    start() {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    }

    stop() {
        cancelAnimationFrame(this.frameId)
    }

    animate() {


        this.renderScene()
        this.frameId = window.requestAnimationFrame(this.animate)
    }

    renderScene() {
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div
                style={{width: '2000px', height: '2000px'}}
                ref={(mount) => {
                    this.mount = mount
                }}
            />
        )
    }
}

export default LoadNRRD;
Run Code Online (Sandbox Code Playgroud)

我不明白的部分是:为什么它显示画布但没有里面的模型?:

在此输入图像描述

我已经检查过加载器运行良好并且模型已经到位,因为我们可以在控制台日志中看到它:

THREE.VolumeSlice
axis:"z"
canvas:canvas
canvasBuffer:canvas
... more properties ...
Run Code Online (Sandbox Code Playgroud)

请问你能帮帮我吗?我错过了一步吗?你以前遇到过这个问题吗?你怎么能试图解决这种情况呢?

作为一个注释,在分支ThreeIntegrate是我正在写的代码:https://github.com/PrototipoAtlas/Prototipo

如果您有任何建议或建议,请告诉我

编辑:谢谢@Jim Tang的建议.我已经在全局变量window.scene中设置场景之后安装了Three Chrome扩展及其输出,如下所示:https: //github.com/jeromeetienne/threejs-inspector/issues/11

结果是: 在此输入图像描述

我尝试使用独立的纯HTML/JS示例,它的模型如下:

在此输入图像描述

那么关键问题是:为什么没有将模型与场景联系起来?‽‽

我再次尝试过,看起来模型确实已加载但是在控制台中,当我改变iot的颜色,或者甚至点击它时说:

THREE.MeshBasicMaterial: .shading has been removed. Use the boolean .flatShading instead.
Run Code Online (Sandbox Code Playgroud)

我认为,解决方案可能是关于什么是缺失的部分.如果我们再次看到有效的例子,我们有三个要素:

在此输入图像描述

我怀疑这三个元素可能是添加到DOM中的元素:

        document.body.appendChild(container);
        container.appendChild(renderer.domElement);
...more code...
        container.appendChild(stats.dom);
Run Code Online (Sandbox Code Playgroud)

在未显示模型的代码中,附加内容为:

this.mount.appendChild(this.renderer.domElement);
this.mount.removeChild(this.renderer.domElement)
Run Code Online (Sandbox Code Playgroud)

即使删除了正在追加的非常见元素,统计数据,纯HTML/JS示例也能正常工作并显示三个元素!

在此输入图像描述

我发现缺少的元素是相机,因为如果我们看到工作示例,对象有fov,near,far等:

在此输入图像描述

所以我试着在我们讨论的代码中将相机添加到场景中:

const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        );


        scene.add(camera);
Run Code Online (Sandbox Code Playgroud)

它提供了巨大而庞大的堆栈跟踪:

    TypeError: Cannot read property 'getExtension' of null
    get
    http://localhost:3001/build/three.js:20112:22
    initGLContext
    http://localhost:3001/build/three.js:21116:15
    new WebGLRenderer
    http://localhost:3001/build/three.js:21161:3
    LoadNRRD.componentDidMount
    http://localhost:3001/static/js/bundle.js:62599:28
      62596 |     scene.add(sliceZ.mesh);
      62597 | });
      62598 | 
    > 62599 | var renderer = new THREE.WebGLRenderer({ antialias: true });
            |                ^  62600 | renderer.setPixelRatio(window.devicePixelRatio);
      62601 | renderer.setSize(width, height);
      62602 | 
    View source
    ? 17 stack frames were collapsed.
    ./src/index.js
    C:/Users/YonePC/WebstormProjects/prototipo/src/index.js:22
      19 |     );
      20 | };
      21 | 
    > 22 | ReactDOM.render(
      23 |     <BrowserRouter>
      24 |         <App/>
      25 |     </BrowserRouter>
    View compiled
    ? 6 stack frames were collapsed.

TypeError: Cannot read property 'domElement' of undefined
LoadNRRD.componentWillUnmount
C:/Users/YonePC/WebstormProjects/prototipo/src/components/animals/LoadNRRD.js:73
  70 | 
  71 |    componentWillUnmount() {
  72 |        this.stop();
> 73 |        this.mount.removeChild(this.renderer.domElement)
  74 |    }
  75 | 
  76 |    start() {
View compiled
? 25 stack frames were collapsed.
./src/index.js
C:/Users/YonePC/WebstormProjects/prototipo/src/index.js:22
  19 |     );
  20 | };
  21 | 
> 22 | ReactDOM.render(
  23 |     <BrowserRouter>
  24 |         <App/>
  25 |     </BrowserRouter>
View compiled
? 6 stack frames were collapsed.
Run Code Online (Sandbox Code Playgroud)

EDIT2:

我再次尝试,我们看到:

在此输入图像描述

我们正在谈论的代码:

/*global THREE */

import React from 'react';

class LoadNRRD extends React.Component {

    constructor(props) {
        super(props);

        this.stop = this.stop.bind(this);
        this.start = this.start.bind(this);
        this.animate = this.animate.bind(this)
    }

    componentDidMount() {
        const width = this.mount.clientWidth;
        const height = this.mount.clientHeight;

        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        );


        scene.add(camera);

        const loader = new THREE.NRRDLoader();
        loader.load("models/columnasegmentado01.nrrd", function (volume) {
            let sliceZ;


            //z plane

            const indexZ = 0;
            sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));
            sliceZ.name = 'foo';
            console.log(sliceZ);
            scene.add(sliceZ.mesh);

        });

        var renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);


        renderer.setClearColor('#000000');


        this.scene = scene;
        window.scene = scene;
        this.camera = camera;
        this.renderer = renderer;

        let controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.rotateSpeed = 0;
        controls.zoomSpeed = 5;
        controls.panSpeed = 2;
        controls.noZoom = false;
        controls.noPan = false;
        controls.staticMoving = true;
        controls.dynamicDampingFactor = 0.3;


        this.mount.appendChild(this.renderer.domElement);
        this.start()
    }

    componentWillUnmount() {
        this.stop();
        this.mount.removeChild(this.renderer.domElement)
    }

    start() {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    }

    stop() {
        cancelAnimationFrame(this.frameId)
    }

    animate() {


        this.renderScene();
        this.frameId = window.requestAnimationFrame(this.animate)
    }

    renderScene() {
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div
                style={{width: '2000px', height: '2000px'}}
                ref={(mount) => {
                    this.mount = mount
                }}
            />
        )
    }
}

export default LoadNRRD;
Run Code Online (Sandbox Code Playgroud)

如您所见,我已将名称添加到sliceZ,并在Web控制台上显示: 在此输入图像描述

EDIT3:使用渲染器信息对象进行debbug:

我们看到有一个geomtery和da纹理,并且渲染方法被调用217次:

{render: {…}, memory: {…}, programs: Array(0), autoReset: false, reset: ƒ}
autoReset:false
memory:geometries:1 textures:1
__proto__:Object
programs:[WebGLProgram]
render:
calls:217
faces:434
frame:0
points:0
vertices:1302
__proto__:Object
reset:
ƒ resetInfo()
__proto__:
Object
Run Code Online (Sandbox Code Playgroud)

我已经调试了一个工作示例,其中React与Three集成:

https://codepen.io/anon/pen/eMYxZw

在控制台上我们看到info对象:

autoReset: false
?
memory: Object { geometries: 1, textures: 0 }
?
programs: Array [ {…} ]
?
render: Object { frame: 485, calls: 1, vertices: 36, … }
?
__proto__: Object { … }
Run Code Online (Sandbox Code Playgroud)

所以我们看到调用是1,可能是代码中的问题显示对渲染器渲染方法的调用是否被连续调用?

另外我上传了被讨论的代码:

https://github.com/YoneMoreno/ThreeReactNRRDLoaderStandalone.git

请问你能帮帮我吗?

小智 0

有一个 google chrome 扩展three.js inspector,您可以安装它并非常方便地检查场景层次结构。顺便说一句,如果它位于元素内部,它无法找到您的三个对象。