TA不一样(十一)
前言:Stencil Buffer(模板缓存)做为一个核心缓冲区类型简单介绍一下实现和功能。然后再补充一下剔除和深度的功能。Stencil Buffer1.什么是stencil buffer如果将屏幕上所有像素想象成一串连续的0组成的矩形那么stencil bufer的作用就是将某些0变为123 ..255在每个pass中可以决定只渲染某个特定stencil值的像素并抛弃对其他非该值像素的操作stencil bufer与depth buffer一样都是缓冲区存在于显存内的某一片区域中目前的显卡架构中stenci buffer与depth bufer是在一起的比如在depth/stencil缓冲区某个32位的区域中有24位记录着像素A的depth数据紧接着8位记录着像素A的stencil数据stencil测试在管线中写入与读取的位置在透明测试(AlphaTest)和深度测试(DepthTest)之间2.常用语法格式Stencil{Ref reference Value // 参考值 默认値为 0Comp comparisonFunction // 定义参考值与缓冲值比较的方法 默认值为AlwaysPass stencilOperation // 定义当通过模板测试时根据参考值对缓冲值的处// 理方法 默认值为keepFail stencillperation // 定义当没有通过模板测试时根据参考值对缓冲值// 的处理方法 默认为keepZFail stencilOperation // 定义当通过模板测试却没有通过深度测试时根据// 参考对缓冲值的处理方法 默认为keep}3.代码实现使用stencil buffer实现物体描边。之前实现描边通过法线拓展并进行前剔除实现物体边缘膨胀描边而通过stencil buffer实现的是通过渲染顺序跳过法线扩展和模型的重合部分实现描边。由于描边通过Ref值Comp Equal(相等判断)多个部分重合模型的描边会丢失重合部分的描边。Shader MyCustom/StencilOutline { Properties { // 描边宽度 _Edge (_Edge, Float) 0.1 // 物体颜色 _Color (_Color, Color) (1, 1, 1, 1) } SubShader { Tags { RenderTypeOpaque } Stencil { Ref 0 // 0-255 Comp Equal // 已经定义的ref 和 stencil buff值做对比 Pass IncrSat // ref 值自增1 } // 第一部分Pass渲染物体施加lambert效果由于第一次经过Stencil Buffer默认值为0对比通过执行Pass逻辑屏幕这部分值自增一为1。 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldNormal : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.worldNormal UnityObjectToWorldNormal(v.normal); return o; } fixed4 frag (v2f i) : SV_Target { float3 worldNormal normalize(i.worldNormal); float3 worldLight normalize(_WorldSpaceLightPos0.xyz); float diffuse saturate(dot(worldNormal, worldLight) * 0.5 0.5); // ambient float3 ambient UNITY_LIGHTMODEL_AMBIENT.xyz; return float4(diffuse ambient, 1); } ENDCG } // 第二部分Pass法线扩展实现描边由于第二次经过Stencil Buffer覆盖区域默认值为0对比之前模型渲染的屏幕部分的值为1不通过跳过渲染。只有法线扩出来的部分默认值为0进行渲染并执行Pass逻辑自增1。 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; }; float _Edge; float4 _Color; v2f vert (appdata v) { v2f o; o.vertex v.vertex normalize(float4(v.normal, 0)) * _Edge; o.vertex UnityObjectToClipPos(o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _Color; } ENDCG } } }通过stencil buffer实现X射线透视效果。渲染顺序x射线物体 -- 遮挡物体 -- 被遮挡物体stencil buffer值x射线物体99永远通过赋值为99遮挡物体0相等通过自增1被遮挡物体99相等通过自增1Shader MyCustom/StencilXrayMask { Properties { _Color (_Color, Color) (1, 1, 1, 1) } SubShader { //Tags { RenderTypeOpaque } ZWrite Off ColorMask 0 Stencil { Ref 99 // 0-255例子中是99 Comp always // 比较的结果是永远通过 Pass replace // pass的结果是替换就是拿99替换 stencil buff的值由于优先渲染X射线遮罩部分的值为99遮挡物值为0被跳过渲染被遮挡物值为99进行渲染。 } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldNormal : TEXCOORD0; }; float4 _Color; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _Color; } ENDCG } } }Culling和Z剔除和深度1.Cull剔除剔除语法:Cull Back | Front | OffBack不渲染背离观察者的多边形Front不渲染面向观察者的多边形用于从里到外翻转对象Off禁用剔除-绘制所有面VFace表示一个渲染表面是否面朝相机。使用这个语义需要 Shader Model 3.0也就是#pragma target 3.0在使用Cull Off绘制所以面时可以在片元着色器中使用VFace参数表示正反面fixed4 frag (v2f i, float facing : VFACE) : SV_Target{// facing0为正面0为反面float4 col lerp(_Color0, _Color1, facing 0);float3 normal i.normal * 0.5 0.5;col.rgb lerp(normal, float3(0, 0, 1), facing 0);return col;}2.深度深度缓存depth buffer存储着准备要绘制在屏幕上的像素点的深度值。如果启用了深度缓冲区在绘制每个像素之前OpenGL会把该像素的深度值和深度缓存的深度值进行比较。深度测试 depth test将要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较如果比深度缓存中的值小那么用新像素的颜色值更新深度缓存中对应像素的颜色值。ZTest Less | Greater | LEqual | GEqual | Equal | NotEqual | Always应如何执行深度测试默认值为 LEqualZWrite On l Off控制是否将此对象的像素写入深度缓冲区默认值为 On如果要绘制实体对象将其保留为 On。如果要绘制半透明效果请切换到ZWrite OffOffset Factor, Units允许使用两个参数指定深度偏移