【渲染流水线】[逐片元阶段]-[模版测试]以UnityURP为例

  • 用于精确控制像素丢弃的逐片元操作,通过模板缓冲区(8位整数/像素)实现复杂遮罩效果。
  • 支持8种比较函数和6种缓冲操作‌
  • 当前模版操作,只能在Shader文件中书写操作,ShaderGraph中无法直接使用模版指令。

【从UnityURP开始探索游戏渲染】专栏-直达

核心配置预览

  • 核心配置所有配置列出(非全必要):
Stencil {     Ref 2 // 必要模版数值     ReadMask 255     WriteMask 255     Comp Greater // 必要比较操作符     Pass Replace // 必要通过后的操作     Fail Keep     ZFail DecrSat } 

1. 核心配置命令

  • Ref:设置参考值(0-255整数),用于与模板缓冲区比较
  • ReadMask:读取掩码(0-255),按位与操作后比较(默认255)
  • WriteMask:写入掩码(0-255),控制可修改的缓冲区位(默认255)

2. 比较函数命令

Comp支持以下枚举值:

  • Always/Never:始终通过/拒绝
  • Less/Greater:小于/大于时通过
  • Equal/NotEqual:等于/不等于时通过
  • LessEqual/GreaterEqual:小于等于/大于等于时通过

3. 操作命令

Pass/Fail/ZFail支持的操作:

  • Keep:保持原值
  • Zero:置零
  • Replace:用Ref值替换
  • IncrSat/DecrSat:饱和增减(0/255边界)
  • IncrWrap/DecrWrap:循环增减
  • Invert:按位取反

模板测试具体过程‌

  1. 缓冲区初始化

    • 清除模板缓冲:GL.Clear(ClearBufferMask.StencilBufferBit)
    • 设置初始值:GL.StencilMask(0xFF)(默认全255)
  2. 测试阶段-逐片元

    plaintext if (片元模板值 [比较函数] 参考值) {     执行通过操作(如保留像素) } else {     执行失败操作(如丢弃像素) } 
  3. 缓冲更新

    • 根据测试结果修改模板缓冲值(可选)

‌比较函数(StencilFunc)‌

函数 含义 OpenGL常量
Never 永远不通过 GL_NEVER
Always 永远通过 GL_ALWAYS
Less 模板值 < 参考值 GL_LESS
LEqual 模板值 ≤ 参考值 GL_LEQUAL
Greater 模板值 > 参考值 GL_GREATER
GEqual 模板值 ≥ 参考值 GL_GEQUAL
Equal 模板值 == 参考值 GL_EQUAL
NotEqual 模板值 != 参考值 GL_NOTEQUAL

‌缓冲操作(StencilOp)‌

操作组合 含义
Keep 保持当前模板值不变(默认)
Zero 将模板值设为0
Replace 用参考值替换模板值
Incr/IncrWrap 模板值+1(超过255时,前者截断后者循环)
Decr/DecrWrap 模板值-1(低于0时,前者截断后者循环)
Invert 按位取反(~操作)

示例1

  • 遮罩区域shader
Shader "Custom/StencilMask" {     SubShader {         Tags { "Queue"="Geometry-1" } // 优先渲染         ColorMask 0 // 不写入颜色         ZWrite Off          Stencil {             Ref 1             Comp Always             Pass Replace // 将模板值设为1         }         Pass {} // 空Pass仅用于写入模板     } } 
  • 物体模版测试shader,该Shader仅在模板值为1的区域内渲染物体。
Shader "Custom/StencilObject" {     SubShader {         Stencil {             Ref 1             Comp Equal // 仅渲染模板值=1的区域             Pass Keep         }         Pass {             // 正常着色代码...         }     } } 

示例2 外轮廓描边

Shader "Custom/StencilOutline" {     Properties {         _MainTex ("Base Texture", 2D) = "white" {}         _OutlineColor ("Outline Color", Color) = (1,0,0,1)         _OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.05     }      SubShader {         Tags {              "RenderPipeline"="UniversalRenderPipeline"             "RenderType"="Opaque"         }          // Pass 1: 正常渲染角色并写入模板         Pass {             Name "Character"             Tags             {                 "LightMode" = "UniversalForward"             }              Stencil {                 Ref 1                 Comp Always                 Pass Replace                 ZFail Keep             }              HLSLPROGRAM             #pragma vertex vert             #pragma fragment frag             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"              struct Attributes {                 float4 positionOS : POSITION;                 float2 uv : TEXCOORD0;             };              struct Varyings {                 float4 positionCS : SV_POSITION;                 float2 uv : TEXCOORD0;             };              TEXTURE2D(_MainTex);             SAMPLER(sampler_MainTex);              Varyings vert(Attributes IN) {                 Varyings OUT;                 OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);                 OUT.uv = IN.uv;                 return OUT;             }              half4 frag(Varyings IN) : SV_Target {                 return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);             }             ENDHLSL         }          // Pass 2: 渲染轮廓         Pass {             Name "Outline"             Cull Front             Stencil {                 Ref 1                 Comp NotEqual                 Pass Keep             }              HLSLPROGRAM             #pragma vertex vert             #pragma fragment frag             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"              struct Attributes {                 float4 positionOS : POSITION;                 float3 normalOS : NORMAL;             };              struct Varyings {                 float4 positionCS : SV_POSITION;             };              CBUFFER_START(UnityPerMaterial)                 float4 _OutlineColor;                 float _OutlineWidth;             CBUFFER_END              Varyings vert(Attributes IN) {                 Varyings OUT;                 float3 posWS = TransformObjectToWorld(IN.positionOS.xyz * (1 + _OutlineWidth));                 float3 normalWS = TransformObjectToWorldNormal(IN.normalOS);                 // posWS += normalWS * _OutlineWidth; // 沿法线方向扩展                 OUT.positionCS = TransformWorldToHClip(posWS);                 return OUT;             }              half4 frag(Varyings IN) : SV_Target {                 return _OutlineColor;             }             ENDHLSL         }     } } 

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

发表评论

评论已关闭。

相关文章