在渲染模型时控制它的透明通道(Alpha Channel)。在Unity中,有两种方法来实现透明效果:

透明度测试(Alpha Test)
透明度混合(Alpha Blending)

对于不透明(opaque)物体,不考虑它们的渲染顺序也能得到正确的排序结果,这是由于强大的深度缓冲的存在(depth buffer,也被称为z-buffer)。如果要渲染的片元,深度值和深度缓冲的比较,距离摄像机更远,就不应该渲染在屏幕上(有物体遮挡住了)。

透明度测试: “霸道极端”,只要一个片元的透明度不满足条件(通常是小于某个阈值),就舍弃。不需要关闭深度写入(ZWrite),要么完全透明看不见,要么完全不透明像不透明物体。
透明度混合: 可以得到真正的半透明效果,使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色值。需要关闭深度写入,不用关闭深度测试,需要非常小心物体的渲染顺序。(没有写入深度,如何进行深度测试,判断这个透明物体是在不透明物体前面还是后面?)

UnityShader的渲染顺序

Unity 物体离很远拉不进_游戏引擎

使用透明度测试的代码类似这样:

SubShader{
    Tags{"Queue"="AlphaTest"}
    Pass{
        ...
    }
}

使用透明度混合的代码类似这样:

SubShader{
    Tags{"Queue"="Transparent"}
    Pass{
        ZWrite Off
        ...
    }
}

透明度测试

在片元着色器中使用clip函数进行透明度测试,原理:

void clip(float4 x){
    if(any(x<0)) discard;
}

Shader中使用

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

透明度混合

Unity 物体离很远拉不进_Front_02

Blend SrcFactor DstFactor 来进行混合,使用Blend命令的时候,Unity就打开了混合模式。经过混合后的新颜色是:

Unity 物体离很远拉不进_Unity 物体离很远拉不进_03

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

为了解决由于关闭深度写入而造成的错误排序的情况,可以使用两个Pass来渲染模型:

第一个Pass开启深度写入,但不输出颜色,仅把该模型的深度值写入深度缓冲
第二个Pass进行正常的透明度混合,因为有了深度信息,就可以按照像素级别的深度排序结果进行渲染
损失了一定的性能,实现模型与后面背景的混合,但模型内部没有真正的半透明效果。
开启深度写入的Pass,只需要在原来使用的Pass前面增加一个新的Pass。

Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}

// Extra pass that renders to depth buffer only
Pass{
    ZWrite On
    ColorMask 0 // 该Pass不写入任何颜色通道,即不输出任何颜色
}

Pass{
    // ...透明度混合
}

ShaderLab的混合命令

混合还有很多其他用处,不仅仅用于透明度混合。

混合的实现:源颜色(source color)和目标颜色(destination color)。

源颜色S:由片元着色器产生的颜色
目标颜色D:从颜色缓冲中读取到的颜色值
输出颜色O:混合后的颜色,重新写入颜色缓冲

混合等式和参数

混合等式(blend equation):已知S和D,得到O的等式。

混合是一个逐片元的操作,不可编程,但高度可配置。可以设置混合时使用的运算操作、混合因子,需要两个等式分别用于RGB和A。

Unity 物体离很远拉不进_unity_04

第一个命令使用同样的因子来混合RGB通道和A通道。使用这些因子进行加法混合时使用的混合等式:

Orgb=SrcFactorSrgb+DstFactorDrgb
Oa=SrcFactorSa+DstFactorDa

ShaderLab支持的混合因子如下:

Unity 物体离很远拉不进_Blend_05

使用不同参数混合A通道的命令如下:

Blend SrcAlpha OneMinusSrcAlpha, One Zero

混合操作

以上的混合等式默认是使用加法,使用ShaderLab的混合操作命令BlendOp BlendOperation,可以进行更改:

Unity 物体离很远拉不进_unity_06

常见的混合类型

// 正常(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

上混合模式的公式,S源颜色、D颜色缓冲中的颜色(认为是后面物体的颜色吧)、O输出:

正常:O=SxSa+Dx(1-Sa)
 柔和相加:O=Sx(1-Drgba)+D
 正片叠底:O=SxDrgba=(SrxDr,SgxDg,SbxDb,SaxDa)
 两倍相乘:O=SxDrgba+DxSrgba=(SrxDr+DrxSr,SgxDg+DgxSg,SbxDb+DbxSb,SaxDa+DaxSa)=2(SrxDr,SgxDg,SbxDb,SaxDa)
 变暗:O=Min(S,D)
 变亮:O=Max(S,D)
 滤色:O=Sx(1-Drgba)+D,同柔和相加?
 线性减淡:O=S+D

Unity 物体离很远拉不进_Unity 物体离很远拉不进_07

双面渲染的透明效果

之前无论是透明度测试还是透明度混合,都无法观测到正方体内部和背面的形状。这是因为默认下渲染引擎剔除了物体背面(相对于相机的方向),只渲染了正面。可以使用Cull指令来控制需要剔除哪个面,语法如下:

Cull Back|Front|Off

Back 背对摄像机的渲染图元不会被渲染(默认情况下的剔除状态)
Front 朝向摄像机的渲染图元不会被渲染
Off 关闭剔除功能 所有渲染图元都会被渲染

透明度测试的双面渲染

在Pass的渲染设置中使用Cull指令来关闭剔除:

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

    // Turn off culling
    Cull Off

    CGPROGRAM
    ...

透明度混合的双面渲染

因为透明度混合需要关闭深度写入,所以不能直接关闭剔除功能,这样无法保证同一物体的正面和背面图元的渲染顺序。

为此我们把双面渲染的工作分成两个Pass——第一个Pass只渲染背面,第二个Pass只渲染正面。

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

    // First pass renders only back faces
    Cull Front
    ...
}

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

    // Second pass renders only front faces
    Cull Back
    ...
}