【问题标题】:THREEJS: How to render different models in seperate DIVs?THREEJS:如何在单独的 DIV 中渲染不同的模型?
【发布时间】:2019-05-31 11:05:21
【问题描述】:

(我是 Three.js 的新手)


说明

我正在为多个 stl 模型开发 3D 查看器。每个模型都应该在单独的 div 中呈现,因此您将获得一个包含不同模型的网格。 文件名存储在 json 文件中。使用 for 循环,我遍历 json 并将文件名存储在“var filename”中。我使用该变量来完成模型的路径。


详情

var filename = data[i].Filename;

通过 for 循环,我可以从 json 文件中获取所有文件名。

loader.load('model/' + filename, function(geometry) {...

然后我添加文件名以完成模型路径。

modelContainer = document.createElement('div');
modelContainer.className = modelClass;
document.getElementById(modelId).appendChild(modelContainer);

具有类名的 div 会在具有给定 ID 的 div 中创建。并且模型应该被渲染到 div (带有类的 div)内的画布中。


问题

See image 现在发生的情况是代码将所有模型(本例中为 3 个模型)放入最后一个 div。用于模型 1 和 2 的前两个 div 是黑色的。

经过大量的实验和分析,我还没有找到解决方案。 也许我必须将所有场景存储在一个数组中,并在渲染函数中循环所有场景?


完整的 Html 和 Javascript

您可以在此处找到完整的项目代码(不含 .stl 模型): https://codepen.io/indoguy/project/editor/XqNKGQ

<div class="container">
    <div class="row" id="modelGrid">
        <!-- The models will be uploaded within this grid -->
    </div>
</div>

<script src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
<script src="js/WebGL.js"></script>

<script>
    var modelDiv = document.getElementById('modelGrid');
    var ourRequest = new XMLHttpRequest();

    // In this code snippit I will read the model filenames out of the json.
    // Add URL where the json is stored
    ourRequest.open('GET', 'http://localhost:8888/3D%20Modeling/3D%20Previewer/3D%20Previewer%20Library/models.json');
    ourRequest.onload = function() {
        var queueData = JSON.parse(ourRequest.responseText);
        renderHTML(queueData);
    };
    ourRequest.send();

    function renderHTML(data) {
        // 3D Model Process
        if (WEBGL.isWebGLAvailable() === false) {
            document.body.appendChild(WEBGL.getWebGLErrorMessage());
        }

        // Editable
        var divWidth = 300;
        var divHeight = 300;
        var modelId = 'modelGrid';
        var modelClass = 'col-sm';

        // Don't change
        var modelContainer;
        var camera, cameraTarget, scene, renderer;
        var cameraFov = 45;


        init(data);
        animate();


        // init
        function init(data) {

            // For loop for every model in the json file
            for (i = 0; i < data.length; i++) {

                var filename = data[i].Filename;

                // Scene
                scene = new THREE.Scene();
                scene.background = new THREE.Color(0xffe5e5);

                // Camera
                camera = new THREE.PerspectiveCamera(cameraFov, divWidth / divHeight, 1, 15);
                cameraTarget = new THREE.Vector3(0, -0.25, 0);

                // Creates a div with the name of the value of var className, inside modelGrid id
                modelContainer = document.createElement('div');
                modelContainer.className = modelClass; // Add class to the div
                document.getElementById(modelId).appendChild(modelContainer);

                // STL Loader
                var loader = new THREE.STLLoader();
                loader.load('model/' + filename, function(geometry) {
                    var material = new THREE.MeshStandardMaterial({
                        color: 0xff1919,
                        roughness: 0.5,
                        metalness: 0
                    });
                    var mesh = new THREE.Mesh(geometry, material);
                    mesh.position.set(0, -0.5, 0); // Position with the Y value
                    mesh.rotation.set(0, -Math.PI / 2, 0);
                    mesh.scale.set(0.15, 0.15, 0.15);
                    scene.add(mesh); // add model to the scene
                });

                // Lights
                scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444));
                var light = new THREE.DirectionalLight(0xffffff, 0.5);
                light.position.set(1, 1, 1);
                scene.add(light);

                // renderer
                renderer = new THREE.WebGLRenderer({
                    antialias: true
                });
                renderer.setClearColor(0xffffff, 1);
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(divWidth, divHeight);
                // renderer.setSize(window.innerWidth, window.innerHeight);
                renderer.gammaInput = true;
                renderer.gammaOutput = true;
                renderer.shadowMap.enabled = true;
                modelContainer.appendChild(renderer.domElement);
                // window.addEventListener('resize', onWindowResize, false);
            }
        }

        // animate
        function animate() {
            render();
            requestAnimationFrame(animate);
        }

        // render
        function render() {
            renderer.setClearColor(0xffffff);
            var timer = Date.now() * 0.0005;
            camera.position.x = Math.cos(timer) * 3;
            camera.position.z = Math.sin(timer) * 3;
            camera.lookAt(cameraTarget);
            renderer.render(scene, camera);
        }
    };
