商城网站建设第一章,东莞网站建设营销服务平台,移动网站建设厂家,制作wordpress模板教程视频模板测试(Stencil Test)#xff1a;
当片段着色器处理完一个片段之后#xff0c;模板测试(Stencil Test)会开始执行#xff0c;和深度测试一样#xff0c;它也可能会丢弃片段。接下来#xff0c;被保留的片段会进入深度测试#xff0c;它可能会丢弃更多的片段。模板测试…模板测试(Stencil Test)
当片段着色器处理完一个片段之后模板测试(Stencil Test)会开始执行和深度测试一样它也可能会丢弃片段。接下来被保留的片段会进入深度测试它可能会丢弃更多的片段。模板测试是根据又一个缓冲来进行的它叫做模板缓冲(Stencil Buffer)我们可以在渲染的时候更新它来实现一些很有意思的效果。
一个模板缓冲中通常每个模板值(Stencil Value)是8位的。所以每个像素/片段一共能有256种不同的模板值。我们可以将这些模板值设置为我们想要的值然后当某一个片段有某一个模板值的时候我们就可以选择丢弃或是保留这个片段了。
模板缓冲的一个简单的例子如下 模板缓冲首先会被清除为0之后在模板缓冲中使用1填充了一个空心矩形。场景中的片段将会只在片段的模板值为1的时候会被渲染其它的都被丢弃了。
每个窗口库都需要为你配置一个模板缓冲。GLFW自动做了这件事所以我们不需要告诉GLFW来创建一个但其它的窗口库可能不会默认给你创建一个模板库所以记得要查看库的文档。
测试和混合发生在渲染流程中的最后一个阶段在这个阶段里GPU主要的工作是逐片元操作将片元的颜色以某种形式合并得到最终在屏幕上显示的像素颜色。
在webgl中的测试有裁剪测试、透明的测试、模板测试以及深度测试。这几个测试都是高度可配置的测试流程如下图 模板缓冲:
通常用户在启用模板缓冲的时候会将整个模板缓冲中的所有片段模板值设置为0从而丢弃所有的片段然后再设置特定区域的模板值以及比较函数。GPU会读取用户设置的模板值然后将该值和模板缓冲中该位置的模板值按比较函数进行比较最终决定是保留还是舍弃该片段形成遮罩效果。在模板测试中有两个很重要的方法是stencilFunc和stencilOpstencilFunc用来控制stencil的测试方式得出测试结果。stencilOp根据结果决定要如何处理缓冲中的数据。
glStencilFunc glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三个参数
func设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。可用的选项有GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。ref设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。mask设置一个掩码它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。
测试的时候ref会先和mask做与运算再将模板缓冲中的值与mask做与运算最后把这两个与运算的值代入比较函数得出结果。
所有的比较函数如下 举个例子
glStencilFunc(gl.GEQUAL, 1, 0xFF) 先将参考值1与0xff做与运算得到运算结果1将运算结果再与模板缓冲和0xff进行与运算的值进行比较。判断是否满足前者大于后者如果是的话模板测试成功否则失败。
mask值设为0xff的时候就等于直接拿参考值和模板缓冲值做比较。
想要禁用模板也可以将mask设置为0x00
经历了glStencilFunc之后我们就知道模板测试是不是通过。接下来就要对模板缓冲进行操作这就需要 glStencilOp这个函数了。
glStencilOp
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项我们能够设定每个选项应该采取的行为
sfail模板测试失败时采取的行为。dpfail模板测试通过但深度测试失败时采取的行为。dppass模板测试和深度测试都通过时采取的行为。
每个选项都可以选用以下的其中一种行为
行为描述gl.KEEP保持当前储存的模板值gl.ZERO将模板缓冲值设置为0gl.REPLACE将模板缓冲区值设置为glStencilFunc函数设置的ref值gl.INCR如果模板缓冲值小于最大值则将模板值加1gl.INCR_WRAP与GL_INCR一样但如果模板缓冲值超过了最大值则归零gl.DECR如果模板缓冲值大于最小值则将模板值减1gl.DECR_WRAP与GL_DECR一样但如果模板缓冲值小于0则将其设置为最大值gl.INVERT按位翻转当前的模板缓冲值
默认情况下glStencilOp是设置为(gl.KEEP, gl.KEEP, gl.KEEP)的所以不论测试的结果是什么模板缓冲都会保留它的值。默认的行为不会更新模板缓冲所以如果你想写入模板缓冲的话你需要至少对其中一个选项设置不同的值。
通常我们这样设置glStencilOp(gl..KEEP, gl.KEEP,gl.REPLACE)测试失败时保持原有值KEEP测试通过的参考值替换模板缓冲值REPLACE 在webgl中模板测试默认是处于禁用状态使用时需要手动开启我们采用gl.enable来开启
gl.enable(gl.STENCIL_TEST);注意和颜色和深度缓冲一样我们也需要在每帧绘制之前清除模板缓冲。
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
如果想自己在webgl上写模板测试需要在获取gl上下文对象时传入stencil的请求
const gl canvas.getContext(webgl,{stencil:true});
深度测试Depth Test
深度测试可以帮助实现3D渲染的物体遮挡效果
深度缓冲就像颜色缓冲(Color Buffer)储存所有的片段颜色一样在每个片段中储存了信息并且通常和颜色缓冲有着一样的宽度和高度。深度缓冲是由窗口系统自动创建的它会以16、24或32位float的形式储存它的深度值。在大部分的系统中深度缓冲的精度都是24位的。 当深度测试被启用的时候OpenGL会将一个片段的深度值与深度缓冲的内容进行对比。OpenGL会执行一个深度测试如果这个测试通过了的话深度缓冲将会更新为新的深度值。如果深度测试失败了片段将会被丢弃。
深度缓冲是在片段着色器运行之后以及模板测试运行之后在屏幕空间中运行的。屏幕空间坐标与通过OpenGL的glViewport所定义的视口密切相关并且可以直接使用GLSL内建变量gl_FragCoord从片段着色器中直接访问。gl_FragCoord的x和y分量代表了片段的屏幕空间坐标其中(0, 0)位于左下角。gl_FragCoord中也包含了一个z分量它包含了片段真正的深度值。z值就是需要与深度缓冲内容所对比的那个值。
深度测试默认是禁用的所以如果要启用深度测试的话我们需要用GL_DEPTH_TEST选项来启用它
glEnable(GL_DEPTH_TEST);当它启用的时候如果一个片段通过了深度测试的话OpenGL会在深度缓冲中储存该片段的z值如果没有通过深度缓冲则会丢弃该片段。如果你启用了深度缓冲你还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT来清除深度缓冲否则你会仍在使用上一次渲染迭代中的写入的深度值
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);如果在某些情况下你会需要对所有片段都执行深度测试并丢弃相应的片段但不希望更新深度缓冲。也就是你在使用一个只读的(Read-only)深度缓冲。OpenGL允许我们禁用深度缓冲的写入只需要设置它的深度掩码(Depth Mask)设置为GL_FALSE就可以了
glDepthMask(GL_FALSE);注意这只在深度测试被启用的时候才有效果。
深度测试函数
OpenGL允许我们修改深度测试中使用的比较运算符。这允许我们来控制OpenGL什么时候该通过或丢弃一个片段什么时候去更新深度缓冲。我们可以调用glDepthFunc函数来设置比较运算符或者说深度函数(Depth Function)
glDepthFunc(GL_LESS);这个函数接受下面表格中的比较运算符
函数描述GL_ALWAYS永远通过深度测试GL_NEVER永远不通过深度测试GL_LESS在片段深度值小于缓冲的深度值时通过测试GL_EQUAL在片段深度值等于缓冲区的深度值时通过测试GL_LEQUAL在片段深度值小于等于缓冲区的深度值时通过测试GL_GREATER在片段深度值大于缓冲区的深度值时通过测试GL_NOTEQUAL在片段深度值不等于缓冲区的深度值时通过测试GL_GEQUAL在片段深度值大于等于缓冲区的深度值时通过测试
默认情况下使用的深度函数是GL_LESS它将会丢弃深度值大于等于当前深度缓冲值的所有片段。 如果想自己在webgl上写深度测试需要在获取gl上下文对象时传入depth的请求
const gl canvas.getContext(webgl,{stencil:true,depth:true});
Creator中使用深度、模板测试
打开TS引擎源码文件cocos\gfx\webgl\webgl-swapchain.ts(低版本打开cocos\gfx\webgl\webgl-device.ts)
有个initStates函数引擎在这里初始化depth、stencil state function initStates (gl: WebGLRenderingContext) {gl.activeTexture(gl.TEXTURE0);gl.pixelStorei(gl.PACK_ALIGNMENT, 1);gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);gl.bindFramebuffer(gl.FRAMEBUFFER, null);// rasterizer stategl.enable(gl.SCISSOR_TEST);gl.enable(gl.CULL_FACE);gl.cullFace(gl.BACK);gl.frontFace(gl.CCW);gl.disable(gl.POLYGON_OFFSET_FILL);gl.polygonOffset(0.0, 0.0);// depth stencil stategl.enable(gl.DEPTH_TEST);gl.depthMask(true);gl.depthFunc(gl.LESS);gl.depthRange(0.0, 1.0);gl.stencilFuncSeparate(gl.FRONT, gl.ALWAYS, 1, 0xffff);gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP);gl.stencilMaskSeparate(gl.FRONT, 0xffff);gl.stencilFuncSeparate(gl.BACK, gl.ALWAYS, 1, 0xffff);gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.KEEP);gl.stencilMaskSeparate(gl.BACK, 0xffff);gl.disable(gl.STENCIL_TEST);// blend stategl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);gl.disable(gl.BLEND);gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO);gl.colorMask(true, true, true, true);gl.blendColor(0.0, 0.0, 0.0, 0.0);
}以上初始化的配置主要针对3D对象因为2D对象大多数包含透明像素因此2D管线不需要进行深度测试比如对于builtin-sprite.effect这类2d shader中对应深度测试部分都被默认关闭了 除了可以在effect文件中初始化深度模板测试 creator编辑器还可以在material文件中修改 所有深度、模板测试相关的配置都可以在Pipeline States下修改。
下面我们做个案例 如图结合相机位置和右下角相机拍摄出的画面来看由于长方体柱子的深度值比其他模型的深度值要小由于深度测试函数是GL_LESS深度值小的通过测试使得长方体背后的模型的片段被剔除了因此长方体遮挡住了其他物体包括地面。接下来我们对长方体的深度测试做一些配置修改再看看显示效果
1.关闭长方体模型材质中的的深度测试 关闭长方体的深度测试表现的效果 长方体关闭了深度测试后没有了深度值在它和其他模型重叠的地方会被有深度的模型片段填充。