Threejs中局部裁剪的实现

836 阅读1分钟

threejs中通过在material中设置clippingPlanes:[]这个数组来实现局部裁剪,这其中的实现过程不是很清楚。通过解读源码,简单分析实现一下threejs中的裁剪。

场景中添加实体
    //这里采用normal(0,-1,0) constant = 2 带代替THREE.Plane();
    //...
     geometry = new THREE.BoxGeometry(5,5,5)
     material = new THREE.RawShaderMaterial({
        vertexShader:document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,
        side:THREE.DoubleSide,
        transparent:true,
        uniforms:{
            u_initNormalValue:{value:new THREE.Vector3(0,-1,0)},
            u_originPoint:{value:new THREE.Vector3()},
            u_viewNormalMatrix:{value:new THREE.Matrix3()},
            u_modelViewMatrix:{value:new THREE.Matrix4()}

        }
    })
    mesh = new THREE.Mesh(geometry,material);
    scene.add(mesh);
    scene.add(mesh1)
requestAnimationFrame中传入法向量和距离
     function coplanarPoint( target ) {
        // 这里的normal 和contant 分别使用固定值(0,-1,0)和2
        return target.copy( normal ).multiplyScalar( - constant );
    }
  
    // 获取视图矩阵
    const viewMatrix = camera.matrixWorldInverse;
    // 获取视图坐标系下的法向量转换矩阵
    const viewNormalMatrix = new THREE.Matrix3();
    viewNormalMatrix.getNormalMatrix( viewMatrix );
    
    // 获取共面点坐标
    const originPoint = material.uniforms.u_originPoint.value;
    // 计算视图坐标系下的共面点坐标
    const referencePoint = coplanarPoint( originPoint ).applyMatrix4( viewMatrix );
    
    // 获取法向量矩阵在视图坐标系下的值
    const normal_ = new THREE.Vector3(0,-1,0);
    const normal = normal_.applyMatrix3( viewNormalMatrix ).normalize();
    // 计算视图空间下,共面点在该方向上的投影距离
    const constant_ = -referencePoint.dot(normal);
    
    // 将值传递给到material
    material.uniforms.u_transformedNormal.value = normal;
    material.uniforms.u_transformedDistance.value = constant_;
    renderer.render(scene,camera);
    requestAnimationFrame(animate);
shader处理
    //顶点着色器
    varying vec3 clippingPosition;
    vec4 modelPosition = modelMatrix *vec4(position,1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    // 所有计算是在视图空间下进行
    clippingPosition =viewPosition.xyz;
    //片段着色器
    varying vec3 clippingPosition;
    uniform vec3 u_transformedNormal;
    uniform float u_transformedDistance;
   
    void main(){
        vec3 color = vec3(0.0,1.0,0.0);
        // 视图空间下,如果mesh片段在该方向上的投影距离大于constant = 2,则丢弃该片段
        if(dot( clippingPosition, -u_transformedNormal ) > u_transformedDistance){
            discard;
        }else{
            gl_FragColor = vec4(color,1.0);
        }  
    }
   
效果示意图

image.png