本系列主要參考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同一时候会加上一点个人理解或拓展。

这里是本书全部的插图。这里是本书所需的代码和资源(当然你也能够从官网下载)。

========================================== 切割线 ==========================================



写在前面



从这篇開始是一个全新的章节:透明效果(Transparency)。之前在制作LOGO闪光效果的时候就一直调不出来背景透明。就是那个时候决定要学一下Shader的基础知识,不求成为多么厉害的大神。仅仅望对渲染的内部原理有些许了解~


開始正文。


在我们学习怎样编写透明的Surface Shader的開始,我们须要理解应该包括哪些代码使得我们能够开启透明效果。

Unity再一次大方地为我们提供了一些内置參数,我们能够通过包括这些參数来高速得到透明效果。


这是通过在Shader的#pragma声明中加入alpha參数来实现的。这句话告诉Unity我们想要在Shader中使用透明度。但当我们在创建透明Shaders时,须要细致考虑一些事情。那就是代码中元素的绘制顺序。这篇文章将会讲述一些基本问题,来得到一个非常easy的透明物体。

在以下的章节中,将会解说其它有关透明度的问题。



准备工作



和曾经一样,我们须要创建一个新的场景。

  1. 创建一个新的场景,加入一个平行光以及一个球体(Sphere)。

  2. 创建一个新的Shader和新的Material。能够命名为SimpleAlpha。把Shader赋给Material后,再把该Material赋给第一步中的球体。
  3. 最后,我们须要一张贴图作为控制隐私,来控制哪些部分是透明的。哪些部分是不透明的。
下图是我们用到的贴图。这张贴图仅包括单纯的RGB和白色(自带资源中没有。能够自己画,非常easy)。我们使用它的RGB通道作为一个取值为0或1的透明度值。


实现


这篇Shader非常easy。


  1. Properties块中加入一个新的property,这使得我们能够全局控制透明度。
    	Properties {
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_TransVal ("Transparency Value", Range(0,1)) = 0.5
    	}
  2. 改变渲染队列:
    Tags { "Queue"="Transparent" }

    解释:这一步非常重要,原书中没有加入这一句实际是错误的。

  3. 然后,我们在#pragma声明中加入一个新的參数:alpha參数。
    		CGPROGRAM
    		#pragma surface surf Lambert alpha

    解释:再解释一遍上面这句声明的意思。使用名为surf的Surface Function,使用内置的Lambert光照函数。开启透明通道。



  4. 最后,在surf()函数中加入控制透明度的代码。
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			o.Albedo = c.rgb;
    			o.Alpha = c.b * _TransVal;
    		}

完整代码例如以下:
Shader "Custom/SimpleAlpha" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_TransVal ("Transparency Value", Range(0,1)) = 0.5
	}
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Transparent"}
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert alpha

		sampler2D _MainTex;
		float _TransVal;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			o.Alpha = c.b * _TransVal;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}


假设没有透明效果。例如以下所看到的:

最后效果例如以下(从左到右分别相应了o.Alpha = c.r * _TransVal, o.Alpha = c.g * _TransVal, o.Alpha = c.b * _TransVal):




解释


你能够看到。使用Unity的Surface Shaders得到透明效果是非常easy的。

这类Shader依赖三个元素:正确设置Shader渲染队列,#pragma声明中的alpha參数,以及在SurfaceOutput结构体中的Alpha值。


Tags { "Queue"="Transparent" }一步将决定半透明物体和不透明物体之间正确的渲染关系,假设没有正确设置,那么非常有可能就会出现后面的物体跑到了透明物体的前面。详细解释能够參见这篇文章

一旦我们在#pragma声明中加入了alpha參数。这就告诉了Unity:嘿,接下来你要同意我渲染一个透明的surface。然后,我们就仅仅须要逐像素地使用一个取值范围为0到1的值来填充SurfaceOutput结构体中的O.Alpha值。

从颜色角度讲(这里的颜色指一个灰度值,由于透明度能够用一个单通道的灰度值来表示),一个为1的透明度,即白色,将会产出一个全然不透明的效果。而0值,即黑色,表示一个全然透明的效果。


这就解释了上述的效果。

比如,当我们使用例如以下语句控制透明度:

o.Alpha = c.r * _TransVal

则贴图中除了红色部分以及白色部分(白色的RGB通道值均为1)其R通道的值为1。其余(绿色和蓝色部分)均为0。因此仅仅有红色和白色的部分才不透明。


虽然关于透明度有非常多东西,但我们要知道,上述是其最主要的实现。

在以下的章节中,我们将開始怎样在实时渲染中使用alpha通道或者半透明的Shader。