除了基础的vertex fragment Shader,Unity还给我们提供了另一种方式,只要我们定义一些参数,Unity将自动生成包含复杂光照计算的Shader,这种Shader被称为“Surface Shaders”.
在开始Surface Shaders 之前,最好参考我们之前的教程,先了解无光照Shader。
ResultConversion to simple Surface Shader
下面我们基于上一节的 Shader 代码将它修改为 Surface Shader。删除我们的vertex 函数,删除掉 vertex 和 fragment 函数的 pragma定义,删除input 和 v2f,删除掉MainTex_ST 变量 ,以及UnityCG的引用文件。移除掉Pass 标记,因为Unity会为我们生成Pass。现在我们的Shader变成了下面这样:
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags{ "RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
sampler2D _MainTex;
fixed4 _Color;
fixed4 frag (v2f) : SV_TARGET {
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
return col;
}
ENDCG
}
FallBack "Standard"
}
现在它还不能正常使用,让我们来添加一些东西,使其成为能正常工作的surface shader。
首先我们添加一个结构体Input,它将包含用来设置surface颜色的所有信息。在这里,它仅仅包含uv 坐标信息,声明一个 float2 类型的变量,将它命名为 uv_MainTex,注意这个名称非常重要,它自动包含我们tiling和offset信息。如果我们使用别的名称,需要用对应的 uvTextureName 以便获得贴图的uv信息。
struct Input {
float2 uv_MainTex;
};
修改 fragment 函数为surface 函数,重命名为surf,修改返回类型为void。
两个参数,第一个为我们上面声明的Input结构体的实例,这样我们能获得每个顶点的信息。第二个参数SurfaceOutputStandard,用来输出surf函数结果,声明参数类型为inout。它包含了unity用来进行计算光照的所有信息。光照计算是基于物理的,后面我们将会解释。
删除函数的 sv_target 属性,因为unity也会自动帮我们处理。
最后需要删除 return 语句,因为上面返回值已经改为了void。设置输出部分的Albedo为贴图颜色乘上_Color。
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
}
然后需要像之前的教程中讲的 vertex 和 fragment shader一样,进行pragma声明。
以 #pragma 开头,后面跟着的是shader类型(surface),surface 函数的名称(surf),以及光照模型(Standard)。
现在到Unity中查看,它已经能正确显示,并且处理了光照信息。
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags{ "RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
sampler2D _MainTex;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
};
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
}
ENDCG
}
}
Simple Albedo Material
我们来看下 SurfaceOutputStandard 的属性:
-
Albedo
Albedo 漫反射颜色 -
Normal
切线空间法线 -
Emission
自发光颜色 -
Metallic
金属度;取0为非金属, 取1为金属 -
Smoothness
光泽度;取0为非常粗糙, 取1为非常光滑 -
Occlusion
遮挡(默认值为1) -
Alpha
透明度
现在让往shader中设置一些上面的属性值,emission,metallic 和 smoothness。
首先添加 两个标量类型 ,这里为half。
half _Smoothness;
half _Metallic;
然后把它们添加到Properties里面,这样可以在Inspector中修改它们的值。Properties不支持half,所以声明为float类型。
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
_Smoothness ("Smoothness", float) = 0
_Metallic ("Metalness", float) = 0
}
现在可以把 smoothness 和 metalness 赋值给 SurfaceOutputStandard 对应的属性。
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
}
float 类型的属性,当改变值的时候,非常容易调整为大于1或者小于0,我们可以改为range,这样就可以把它的值限定在0和1之间。
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
_Smoothness ("Smoothness", Range(0, 1)) = 0
_Metallic ("Metalness", Range(0, 1)) = 0
}
Inspector and Material with smoothness und metalness
最后我们添加 emissive 颜色。先在HLSL 代码中添加变量,然后添加对应的属性。
// ...
_Emission ("Emission", Color) = (0,0,0,1)
// ...
half3 _Emission;
// ...
o.Emission = _Emission;
Emissive Material
上面的颜色值最大值只能到1,如果想让它超过1,可以添加一个[HDR] 标记,这样亮度效果能更强。也可以把贴图作为emission color。
[HDR] _Emission ("Emission", Color) = (0,0,0,1)
HDR Inspector
Minor Improvements
最后,我们做两个小的操作,来让shader效果看起来更好一点。
首先,可以添加一个 fallback shader 在 subshader 后面,这样unity将会直接使用它,这样可以减少代码冗余。这里使用standard shader 作为fallback , unity 会使用里面的 shadow pass, 使material 向其他的物体上产生投影。
接着我们修改下pragma 命令,添加 fullforwardshadows ,这样能获得更好的影子效果。再添加一个#pragma target 3.0 ,这样untiy将使用更精确的数值,使效果更好一些。
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
_Smoothness ("Smoothness", Range(0, 1)) = 0
_Metallic ("Metalness", Range(0, 1)) = 0
[HDR] _Emission ("Emission", color) = (0,0,0)
}
SubShader {
Tags{ "RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
fixed4 _Color;
half _Smoothness;
half _Metallic;
half3 _Emission;
struct Input {
float2 uv_MainTex;
};
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
o.Emission = _Emission;
}
ENDCG
}
FallBack "Standard"
}
Result
通过今天的教程,你应该已经明白了如何写一个具有看起来还不错的光照效果的shader了。
网友评论