Unity3D Shader 渲染状态详解

Unity3D Shader 渲染状态详解

前言

很多刚学 Unity Shader 的同学,一开始最关注的都是:

  • vert 怎么写
  • frag 怎么写
  • 纹理怎么采样

但真正开始做效果后,你很快就会发现:

有时候问题根本不在代码逻辑,而在渲染状态。

例如:

  • 为什么物体被挡住了却还显示在前面?
  • 为什么透明物体看起来怪怪的?
  • 为什么描边会盖住本体?
  • 为什么同样的颜色混合结果完全不同?

这些问题,很多都和 Render State(渲染状态) 有关。

什么是渲染状态

渲染状态可以理解为:

GPU 在画这个 Pass 时,需要遵守的一组规则。

比如:

  • 要不要剔除背面
  • 要不要写入深度
  • 深度如何比较
  • 颜色如何混合

这些并不是写在 vert() / frag() 里的计算代码,

而是写在 Pass 外层的控制指令。

例如:

c 复制代码
Pass
{
    Cull Back
    ZWrite On
    ZTest LEqual

    CGPROGRAM
    ...
    ENDCG
}

这些 CullZWriteZTest 就是渲染状态。

为什么渲染状态这么重要

因为同样的 Shader 代码,只要渲染状态一改,结果可能完全不同。

例如你明明返回的是同样的颜色:

c 复制代码
return fixed4(1, 0, 0, 0.5);

如果没有设置混合:

  • 它可能直接不透明地画上去

如果加上:

c 复制代码
Blend SrcAlpha OneMinusSrcAlpha

它才会按 Alpha 做半透明混合。

所以:

渲染状态决定"怎么画",Shader 代码决定"画什么"。

最常见的几个渲染状态

这一篇重点讲 5 个最常用的:

  • Cull
  • ZWrite
  • ZTest
  • Blend
  • ColorMask

最后再补充:

  • Queue
  • RenderType

Cull 面剔除

Cull 用来控制:

哪些面的三角形要被剔除,不参与渲染。

常见取值:

c 复制代码
Cull Back
Cull Front
Cull Off

1 Cull Back

剔除背面。

这是最常见、也是默认的设置。

意思是:

  • 面朝摄像机的一侧绘制
  • 背向摄像机的一侧不画

适合大多数实体模型。

2 Cull Front

剔除正面,只画背面。

这在普通模型里不常用,

但在这些效果中非常关键:

  • 描边
  • 外扩壳层
  • 某些双层特效

例如你之前写的法线外扩描边,就是典型场景。

3 Cull Off

正反两面都绘制。

适合:

  • 纸片树叶
  • 布料
  • 一些双面面片特效

但要注意:

双面渲染通常会增加填充开销。

ZWrite 深度写入

ZWrite 控制的是:

当前物体渲染后,要不要把深度写进深度缓冲。

常见写法:

c 复制代码
ZWrite On
ZWrite Off

ZWrite On

写入深度。

通常用于:

  • 不透明物体
  • 需要正确遮挡关系的物体

这也是默认行为。

ZWrite Off

不写入深度。

通常用于:

  • 透明物体
  • 粒子
  • 光效叠加

为什么透明物体常常要关掉 ZWrite

因为如果透明物体先把深度写进去,

后面的透明物体可能会被错误挡住。

ZTest 深度测试

ZTest 控制的是:

当前像素是否允许通过深度比较。

最常见写法:

c 复制代码
ZTest LEqual

意思是:

当前像素深度 小于等于 深度缓冲中的值时,才允许绘制。

也就是说:

  • 更靠近摄像机的像素能画出来
  • 被挡在后面的像素画不出来

常见取值

c 复制代码
ZTest LEqual
ZTest Greater
ZTest Always
ZTest LEqual

默认值,最常用。

ZTest Greater

只有当当前像素更"远"时才通过。

比较少见,但可以做一些特殊遮挡效果。

ZTest Always

永远通过深度测试。

适合:

  • 屏幕叠加特效
  • 永远显示在最上层的提示
  • 调试显示

但要谨慎使用,因为它会无视正常遮挡关系。

Blend 颜色混合

Blend 用来控制:

当前像素颜色,如何与屏幕上已有颜色混合。

最常见的透明混合:

c 复制代码
Blend SrcAlpha OneMinusSrcAlpha

它的效果就是我们常说的:

标准 Alpha 混合。

几种常见混合模式

1 普通透明
c 复制代码
Blend SrcAlpha OneMinusSrcAlpha

适合:

  • 玻璃
  • UI 半透明
  • 常规透明材质
2 纯加法混合
c 复制代码
Blend One One

特点:

  • 越叠越亮
  • 没有"遮住后面"的感觉

适合:

  • 火焰
  • 魔法光效
  • 能量特效
3 Alpha 加法
c 复制代码
Blend SrcAlpha One

比纯加法更容易控制亮度。

适合很多特效场景。

ColorMask 颜色通道写入控制

ColorMask 用来控制:

当前 Pass 允许写入哪些颜色通道。

例如:

c 复制代码
ColorMask RGB

表示只写 RGB,不写 Alpha。

再比如:

c 复制代码
ColorMask 0

表示不写任何颜色。

这有什么用?

一个典型用途是:

  • 只写深度
  • 不输出可见颜色

常见于:

  • 特殊遮挡处理
  • 预写深度
  • 某些后处理辅助 Pass

Queue 渲染队列

Queue 决定的是:

这个物体大致在什么时候渲染。

常见写法:

c 复制代码
Tags { "Queue"="Geometry" }

常见队列:

Queue 数值 用途
Background 1000 背景
Geometry 2000 不透明物体
AlphaTest 2450 裁剪物体
Transparent 3000 半透明物体
Overlay 4000 叠加层

一般来说:

  • 不透明 → Geometry
  • 裁剪 → AlphaTest
  • 半透明 → Transparent

RenderType 材质分类标签

例如:

c 复制代码
Tags { "RenderType"="Opaque" }

它主要是告诉 Unity:

这个 Shader 属于哪一类渲染材质。

常见值:

  • Opaque
  • Transparent
  • TransparentCutout

它常用于:

  • 替换 Shader
  • 渲染分类
  • 某些内置流程判断

一个简单的渲染状态示例

下面这个 Pass 同时设置了几个常见状态:

c 复制代码
Pass
{
    Cull Off
    ZWrite Off
    ZTest LEqual
    Blend SrcAlpha OneMinusSrcAlpha

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    ENDCG
}

这段配置表示:

  • 双面都画
  • 不写深度
  • 仍然参与正常深度测试
  • 按 Alpha 做半透明混合

这就是很多透明面片特效的典型配置。

常见组合方案

1 不透明物体

c 复制代码
Cull Back
ZWrite On
ZTest LEqual
Blend Off
Tags { "Queue"="Geometry" "RenderType"="Opaque" }

2 透明裁剪物体

c 复制代码
Cull Back
ZWrite On
ZTest LEqual
Blend Off
Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" }

3 半透明物体

c 复制代码
Cull Back
ZWrite Off
ZTest LEqual
Blend SrcAlpha OneMinusSrcAlpha
Tags { "Queue"="Transparent" "RenderType"="Transparent" }

4 加法特效

c 复制代码
Cull Off
ZWrite Off
ZTest LEqual
Blend One One
Tags { "Queue"="Transparent" "RenderType"="Transparent" }

常见坑

1 透明物体忘记关 ZWrite

结果往往是:

  • 前后透明物体互相遮挡异常
  • 看起来"缺一块"

2 写了半透明颜色却没开 Blend

例如:

c 复制代码
return fixed4(1,1,1,0.5);

如果没写 Blend,它并不会自动帮你变成半透明。

3 双面渲染滥用 Cull Off

有些模型本来应该有厚度,却直接开双面,

容易导致:

  • 性能浪费
  • 视觉不合理

4 ZTest Always 用过头

虽然看起来方便,

但它经常会让特效无视遮挡,显得很"飘"。

总结

渲染状态决定这个 Pass 以什么规则被画出来。

最常见的几个状态作用如下:

状态 作用
Cull 控制剔除哪一面
ZWrite 是否写入深度
ZTest 如何进行深度比较
Blend 如何与背景颜色混合
ColorMask 写入哪些颜色通道
Queue 大致渲染顺序
RenderType 材质分类标签
相关推荐
魔士于安9 小时前
unity 音乐会场景 unity2022
游戏·unity·游戏引擎·贴图·模型
Mediary11 小时前
Unity is running with Administrator privileges, which isnot supported...
unity
游乐码13 小时前
Unity基础(十四)场景异步加载
unity·游戏引擎
mxwin14 小时前
Unity Shader URP:法线在空间变换上的特殊性
unity·游戏引擎·shader
nnsix14 小时前
Unity 动态批处理、静态批处理、GPU Instaning、SRP Batcher 笔记
笔记·unity·单一职责原则
charlee4415 小时前
Unity在安卓端如何调试输出信息
android·unity·adb·游戏引擎·真机调试
_Athie16 小时前
【开发工具】自动创建项目文件夹结构
unity·编辑器
auccy1 天前
Unity Sprite 添加法线贴图
unity·贴图·normal
mxwin2 天前
次世代角色 PBR 贴图制作 + Unity URP 接入 极简流程图
unity·流程图·贴图·shader