三:我们如何将本地加载的网格插入到由三生成的画布中以显示它?

Posted

技术标签:

【中文标题】三:我们如何将本地加载的网格插入到由三生成的画布中以显示它?【英文标题】:Three: How could we insert a locally loaded mesh into the canvas being generated by Three to display it? 【发布时间】:2018-08-04 15:30:48 【问题描述】:

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

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

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

作为一个外观示例:

但是,我想将前面的示例与 React 集成。

我研究了如何使用这个 SO 线程来关联 React 和 Three: Rendering three.js element in React?

现在我的代码如下所示:

/*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;

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

我已经检查了加载器工作正常并且模型就位,因为我们可以在控制台日志中看到它:

THREE.VolumeSlice
axis:"z"
canvas:canvas
canvasBuffer:canvas
... more properties ...

你能帮帮我吗?我错过了一些步骤吗?你以前遇到过这个问题吗?您如何尝试调试这种情况?

请注意,在分支 ThreeIntegrate 中是我正在编写的代码:https://github.com/PrototipoAtlas/Prototipo

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

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

结果是:

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

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

我又试了一次,看起来模型确实已经加载了,但是当我尝试更改 iot 的颜色,甚至点击它时,它会在控制台中显示:

THREE.MeshBasicMaterial: .shading has been removed. Use the boolean .flatShading instead.

我认为,解决方案可能是发现缺少的部分。如果我们再次查看有效的示例,我们有三个元素:

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

        document.body.appendChild(container);
        container.appendChild(renderer.domElement);
...more code...
        container.appendChild(stats.dom);

在没有显示模型的代码中,追加是:

this.mount.appendChild(this.renderer.domElement);
this.mount.removeChild(this.renderer.domElement)

即使在删除不常见的元素后,统计数据、普通 HTML/JS 示例也可以正常工作并显示三个元素!!

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

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

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


        scene.add(camera);

它提供了一个巨大的堆栈跟踪:

    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.

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;

如您所见,我已将名称添加到 sliceZ,并显示在 Web 控制台上:

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

我们看到有一个几何和一个纹理,并且渲染方法被调用了 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

我已经调试了一个工作示例,其中 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  … 

所以我们看到调用是 1,可能是代码中的问题表明对渲染器的渲染方法的调用被不断调用?

另外我已经上传了正在讨论的代码:

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

你能帮帮我吗?

【问题讨论】:

【参考方案1】:

有一个谷歌浏览器扩展three.js inspector,你可以很方便的安装和查看场景层次。顺便说一句,如果它在元素内,它无法找到您的三个对象。

【讨论】:

@Enoy 对不起,我不能直接对你的问题添加评论,所以我把它放在这里。检查器只显示 3 个 event dispatcher 对象而没有 Object3D 对象,这很奇怪,所以我怀疑它是否显示了您真正想要检查的正确 3d 画布。我建议在加载后明确命名您的 3d 对象,例如 `sliceZ.name=\'foo\'。顺便说一句,检查器并不是要显示 HTML DOM 结构,而是要显示 THREE.Scene 对象下的层次结构。 这很有趣。我听从了@Jim Tang 的建议,正如您在 EDIT2 上看到的那样,我在调试器上看到了多个“场景”,我的意思是,很多场景同时渲染,每个场景都有渲染器、相机和模型,但没有名字,你能帮我吗? 不知道为什么你的页面上有这么多场景。尝试添加一个保护标志以防止在您的 componentDidMount 回调中创建多个场景。您可以检查渲染器的 info 属性以帮助调试。请参阅document。 谢谢@Jim Tang,我正在尝试使用三个库的渲染器中提供的信息对象进行调试,并在最后一次编辑中展示了所有内容。 info.calls 是在单帧中调用渲染函数的次数。如果你的场景中只有一个简单的立方体,那么它可能是 1,没错。

以上是关于三:我们如何将本地加载的网格插入到由三生成的画布中以显示它?的主要内容,如果未能解决你的问题,请参考以下文章

如何将字符串字段添加到由流形成的字符串列表中

如何使用点图元将纹理映射到由参数方程渲染的球体

Unity小功能开发实战教程在UI画布上画网格线

将 Js 加载项添加到由 VSTO 加载项创建的自定义功能区选项卡

如何使用 PIL/Pillow 将图像合并到画布中?

MSBuild:如何将文件复制到由执行命令的输出确定的 DestinationFolder?