0

我正在尝试使用 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>

4

1 回答 1

0

ExtrudeGeometry 上的材料不会按照您期望的方式工作。引用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>
于 2021-03-15T09:09:03.763 回答