一、算法
原理在很多地方都有介绍,这儿主要记录一下z-pass算法和z-fail算法步骤
1.1 z-pass 算法
如上图
z-pass算法的核心算法:
- a.先正常绘制场景中的其他物体并构建阴影体
- b.关闭颜色写入和深度写入,打开模板测试和剔除
- c.设置模板测试总是通过并能写入
- d.
先绘制正面,当深度测试通过时模板值+1 - e.
绘制背面,当深度测试成功时模板值-1 - f.关闭模板值写入,打开颜色和深度写入,设置只绘制模板值不为0的部分
- g.关闭模板写入,恢复其他状态为默认
z-pass算法的缺陷是视点在阴影体内,那么产生的计数值就会出错,因为根本没有阴影体正面的深度测试
1.2 z-fail算法
z-fail算法与z-pass算法只有上述d,e步骤不同:
- d.
先绘制背面,当深度测试失败时模板值+1 - e.
绘制正面,当深度测试失败时模板值-1
二、绘制贴地面
本次绘制采用一个立方体作为阴影体,使用z-fail算法绘制贴地面核心代码如下:
function draw() {
currentAngle += 0.5;
gl.clearColor(0, 0, 0, 1);
context.clear();
gl.enable(gl.DEPTH_TEST);
planeGeometry.modelMatrix.setRotate(-90,1,0,0)
planeGeometry.modelMatrix.scale(2,2,1)
const uniformMapColor = {
u_viewProjectMatrix: camera.viewProjectMatrix,
u_modelMatrix: planeGeometry.modelMatrix,
u_texture:domTexture,
u_demTexture:demTexture
};
drawCommand1.uniformMap = uniformMapColor;
drawCommand1.execute();
boxGeometry.modelMatrix.setScale(0.5,0.5,0.3)
const uniformMap1 = {
u_viewProjectMatrix: camera.viewProjectMatrix,
u_modelMatrix: boxGeometry.modelMatrix,
};
drawCommand0.uniformMap = uniformMap1;
//关闭深度写入和颜色写入
gl.colorMask(false, false, false, false);
gl.depthMask(false);
//打开模板测试
gl.enable(gl.STENCIL_TEST)
//设置模板测试总是通过
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
//允许写入模板
gl.stencilMask(0xFF);
gl.enable(gl.CULL_FACE)
//正面剔除,只绘制背面,
gl.cullFace(gl.FRONT);
//当深度测试失败时+1
gl.stencilOp(gl.KEEP, gl.INCR, gl.KEEP);
//绘制
drawCommand0.execute();
//背面剔除,只绘制正面,
gl.cullFace(gl.BACK);
//当深度测试失败时-1
gl.stencilOp(gl.KEEP, gl.DECR, gl.KEEP);
//绘制
drawCommand0.execute();
gl.colorMask(true, true, true, true);
gl.depthMask(true);
//只绘制模板内的
gl.stencilFunc(gl.EQUAL, 1, 0xFF);
//关闭模板写入
gl.stencilMask(0x00);
//绘制阴影体
drawCommand0.execute();
//恢复默认状态
gl.disable(gl.STENCIL_TEST)
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
gl.disable(gl.CULL_FACE)
}
效果如下: