渲染顺序

名称队列索引号描 述
Background1000这个渲染队列会在任何其他队列之前被渲染,我们通常使用该队列来渲染那些需要绘制在背景上的物体
Geometry2000默认的渲染队列,大多数物体都使用这个队列。不透明物体使用这个队列
AlphaTest2450需要透明度测试的物体使用这个队列。在Unity 5中它从Geometry队列中被单独分出来,这是因为在所有不透明物体渲染之后再渲染它们会更加高效
Transparent3000这个队列中的物体会在所有Geometry和AlphaTest物体渲染后,再按从后往前的顺序进行渲染。任何使用了透明度混合(例如关闭了深度写入的Shader)的物体都应该使用该队列, srpite模式是该队列
Overlay4000该队列用于实现一些叠加效果。任何需要在最后渲染的物体都应该使用该队列

透明度测试

透明度测试: 只要一个片元的透明度不满足条件(通常是小于某个阈值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它

  • 函数 :void clip(float4 x); void clip(float3 x); void clip(float2 x); void clip(float1 x); void clip(float x);
  • 参数 :裁剪时使用的标量或矢量条件。
  • 描述 :如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。
代码
Shader "Unity Shaders Book/Chapter 8/Alpha Test" {
Properties {
    _Color ("Main Tint", Color) = (1,1,1,1)
    _MainTex ("Main Tex", 2D) = "white" {}
    _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
}
SubShader {
    Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}

    Pass {
        Tags { "LightMode"="ForwardBase" }

CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;

struct a2v {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
};

struct v2f {
    float4 pos : SV_POSITION;
    float3 worldNormal : TEXCOORD0;
    float3 worldPos : TEXCOORD1;
    float2 uv : TEXCOORD2;
};

v2f vert(a2v v) {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

    o.worldNormal = UnityObjectToWorldNormal(v.normal);

    o.worldPos = mul(_Object2World, v.vertex).xyz;

    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

    return o;
}
fixed4 frag(v2f i) : SV_Target {
    fixed3 worldNormal = normalize(i.worldNormal);
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

    fixed4 texColor = tex2D(_MainTex, i.uv);

    // Alpha test
    clip (texColor.a - _Cutoff);
    // Equal to 
//  if ((texColor.a - _Cutoff) < 0.0) {
//      discard;
//  }

    fixed3 albedo = texColor.rgb * _Color.rgb;

    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

    fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

    return fixed4(ambient + diffuse, 1.0);
}
ENDCG
    }
}
Fallback "Transparent/Cutout/VertexLit"
}

透明度混合

  • 这种方法可以得到真正的半透明效果。
  • 它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。
  • 透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。

ShaderLab的Blend命令

语 义描 述
Blend Off关闭混合
Blend SrcFactor DstFactor开启混合,并设置混合因子。源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中
Blend SrcFactor DstFactor, SrcFactorA DstFactorA和上面几乎一样,只是使用不同的因子来混合透明通道
BlendOp BlendOperation并非是把源颜色和目标颜色简单相加后混合,而是使用BlendOperation对它们进行其他操作

与透明度测试不同的是,我们还需要在Pass中为透明度混合进行合适的混合状态设置

