webgl 学习之 使用模板阴影体绘制贴地面(线)

171 阅读2分钟

一、算法

原理在很多地方都有介绍,这儿主要记录一下z-pass算法和z-fail算法步骤

1.1 z-pass 算法

image.png 如上图

z-pass算法的核心算法:

  • a.先正常绘制场景中的其他物体并构建阴影体
  • b.关闭颜色写入和深度写入,打开模板测试和剔除
  • c.设置模板测试总是通过并能写入
  • d.先绘制正面,当深度测试通过时模板值+1
  • e.绘制背面,当深度测试成功时模板值-1
  • f.关闭模板值写入,打开颜色和深度写入,设置只绘制模板值不为0的部分
  • g.关闭模板写入,恢复其他状态为默认

z-pass算法的缺陷是视点在阴影体内,那么产生的计数值就会出错,因为根本没有阴影体正面的深度测试

1.2 z-fail算法

image.png

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)


}

效果如下:

image.png