Flipping Face Normal ThreeJS 但不在地板上

Flipping Face Normal ThreeJS but not on floor

我尝试根据平面图建造一个房屋发电机。生成网格效果很好,但现在我需要翻转某些面上的法线。

buildRoomMeshFromPoints(planeScalar, heightScalar){
    var pointsAsVector2 = []
    this.points.map(e => {
        pointsAsVector2.push(new THREE.Vector2(e.x * planeScalar, e.y * planeScalar))
    })
    var shape = new THREE.Shape();

    shape.moveTo(pointsAsVector2[0].x, pointsAsVector2[0].y)
    pointsAsVector2.shift()
    pointsAsVector2.forEach(e => shape.lineTo(e.x, e.y))
    
    const extrusionSettings = {
        steps: 2,
        depth: heightScalar,
        bevelEnabled: false
    };
    
    var roomGeometry = new THREE.ExtrudeGeometry( shape, extrusionSettings );
    var materialFront = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
    var materialSide = new THREE.MeshBasicMaterial( { color: 0xff8800 } );
    var materialArray = [ materialFront, materialSide ];
    var roomMaterial = new THREE.MeshFaceMaterial(materialArray);

    var room = new THREE.Mesh(roomGeometry, roomMaterial);
    room.position.set(0,0,0);
    room.rotation.set(THREE.MathUtils.degToRad(-90),0,0)
    return room;
}

这是根据二维点集合生成房屋的代码。为了让墙壁看透,我想改变所有墙壁和屋顶的法线。

我的方法是将每个面法线角度与向上矢量 (THREE.Vector3(0,1,0)) 进行比较,如果角度大于 0.0xx,则翻转它。我根本不知道如何翻转它们:)

感谢您的帮助!

你好帕斯卡

用最简单的术语来说,“翻转”或找到法线(或任何)向量的负数是对其每个分量取反的问题。因此,如果您的法向量 n 是一个 THREE.Vector3 实例,那么它的负数是 n.multiplyScalar(-1),或者如果它在 [ x, y, z ] 形式的数组中,那么它的负数是 [ -1 * x, -1 * y, -1 * z ].

不过,翻转法线向量并不能完成您想要完成的所有工作。 Three.js(以及许多其他引擎和渲染器)中的法线与正在渲染的三角形的 的概念是分开的和不同的。因此,如果您 翻转矢量,Three.js 将继续渲染构成网格外部的三角形正面;不过,这些面孔会显得更暗,因为它们的反射方向完全错误。

对于每个三角形,你需要两者 (a) 翻转其顶点的法线;和 (b) 渲染该三角形的背面 反转三角形的正面。

要渲染三角形的背面,您可以设置.side property of your material to THREE.BackSide。 (我没有测试过这个,它可能有其他影响;除其他外,您可能会遇到代码库的其他部分,这些部分必须专门编写,并着眼于渲染背面的事实.)

一个更稳健的解决方案是让三角形本身面向另一个方向。

ExtrudeGeometryBufferGeometry 的工厂,顶点位置存储在生成几何体的 .attributes.position.array 属性 中的平面数组中。您可以将数组中的每第 3-5 个元素与每第 6-9 个元素交换以反转三角形的缠绕顺序,从而改变 Three.js 认为是正面的一侧。因此,定义为 (0, 0, 0), (1, 0, 1), (1, 1, 1) 并在数组中表示为 [ 0, 0, 0, 1, 0, 1, 1, 1, 1 ] 的三角形变为 (0, 0, 0), ( 1, 1, 1), (1, 0, 1) 和 [ 0, 0, 0, 1, 1, 1, 1, 0, 1 ]。 (换句话说,ABC 变成 ACB。)

要在代码中完成此操作,需要类似以下内容。

   /**
    * @param { import("THREE").BufferGeometry } geom
    * @return { undefined }
    */
   flipSides = (geom) => {
        const positions = geom.getAttribute("position");
        const normals = geom.getAttribute("normal");
        const newNormals = Array.from(normals.array);

        for (let attrName of ["position", "normal", "uv"]) {
            // for (let i = 0; i < positions.count; i += 3) {
            // ExtrudeGeometry generates a non-indexed BufferGeometry.  To flip
            // the faces, we must reverse the winding order, i.e., for each triangle
            // ABC, we must change it to ACB.  We must do this for the position,
            // normal, and uv buffers.
            const attr = geom.getAttribute(attrName);
            let newArr = Array.from(attr.array)
            const sz = attr.itemSize;
            for (let i = 0; i < attr.count; i++) {
                const offset = sz * 3 * i;
                // i is the index of the first of three vertices of a triangle.
                // Sample the buffer for the second and third vertices, which
                // we'll swap.  
                const tempB = newArr.slice(
                    offset + sz,
                    offset + 2 * sz
                );
                const tempC = newArr.slice(
                    offset + 2 * sz,
                    offset + 3 * sz
                );
                newArr.splice(offset + sz, sz, ...tempC);
                newArr.splice(offset + 2 * sz, sz, ...tempB);
            }
            // If we're working on the normals buffer, we also need to reverse
            // the normals.  Since reversing a vector simply entails a 
            // scalar-vector multiplication by -1, and since the array is 
            // flat, we can do this with one map() operation.
            if (attrName == "normal") {
                newArr = newArr.map((e) => e * -1);
            }
            // Replace the position buffer with our new array
            geom.setAttribute(
                attrName,
                new THREE.BufferAttribute(
                    Float32Array.from(newArr),
                    sz
                ));
            attr.needsUpdate = true;
        }

我已经在 CodePen 上发布了这种方法的 demonstration