three.js模型贴花贴标效果DecalGeometry

版权声明:本文为作者的原创文章,未经允许不得转载。 https://blog.csdn.net/lin5165352/article/details/88665600

three.js模型贴花贴标效果DecalGeometry

three.js官网很早之前就有这个案例:https://threejs.org/examples/webgl_decals.html
只是看的很神奇,没有深入研究。因为没有想到可以用到什么地方。后来想到可以给模型局部贴牌子,比如衣服贴logo,图片文字什么的,很方便。
在这里插入图片描述

DecalGeometry

贴花几何可以用来创建贴花网格,用于不同的用途,例如为模型添加独特的细节,执行动态视觉环境变化或覆盖接缝。

var geometry =  new THREE.DecalGeometry( meshOne, position, orientation, size );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

构造函数:
DecalGeometry( mesh : Mesh, position : Vector3, orientation : Euler, size : Vector3 )
mesh 网格-任何网格对象。需要传入一个网格对象。
position 位置-贴花投影仪的位置。投影的位置就是网格表面的位置,用射线获取准确位置。
orientation 方向-贴花投影仪的方向。方向投影点的法线方向。
size 贴花投影仪的尺寸。

下图是我做的案例。用了一个立方图和一个球体。发现在立方体的边缘的部分添加DecalGeometry会出现问题。因为边缘部分法线突变。所以会出现错误(其实是投影原理造成的)。不过这种情况比较极端。在球体表面贴还是不成问题的。

这个贴花在opengl中叫投影纹理,可以自己百度看看原理。
ProjectiveTexturing(投影纹理)https://blog.csdn.net/qq_29523119/article/details/53001070
在这里插入图片描述
在这里插入图片描述

主要代码

投影纹理的关键点在于投影坐标和投影法线。
主要代码是在射线部分,求得点击位置的交点坐标和法线。然后用一根线可视化的表现出来。

        var mouseHelper;
        var line;
        var position = new THREE.Vector3();
        var orientation = new THREE.Euler();
        var size = new THREE.Vector3( 10, 10, 10 );
        var intersection = {
            intersects: false,
            point: new THREE.Vector3(),
            normal: new THREE.Vector3()
        };
        var mouse = new THREE.Vector2();

        var textureLoader = new THREE.TextureLoader();
        var decalDiffuse = textureLoader.load( '../img/decal/decal-diffuse.png' );
        var decalNormal = textureLoader.load( '../img/decal/decal-normal.jpg' );

        var decalMaterial = new THREE.MeshPhongMaterial( {
            color:0xfff000 * Math.random(),
            specular: 0x444444,
            map: decalDiffuse,
            normalMap: decalNormal,
            normalScale: new THREE.Vector2( 1, 1 ),
            shininess: 30,
            transparent: true,
            depthTest: true,
            depthWrite: false,
            polygonOffset: true,
            polygonOffsetFactor: - 4,
            wireframe: false
        } );

            mouseHelper = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 10 ), new THREE.MeshNormalMaterial() );
            mouseHelper.visible = false;
            scene.add( mouseHelper );
            var geometry = new THREE.BufferGeometry();
            geometry.setFromPoints( [ new THREE.Vector3(), new THREE.Vector3() ] );

            line = new THREE.Line( geometry, new THREE.LineBasicMaterial() );
            scene.add( line );

		//贴花
        function decalGeo(obj) {

            position.copy(intersection.point);
            orientation.copy( mouseHelper.rotation );
            var material = decalMaterial.clone();
            material.color.setHex( Math.random() * 0xffffff );

            var geometry = new THREE.DecalGeometry(obj, position,orientation, new THREE.Vector3(3, 3, 3));

            var mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);
        }

        window.addEventListener('click', onClick, false);
        window.addEventListener('mousemove',onTouchMove);
        function onTouchMove(event) {
            var x, y;
            if ( event.changedTouches ) {
                x = event.changedTouches[ 0 ].pageX;
                y = event.changedTouches[ 0 ].pageY;
            } else {
                x = event.clientX;
                y = event.clientY;
            }
            mouse.x = ( x / window.innerWidth ) * 2 - 1;
            mouse.y = - ( y / window.innerHeight ) * 2 + 1;

            onDocumentMouseDown();
        }
        function onClick() {
            var obj = onDocumentMouseDown();
            if(!obj) return;
            decalGeo(obj);
        }

        function onDocumentMouseDown() {           
            var raycaster = new THREE.Raycaster();
            raycaster.setFromCamera( mouse, camera );

            let intersects = raycaster.intersectObjects([cube1, sphere1]);
            if (intersects.length > 0) {
                var p = intersects[ 0 ].point;
                mouseHelper.position.copy( p );
                intersection.point.copy( p );
                var n = intersects[ 0 ].face.normal.clone();
                n.transformDirection( mesh.matrixWorld );
                n.multiplyScalar( 10 );
                n.add( intersects[ 0 ].point );
                intersection.normal.copy( intersects[ 0 ].face.normal );
                mouseHelper.lookAt( n );
                var positions = line.geometry.attributes.position;
                positions.setXYZ( 0, p.x, p.y, p.z );
                positions.setXYZ( 1, n.x, n.y, n.z );
                positions.needsUpdate = true;

                intersection.intersects = true;

                return intersects[0].object;
            }
        }

上面的代码有个地方有个问题,就是n.transformDirection( mesh.matrixWorld );这个里面的mesh应该改成intersects[0].object,否则当模型发生旋转之后他的法线没有更新就会出现错误。
这段代码是求当前点击面的法线。

  1. 克隆旧的法线

  2. 旧法线乘以模型的世界矩阵得到归一化后的法线

  3. 和一个标量相乘,放大10倍。

  4. 加上点击点的坐标,进行平移。

            var n = intersects[ 0 ].face.normal.clone();
            n.transformDirection( intersects[0].object.matrixWorld );
            n.multiplyScalar( 10 );
            n.add( intersects[ 0 ].point );                
            mouseHelper.lookAt( n );
    

最后得到的n点就是线段一个顶点的坐标。
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lin5165352/article/details/88665600