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

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

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

核心配置预览

  • 核心配置所有配置列出(非全必要):
c 复制代码
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. 测试阶段-逐片元

    c 复制代码
    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
c 复制代码
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的区域内渲染物体。
c 复制代码
Shader "Custom/StencilObject" {
    SubShader {
        Stencil {
            Ref 1
            Comp Equal // 仅渲染模板值=1的区域
            Pass Keep
        }
        Pass {
            // 正常着色代码...
        }
    }
}

示例2 外轮廓描边

c 复制代码
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开始探索游戏渲染】专栏-直达

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

相关推荐
小蜗 strong6 小时前
unity中实现机械臂自主运动
unity·游戏引擎
★YUI★17 小时前
学习游戏制作记录(制作系统与物品掉落系统)8.16
学习·游戏·ui·unity·c#
SmalBox1 天前
【渲染流水线】[逐片元阶段]-[透明度测试]以UnityURP为例
unity·渲染
三只坚果2 天前
blender制作动画导入unity两种方式
unity·游戏引擎·blender
benben0442 天前
《Unity Shader入门精要》学习笔记二
unity·unity shader
YF云飞2 天前
Unity音频管理:打造沉浸式游戏音效
游戏·unity·游戏引擎·游戏程序·个人开发
CG_MAGIC2 天前
主流 3D 模型格式(FBX/OBJ/DAE/GLTF)材质支持与转换操作指南
3d·渲染·动画·材质·贴图·3d 模型格式·材质支持与转换操作指南
SmalBox2 天前
【渲染流水线】[逐片元阶段]-[裁剪测试]以UnityURP为例
unity·渲染
与火星的孩子对话2 天前
Unity高级开发:反射原理深入解析与实践指南 C#
java·unity·c#·游戏引擎·lucene·反射