深度测试介绍
3维场景中要绘制物体的遮挡关系,就离不开深度测试
深度测试的简单解释(抛开透明等情况): 如果开启了深度测试,每个片元就要拿自己的深度值,跟深度缓冲区中对应位置的深度值比较,如果自己离屏幕更近(默认的比较方式),且开启了深度写入,就会把深度缓冲区中这块位置的深度值替换成自己的深度值,让自己成为标杆,并有机会展示到屏幕上,后面只有这块位置比自己离屏幕更近的,才有机会替换掉自己展示到屏幕上,如果没开启深度测试,则会按绘制顺序,后面绘制的覆盖前面的
three材质相关的参数可以影响深度测试,这些参数,作用于使用这个材质的物体,原理就是控制前面提到的深度相关的的开启与写入
示例代码介绍
后面的示例都会基于此,绘制存在遮挡关系的正方体跟圆柱体,绘制顺序很重要,先绘制的正方体,后绘制的圆柱体
代码片段
// 正方体
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
})
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
})
depthFunc
深度测试函数
作用:默认为LessEqualDepth,
材质使用这些深度函数来比较输入像素和缓冲器中深度的值。 如果比较的结果为true,则将绘制像素。
也就是说深度测试是有规则的,只是默认绘制最近的,可以修改规则
可选择的函数
- THREE.NeverDepth
- THREE.AlwaysDepth
- THREE.LessDepth
- THREE.LessEqualDepth
- THREE.GreaterEqualDepth
- THREE.GreaterDepth
- THREE.NotEqualDepth
源码部分
本质的调用的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
})