看懂Threejs材质中深度测试相关参数

2,674 阅读3分钟

深度测试介绍

3维场景中要绘制物体的遮挡关系,就离不开深度测试

深度测试的简单解释(抛开透明等情况): 如果开启了深度测试,每个片元就要拿自己的深度值,跟深度缓冲区中对应位置的深度值比较,如果自己离屏幕更近(默认的比较方式),且开启了深度写入,就会把深度缓冲区中这块位置的深度值替换成自己的深度值,让自己成为标杆,并有机会展示到屏幕上,后面只有这块位置比自己离屏幕更近的,才有机会替换掉自己展示到屏幕上,如果没开启深度测试,则会按绘制顺序,后面绘制的覆盖前面的

three材质相关的参数可以影响深度测试,这些参数,作用于使用这个材质的物体,原理就是控制前面提到的深度相关的的开启写入

示例代码介绍

后面的示例都会基于此,绘制存在遮挡关系的正方体跟圆柱体,绘制顺序很重要,先绘制的正方体,后绘制的圆柱体

image.png

代码片段

// 正方体
const cubeGeometry = new THREE.BoxGeometry(200, 200, 200)
// 正方体材质
const cubeMaterial = new THREE.MeshLambertMaterial({
  color: 0x00ff00
})
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
scene.add(cube)

// 圆柱体
const cyGeometry = new THREE.CylinderGeometry(50, 50, 300, 32)
const cyMaterial = new THREE.MeshLambertMaterial({
  color: 0xffff00
})
// 圆柱体材质
const cylinder = new THREE.Mesh(cyGeometry, cyMaterial)
cylinder.rotateX(Math.PI / 2)
scene.add(cylinder)

深度测试相关参数介绍

depthTest

作用:决定在渲染此材质时是否启用深度测试,

源码部分

开启深度测试本质是调用的gl.enable(gl.DEPTH_TEST)

setTest: function ( depthTest ) {

   if ( depthTest ) {

      enable( gl.DEPTH_TEST );

   } else {

      disable( gl.DEPTH_TEST );

   }

}
function enable( id ) {

   if ( enabledCapabilities[ id ] !== true ) {

      gl.enable( id );
      enabledCapabilities[ id ] = true;

   }

}
function disable( id ) {

   if ( enabledCapabilities[ id ] !== false ) {

      gl.disable( id );
      enabledCapabilities[ id ] = false;

   }

}

效果展示

使用此参数使圆柱不被遮挡,可以关闭圆柱的深度测试,这样圆柱的绘制,就变成了不按照前后位置关系绘制,而是按照渲染的前后顺序绘制,这样后绘制的圆柱就可以覆盖正方体了

const cyMaterial = new THREE.MeshLambertMaterial({
  ...
  depthTest: false
})

image.png

depthWrite

作用:是否可以深度写入

源码部分

本质调用的是gl.depthMask( false ),使深度缓冲区变为只读

depthBuffer.setMask( material.depthWrite );
setMask: function ( depthMask ) {

   if ( currentDepthMask !== depthMask && ! locked ) {

      gl.depthMask( depthMask );
      currentDepthMask = depthMask;

   }

},

效果展示

上一节为了让圆柱不被遮挡,是处理了圆柱,我们也可以处理正方体使其挡不住圆柱,阻止正方体的深度值写入,这样深度缓冲区中对应位置存的是圆柱的深度信息,圆柱变成了离屏幕最近的,就不会被遮挡了

const cubeMaterial = new THREE.MeshLambertMaterial({
  ...
  depthWrite: false
})

image.png

depthFunc

深度测试函数
作用:默认为LessEqualDepth, 材质使用这些深度函数来比较输入像素和缓冲器中深度的值。 如果比较的结果为true,则将绘制像素。
也就是说深度测试是有规则的,只是默认绘制最近的,可以修改规则

可选择的函数

  • THREE.NeverDepth
  • THREE.AlwaysDepth
  • THREE.LessDepth
  • THREE.LessEqualDepth
  • THREE.GreaterEqualDepth
  • THREE.GreaterDepth
  • THREE.NotEqualDepth

image.png

源码部分

本质的调用的gl.depthFunc

setFunc: function ( depthFunc ) {

   if ( currentDepthFunc !== depthFunc ) {

      if ( depthFunc ) {

         switch ( depthFunc ) {

            case NeverDepth:

               gl.depthFunc( gl.NEVER );
               break;

            case AlwaysDepth:

               gl.depthFunc( gl.ALWAYS );
               break;

            case LessDepth:

               gl.depthFunc( gl.LESS );
               break;

            case LessEqualDepth:

               gl.depthFunc( gl.LEQUAL );
               break;

            case EqualDepth:

               gl.depthFunc( gl.EQUAL );
               break;

            case GreaterEqualDepth:

               gl.depthFunc( gl.GEQUAL );
               break;

            case GreaterDepth:

               gl.depthFunc( gl.GREATER );
               break;

            case NotEqualDepth:

               gl.depthFunc( gl.NOTEQUAL );
               break;

            default:

               gl.depthFunc( gl.LEQUAL );

         }

      } else {

         gl.depthFunc( gl.LEQUAL );

      }

      currentDepthFunc = depthFunc;

   }

}

效果展示

修改圆柱的测试函数为NotEqualDepth,因为取的差集,圆柱被遮挡部分也显示出来了,但是重叠部分会显示正方体的部分,也就是那条绿色切割线

const cyMaterial = new THREE.MeshLambertMaterial({
....
  depthFunc: THREE.NotEqualDepth
})

image.png