【问题标题】:ThreeJs: Unable to load different image on the top and bottom sidesThreeJs:无法在顶部和底部加载不同的图像
【发布时间】:2021-03-15 23:13:19
【问题描述】:

我正在尝试在 Threejs 中使用纹理在顶部和底部放置不同的图像。 但是我在底部和顶部都得到了相同的图像。 下面是我正在使用的代码。我的要求是在 ThreeJs 中使用纹理在两边显示不同的图像。

注意: 1. 在我的代码中,我使用了 ThreeJs 版本(r125),这是必需的,我不能使用旧版本。

<html lang="en">

    <head>
        <title>three.js webgl - geometry - shapes</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
        <style>
            body {
                background-color: #f0f0f0;
                color: #444;
            }
        </style>
    </head>

    <body>

        <div id="info">texture on shapes</div>

        <script type="module">
            import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r125/build/three.module.js';
            import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r125/examples/jsm/controls/OrbitControls.js";

            let container;
            let camera, scene, renderer;
            let group;
            let windowHalfX = window.innerWidth / 2;

            init();
            animate();

            function init() {

                container = document.createElement('div');
                document.body.appendChild(container);

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

                camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
                camera.position.set(0, -150, -300);
                scene.add(camera);

                const light = new THREE.PointLight(0xffffff, 0.8);
                camera.add(light);

                group = new THREE.Group();
                group.rotation.y = Math.PI;
                scene.add(group);

                const helper = new THREE.GridHelper(500, 10);
                helper.rotation.x = Math.PI / 2;
                group.add(helper);

                const rectWidth = 120;
                const rectHeight = 200;

                const rectangleShape = new THREE.Shape()
                    .moveTo(0, 0)
                    .lineTo(0, rectHeight)
                    .lineTo(rectWidth, rectHeight)
                    .lineTo(rectWidth, 0)
                    .lineTo(0, 0);

                const extrudeSettings = { depth: 10, bevelEnabled: true, bevelSegments: 2, steps: 1, bevelSize: 1, bevelThickness: 1 };

                const geometry = new THREE.ExtrudeGeometry(rectangleShape, extrudeSettings);

                var textureLoader1 = new THREE.TextureLoader();
                var topTexture = textureLoader1.load("https://threejsfundamentals.org/threejs/resources/images/tree-01.png"); // Top side Image
                topTexture.repeat.set(1 / rectWidth, 1 / rectHeight);

                var textureLoader2 = new THREE.TextureLoader();
                var bottomTexture = textureLoader2.load("https://threejsfundamentals.org/threejs/resources/images/tree-02.png"); // Bottom side Image
                bottomTexture.repeat.set(1 / rectWidth, 1 / rectHeight);

                var frontMaterial = new THREE.MeshPhongMaterial({ map: topTexture, side: THREE.FrontSide });
                var backMaterial = new THREE.MeshPhongMaterial({ map: bottomTexture, side: THREE.BackSide });
                var sideMaterial = new THREE.MeshPhongMaterial({ color: 0xD9D934 });

                var materials = [frontMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, backMaterial];
                var material = new THREE.MeshFaceMaterial(materials);

                let mesh = new THREE.Mesh(geometry, material);
                mesh.position.set(-60, -100, 0);
                // Edited but faces not found
                for (var face in mesh.geometry.faces) {
                    if (mesh.geometry.faces[face].normal.z < -0.9) {
                        mesh.geometry.faces[face].materialIndex = 5;
                    }
                }
                group.add(mesh);
            
                renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(window.innerWidth, window.innerHeight);
                container.appendChild(renderer.domElement);

                const controls = new OrbitControls(camera, renderer.domElement);
                controls.target.set(0, 0, 0);
                controls.update();

                window.addEventListener('resize', onWindowResize);

            }

            function onWindowResize() {

                windowHalfX = window.innerWidth / 2;

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize(window.innerWidth, window.innerHeight);

            }

            function animate() {

                requestAnimationFrame(animate);

                render();

            }

            function render() {
                renderer.render(scene, camera);
            }

        </script>

    </body>