</script>

【问题讨论】:

    标签: javascript three.js rendering render


    【解决方案1】:

    问题是您只保存和渲染单个 three.js 渲染器。在您的 for 循环中,这一行将覆盖全局 renderer 变量,该变量在您的 render() 函数中被引用:

    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    

    有几种方法可以解决这个问题,包括将渲染器和相机添加到一个数组中,并在单个渲染函数中对其进行迭代,或者为每个渲染器创建一个新的 animate() 循环等。这是第二个的粗略示例选项:

    // For loop for every model in the json file
    for (i = 0; i < data.length; i++) {
    
        var filename = data[i].Filename;
    
        // EDIT Fix3: These variables must be defined inside of the for loop 
        // scope otherwise they will be shared by all loop iterations
        var modelContainer;
        var camera, cameraTarget, scene;
    
        // ...
    
        // Fix 1: Add var declaration so the variable is scoped to this
        // block and variables are not defined globally and overwritten on
        // subsequent iterations. This is needed for all the variables
        // in this block (scene, camera, etc)
        // renderer
        var renderer = new THREE.WebGLRenderer({
            antialias: true
        });
    
        // ...
    
        // Fix 2: Create an animation loop for each renderer instead
        // of a single global one
        // animate
        function animate() {
            render();
            requestAnimationFrame(animate);
        }
    
        // render
        function render() {
            renderer.setClearColor(0xffffff);
            var timer = Date.now() * 0.0005;
            camera.position.x = Math.cos(timer) * 3;
            camera.position.z = Math.sin(timer) * 3;
            camera.lookAt(cameraTarget);
            renderer.render(scene, camera);
        }
    
        animate();    
    
    }
    

    肯定有更清洁的方法,但希望这能让你开始!让我知道这是否可行或您需要澄清。

    编辑:我刚刚注意到您的图像显示了所有三个模型都显示在彼此之上。这又是因为 scene 变量是全局声明的,并且在触发异步 load 函数时,它已设置为最终场景对象。

    【讨论】:

    • 嗨,谢谢您的意见!我理解您的意思并尝试将其应用于我的代码。它确实朝着正确的方向迈出了一步,因为现在左侧的两个框现在呈现为粉红色。所以这意味着如果我是正确的,场景就会被渲染。附言。我正在尝试用我的评论发送更新代码,但它不起作用。我知道我必须将我的代码放在``之间,但它说我使用了太多字符..
    • 所以我去掉了全局变量,在 for 循环的最后我调用了 animate 函数,并将 animate() 和 render() 函数放在了 init() 函数中。所以所有的 div 都有场景背景:scene.background = new THREE.Color(0xffe5e5); 但是所有 3 个模型仍然在最后一个 div 中渲染,相互堆叠,就像你在我的描述中看到的图像一样
    • 很高兴取得了进展——我刚刚用“Fix 3”更新了我的帖子,所以一定要看看。您已经摆脱了全局变量,但场景变量仍在 for 循环范围之外,这意味着它在循环的每次迭代期间仍会被覆盖,并且您将遇到与全局变量相同的问题。将它们移动到 for 循环范围内应该可以解决这个问题。
    • 嘿 Garrett 我看不到您的 Fix 3,并且场景变量是在 for 循环中创建的。我试图分析这段代码,因为它与我想要实现的目标非常接近,但我还没有成功实现它。 threejs.org/examples/#webgl_multiple_elements 在示例中,他们将场景推入 for 循环中的场景 scenes.push(scene); 然后他们在 render() 函数中使用 foreach,在该 foreach 中他们执行 renderer.render(scene, camera); 我还注意到渲染器在 for 之外循环但在 init() 函数内部。
    • 该示例要复杂得多,旨在绕过页面上 16 个 webgl 上下文的限制,同时使用单个渲染器。如果您需要,我会在继续处理类似问题之前进一步了解您当前的问题。此更新在代码块内的注释中标记为 EDIT Fix3:,并将所有变量声明(modelContainer、scene、camera、cameraTarget、renderer)移动到 for 循环内。当场景等在循环内被实例化时,变量被声明在范围之外,这意味着它们将被覆盖。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 2016-04-24
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2018-11-16
    相关资源
    最近更新 更多