Pass {
    Tags { "LightMode"="ForwardBase" }

    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha
代码
Shader "Unity Shaders Book/Chapter 8/Alpha Test" {
Properties {
    _Color ("Main Tint", Color) = (1,1,1,1)
    _MainTex ("Main Tex", 2D) = "white" {}
    _AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader {
    Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}

    Pass {
         Tags { "LightMode"="ForwardBase" }

    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha


CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;

struct a2v {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
};

struct v2f {
    float4 pos : SV_POSITION;
    float3 worldNormal : TEXCOORD0;
    float3 worldPos : TEXCOORD1;
    float2 uv : TEXCOORD2;
};

v2f vert(a2v v) {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

    o.worldNormal = UnityObjectToWorldNormal(v.normal);

    o.worldPos = mul(_Object2World, v.vertex).xyz;

    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

    return o;
}
fixed4 frag(v2f i) : SV_Target {
    fixed3 worldNormal = normalize(i.worldNormal);
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

    fixed4 texColor = tex2D(_MainTex, i.uv);

    fixed3 albedo = texColor.rgb * _Color.rgb;

    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

    fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

    return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
    }
}
Fallback "Transparent/VertexLit"
}

只有使用Blend命令打开混合后,我们在这里设置透明通道才有意义

开启深度写入的半透明效果

  • 第一个Pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;
  • 第二个Pass进行正常的透明度混合,由于上一个Pass已经得到了逐像素的正确的深度信息,该Pass就可以按照像素级别的深度排序结果进行透明渲染 使用这种方法,我们仍然可以实现模型与它后面的背景混合的效果,但模型内部之间不会有任何真正的半透明效果。

ShaderLab的混合命令

混合等式和参数

ShaderLab中设置混合因子的命令

命 令描 述
Blend SrcFactor DstFactor开启混合,并设置混合因子。源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中
Blend SrcFactor DstFactor, SrcFactorA DstFactorA和上面几乎一样,只是使用不同的因子来混合透明通道

ShaderLab中的混合因子

参 数描 述
One因子为1
Zero因子为0
SrcColor因子为源颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于混合A的混合等式时,使用SrcColor的A分量作为混合因子
SrcAlpha因子为源颜色的透明度值(A通道)
DstColor因子为目标颜色值。当用于混合RGB通道的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用DstColor的A分量作为混合因子。
DstAlpha因子为目标颜色的透明度值(A通道)
OneMinusSrcColor因子为(1-源颜色)。当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子
OneMinusSrcAlpha因子为(1-源颜色的透明度值)
OneMinusDstColor因子为(1-目标颜色)。当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子
OneMinusDstAlpha因子为(1-目标颜色的透明度值)

如果我们想要在混合后,输出颜色的透明度值就是源颜色的透明度,可以使用下面的命令

Blend SrcAlpha OneMinusSrcAlpha, One Zero

混合操作

操 作描 述
Add将混合后的源颜色和目标颜色相加。默认的混合操作。使用的混合等式是:{{O}{rgb}}=SrcFactor\times {{S}{rgb}}+DstFactor\times {{D}{rgb}} {{O}{a}}=SrcFactorA\times {{S}{a}}+DstFactorA\times {{D}{a}}
Sub用混合后的源颜色减去混合后的目标颜色。使用的混合等式是:{{O}{rgb}}=SrcFactor\times {{S}{rgb}}-DstFactor\times {{D}{rgb}} {{O}{a}}=SrcFactorA\times {{S}{a}}-DstFactorA\times {{D}{a}}
RevSub用混合后的目标颜色减去混合后的源颜色。使用的混合等式是:{{O}{rgb}}=DstFactor\times {{D}{rgb}}-SrcFactor\times {{S}{rgb}} {{O}{a}}=DstFactorA\times {{D}{a}}-SrcFactorA\times {{S}{a}}
Min使用源颜色和目标颜色中较小的值,是逐分量比较的。使用的混合等式是:{{O}{rgba}}=(\min ({{S}{r}},{{D}{r}}),\min ({{S}{g}},{{D}{g}}),\min ({{S}{b}},D{}{b}),\min ({{S}{a}},{{D}_{a}}))
Max使用源颜色和目标颜色中较大的值,是逐分量比较的。使用的混合等式是:{{O}{rgba}}=(\max ({{S}{r}},{{D}{r}}),\max ({{S}{g}},{{D}{g}}),\max ({{S}{b}},D{}{b}),\max ({{S}{a}},{{D}_{a}}))
其他逻辑操作仅在DirectX 11.1中支持

常见的混合类型

// 正常(Normal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha

// 柔和相加(Soft Additive)
Blend OneMinusDstColor One

// 正片叠底(Multiply),即相乘
Blend DstColor Zero

// 两倍相乘(2x Multiply)
Blend DstColor SrcColor

// 变暗(Darken)
BlendOp Min
Blend One One

// 变亮(Lighten)
BlendOp Max
Blend One One

// 滤色(Screen)
Blend OneMinusDstColor One
// 等同于
Blend One OneMinusSrcColor

// 线性减淡(Linear Dodge)
Blend One One

双面渲染的透明效果

可以使用Cull 指令来控制需要剔除哪个面的渲染图元。在Unity中,Cull指令的语法如下

Cull Back | Front | Off

透明度测试的双面渲染

和透明度测试的代码一样,只是多了一行代码, 如下

Pass {
    Tags { "LightMode"="ForwardBase" }

    // Turn off culling
    Cull Off

我们可以透过正方体的镂空区域看到内部的渲染结果。

透明度混合的双面渲染

透明度混合需要关闭深度写入。复制原Pass的代码,得到另一个Pass。

  • 第一个Pass只渲染背面 Cull Front
  • 第二个Pass只渲染正面 Cull Back

扩展阅读

ZTest (copy)

比较新旧z值的大小,就是Ztest;之后更新摄像机每一个像素的z值,就是Zwrite。 Ztest影响的是当前物体的显示;Zwrite影响的是之后渲染物体的显示。

Zwrite的概念相对简单,无非就是根据条件,对一个变量进行反复地赋值。比较有意思的Ztest。 在三个盒子的例子里,我一直都在强调“默认”两个字。那么默认是什么呢,就是Zwrite On + Ztest On。Zwrite就两种情况(On或者Off)。

而对于Ztest来说,条件就要丰富得多得多。Ztest的条件总共有如下几种: Less (当物体的这个像素的Z值小于当前摄像机在这个像素上的Z值,则通过Ztest)

LEqual(条件变为小于等于)

Greater(条件变为大于)

GEqual(条件变为大于等于)

Equal(条件变为相等)

NotEqual(条件变为不相等)

Always(Ztest永远通过)

Never(Ztest永远不通过)

Off(等同于 ZTest Always)

On(等同于ZTest LEqual)

Stencil (copy)

我们假定Stencil也有一个值叫Ref值。那么Stencil的用法也实在是看着眼熟:获取Ref值->测试(比较)Ref值->写入新的Ref值(如果通过测试)。

说到底这俩玩意儿的区别,就是在第一步,获取当前物体在这个像素上的这个变量。

Z值是根据像素到摄像机的距离算出来的,不会因为你的个人意愿而改变;S值是你可以随便填的(是的随便填,想写几就写几,范围0-255)。

这样一来Stencil可以帮助你突破Ztest所带来的限制,用更灵(jian)活(dan)便(cu)捷(bao)的方式来控制渲染效果。

Ref就是写入这个像素的Ref值,正如我之前提到的想写几就写几完全看心情(所以我一直都认为叫Stencil Buffer模板缓冲实在是有点唬人的感觉。改成“看哪个数字顺眼就用哪个数字比大小”更贴切一些)。

Com是进行Test的条件,当你看到一大堆Less\LEqual\Greater\GEqual\Equal\NotEqual\Always\Never这样的字眼儿,是不是感到非常的眼熟?这一步比较的过程和Ztest完全一样。

Pass和Zwrite简直就是一个妈生出的俩个孩儿。区别就是这个小哥比他兄弟花样儿多点。Zwrite无非就是写入或者不写入(On or Off)。Pass甚至还可以控制如何写入(虽然大多数情况下可能用不到)。

引用

ztest: https://zhuanlan.zhihu.com/p/28557283

--完--