</html>

【问题讨论】:

    标签: javascript html three.js image-rendering


    【解决方案1】:

    ExtrudeGeometry 上的材料无法按您预期的方式工作。引用documentation for ExtrudeGeometry

    使用此几何体创建网格时,如果您想为其面和拉伸侧面使用单独的材质,您可以使用一系列材质。第一种材料将应用于面部;第二种材料将应用于侧面。

    如果需要,您可以添加第三种材质,然后通过手动更改该面的材质索引将其设置在其中一个面上。但据我所知,没有办法通过构造函数自动执行此操作。

    编辑:更正我自己,我相信您不能再这样做(更改单个面的材质索引),因为 ExtrudeGeometry 的实现方式发生了变化。相反,我相信您现在必须使用单个纹理并创建 UV 贴图。

    您的代码已更新为使用 UV 贴图,该贴图使用一张图像的上半部分作为 ExtrudeGeometry 顶面的纹理,下半部分作为底面的纹理。这是通过定义自定义 UV 生成器并将其添加到传递给 ExtrudeGeometry 的配置来完成的。 UV 生成器提供两种方法:generateTopUV() 和 generateSideWallUV()。后者与 THREE 提供的默认值相同。另一个是对几何体的顶部和底部进行 UV 映射。

    请注意,这是一个非常不便携的组件,它针对这个特定问题的几何形状进行了硬编码。

    <html lang="en">
    
    <head>
        <title>three.js webgl - geometry - shapes</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
        <style>
            body {
                background-color: #f0f0f0;
                color: #444;
            }
        </style>
    </head>
    
    <body>
    
        <div id="info">texture on shapes</div>
    
        <script type="module">
            import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r125/build/three.module.js';
            import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r125/examples/jsm/controls/OrbitControls.js";
    
            let container;
            let camera, scene, renderer;
            let group;
            let windowHalfX = window.innerWidth / 2;
    
            init();
            animate();
    
            function init() {
    
                container = document.createElement('div');
                document.body.appendChild(container);
    
                scene = new THREE.Scene();
                scene.background = new THREE.Color(0xbbbbbb);
    
                camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
                camera.position.set(0, -150, -300);
                scene.add(camera);
                const light = new THREE.PointLight(0xffffff, 0.8);
                camera.add(light);
    
                group = new THREE.Group();
                group.rotation.y = Math.PI;
                scene.add(group);
    
                const helper = new THREE.GridHelper(500, 10);
                helper.rotation.x = Math.PI / 2;
                group.add(helper);
    
                const rectWidth = 120;
                const rectHeight = 200;
    
                const rectangleShape = new THREE.Shape()
                    .moveTo(0, 0)
                    .lineTo(0, rectHeight)
                    .lineTo(rectWidth, rectHeight)
                    .lineTo(rectWidth, 0)
                    .lineTo(0, 0);
    
                const uvGenerator = {
                        generateTopUV:  function(geometry, vertices, indexA, indexB, indexC) {
                                const ax = vertices[indexA * 3];
                                const ay = vertices[indexA * 3 + 1];
                                const az = vertices[indexA * 3 + 2];
                                const bx = vertices[indexB * 3];
                                const by = vertices[indexB * 3 + 1];
                                const bz = vertices[indexB * 3 + 2];
                                const cx = vertices[indexC * 3];
                                const cy = vertices[indexC * 3 + 1];
                                const cz = vertices[indexC * 3 + 2];
                                if(indexA > 3) {
                                        return([
                                                new THREE.Vector2(ax, ay / 2),
                                                new THREE.Vector2(bx, by / 2),
                                                new THREE.Vector2(cx, cy / 2),
                                        ]);
                                } else {
                                        return([
                                                new THREE.Vector2(ax, (ay / 2) + rectHeight / 2),
                                                new THREE.Vector2(bx, (by / 2) + rectHeight / 2),
    
                                                new THREE.Vector2(cx, (cy / 2) + rectHeight / 2),
                                        ]);
                                }
                        },
                        generateSideWallUV: function(geometry, vertices, indexA, indexB, indexC, indexD) {
                                const ax = vertices[indexA * 3];
                                const ay = vertices[indexA * 3 + 1];
                                const az = vertices[indexA * 3 + 2];
                                const bx = vertices[indexB * 3];
                                const by = vertices[indexB * 3 + 1];
                                const bz = vertices[indexB * 3 + 2];
                                const cx = vertices[indexC * 3];
                                const cy = vertices[indexC * 3 + 1];
                                const cz = vertices[indexC * 3 + 2];
                                const dx = vertices[indexD * 3];
                                const dy = vertices[indexD * 3 + 1];
                                const dz = vertices[indexD * 3 + 2];
                                if(Math.abs(ay - by) < 0.01) {
                                        return([
                                                new THREE.Vector2(ax, 1 - az),
                                                new THREE.Vector2(bx, 1 - bz),
                                                new THREE.Vector2(cx, 1 - cz),
                                                new THREE.Vector2(dx, 1 - dz),
                                        ]);
                                } else {
                                        return([
                                                new THREE.Vector2(ay, 1 - az),
                                                new THREE.Vector2(by, 1 - bz),
                                                new THREE.Vector2(cy, 1 - cz),
                                                new THREE.Vector2(dy, 1 - dz),
                                        ]);
                                }
                        },
                };
    
                const extrudeSettings = { depth: 10, bevelEnabled: true, bevelSegments: 2, steps: 1, bevelSize: 1, bevelThickness: 1, UVGenerator: uvGenerator };
    
                const geometry = new THREE.ExtrudeGeometry(rectangleShape, extrudeSettings);
    
                var textureLoader1 = new THREE.TextureLoader();
                var topTexture = textureLoader1.load("https://threejsfundamentals.org/threejs/resources/images/tree-01.png"); // Top side Image
                topTexture.repeat.set(1 / rectWidth, 1 / rectHeight);
    
                var frontMaterial = new THREE.MeshPhongMaterial({ map: topTexture, side: THREE.FrontSide });
                var sideMaterial = new THREE.MeshPhongMaterial({ color: 0xD9D934 });
    
                var materials = [frontMaterial, sideMaterial];
                var material = new THREE.MeshFaceMaterial(materials);
    
                let mesh = new THREE.Mesh(geometry, material);
                mesh.position.set(-60, -100, 0);
                group.add(mesh);
            
                renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(window.innerWidth, window.innerHeight);
                container.appendChild(renderer.domElement);
    
                const controls = new OrbitControls(camera, renderer.domElement);
                controls.target.set(0, 0, 0);
                controls.update();
                window.addEventListener('resize', onWindowResize);
    
            }
    
            function onWindowResize() {
    
                windowHalfX = window.innerWidth / 2;
    
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
    
                renderer.setSize(window.innerWidth, window.innerHeight);
    
            }
    
            function animate() {
    
                requestAnimationFrame(animate);
    
                render();
    
            }
            function render() {
                renderer.render(scene, camera);
            }
    
        </script>
    
    </body>
    

    【讨论】:

    • 我从用户 (Priyank) 的stackoverflow.com/questions/40023065/… 中找到了您建议的类似解决方案,但在这里我得到 mesh.geometry.faces 未定义,因此不会执行 for-loop。跨度>
    • 是的,这就是我编辑的重点。这就是它在以前版本中的工作方式,但现在我相信完成同样事情的唯一方法是使用 UV 贴图。
    • 用您的示例的编辑版本更新了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2014-06-14
    • 2015-03-09
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多