示例转自:https://blog.csdn.net/poem_qianmo/article/details/42215079
效果预览
最基本的Surface Shader
先看一个使用内建光照模式的最基本的Surface Shader应该怎么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
Shader "浅墨Shader编程/Volume6/24.最基本的SurfaceShader" { //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式 #pragma surface surf Lambert //【2】输入结构 struct Input { //四元素的颜色值(RGBA) float4 color : COLOR; }; //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //反射率 o.Albedo = float3(0.5,0.8,0.3);//(0.5,0.8,0.3)分别对应于RGB分量 //而o.Albedo = 0.6;等效于写o.Albedo = float3(0.6,0.6,0.6); } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
可以发现,一个最基本的Surface Shader,至少需要有光照模式的声明、输入结构和表面着色函数的编写这三部分。
另外,主要注意其中的surf函数的写法,就是把上文讲到的Surface Output结构体中需要用到的成员变量拿来赋值:
1 2 3 4 5 6 7 |
//【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //反射率 o.Albedo = float3(0.5,0.8,0.3);//(0.5,0.8,0.3)分别对应于RGB分量 //而o.Albedo = 0.6;等效于写o.Albedo = float3(0.6,0.6,0.6); } |
注释中已经写得很明白,且之前也已经讲过,o.Albedo = 0.6;等效于写o.Albedo = float3(0.6,0.6,0.6);
来个举一反三,则o.Albedo =1;等效于写o.Albedo= float3(1,1,1);
颜色可调
在最基本的Surface Shader的基础上,加上一点代码,就成了这里的可调颜色的Surface Shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
Shader "浅墨Shader编程/Volume6/25.颜色可调的SurfaceShader" { //--------------------------------【属性】--------------------------------------- Properties { _Color ("【主颜色】Main Color", Color) = (0.1,0.3,0.9,1) } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType"="Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式 #pragma surface surf Lambert //变量声明 float4 _Color; //【2】输入结构 struct Input { //四元素的颜色值(RGBA) float4 color : COLOR; }; //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //反射率 o.Albedo = _Color.rgb; //透明值 o.Alpha = _Color.a; } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 FallBack "Diffuse" } |
基本纹理载入
再来看看如何实现一个基本的纹理载入Surface Shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
Shader "浅墨Shader编程/Volume6/26.基本纹理载入" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式 #pragma surface surf Lambert //【2】输入结构 struct Input { //纹理的uv值 float2 uv_MainTex; }; //变量声明 sampler2D _MainTex; //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //从纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
凹凸纹理载入
让我们慢慢添加特性,使得到的Surface Shader的效果与功能越来越强大。接着来看看Surface Shader的凹凸纹理如何实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
Shader "浅墨Shader编程/Volume6/27.凹凸纹理载入" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {} } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式 #pragma surface surf Lambert //【2】输入结构 struct Input { //主纹理的uv值 float2 uv_MainTex; //凹凸纹理的uv值 float2 uv_BumpMap; }; //变量声明 sampler2D _MainTex;//主纹理 sampler2D _BumpMap;//凹凸纹理 //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //从凹凸纹理获取法线值 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
纹理载入+颜色可调
接着看一看纹理如何通过一个finalcolor关键字自定义函数,来达到调色的目的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
Shader "浅墨Shader编程/Volume6/28.纹理+颜色修改" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _ColorTint ("【色泽】Tint", Color) = (0.6, 0.3, 0.6, 0.3) } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数 #pragma surface surf Lambert finalcolor:setcolor //【2】输入结构 struct Input { //纹理的uv值 float2 uv_MainTex; }; //变量声明 fixed4 _ColorTint; sampler2D _MainTex; //【3】自定义颜色函数setcolor的编写 void setcolor (Input IN, SurfaceOutput o, inout fixed4 color) { //将自选的颜色值乘给color color *= _ColorTint; } //【4】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
凹凸纹理+边缘光照
在之前凹凸纹理的基础上让我们加上喜闻乐见的边缘光照:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
Shader "浅墨Shader编程/Volume6/29.凹凸纹理+边缘光照" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {} _RimColor ("【边缘颜色】Rim Color", Color) = (0.26,0.19,0.16,0.0) _RimPower ("【边缘颜色强度】Rim Power", Range(0.5,8.0)) = 3.0 } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数 #pragma surface surf Lambert //【2】输入结构 struct Input { //主纹理的uv值 float2 uv_MainTex; //凹凸纹理的uv值 float2 uv_BumpMap; //当前坐标的视角方向 float3 viewDir; }; //变量声明 sampler2D _MainTex;//主纹理 sampler2D _BumpMap;//凹凸纹理 float4 _RimColor;//边缘颜色 float _RimPower;//边缘颜色强度 //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //从凹凸纹理获取法线值 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); //从_RimColor参数获取自发光颜色 half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); o.Emission = _RimColor.rgb * pow (rim, _RimPower); } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
其中的viewDir 意为WorldSpace View Direction,也就是当前坐标的视角方向:
关于surf中的两句新加的代码在这里也讲一下。
上面已经提到过,Normalize函数,用于获取到的viewDir坐标转成一个单位向量且方向不变,外面再与点的法线做点积。最外层再用 saturate算出一[0,1]之间的最靠近的值。这样算出一个rim边界。原理可以这样解释:
这里o.Normal就是单位向量。外加Normalize了viewDir。因此求得的点积就是夹角的cos值。因为cos值越大,夹角越小,所以,这时取反来。这样,夹角越大,所反射上的颜色就越多。于是就得到的两边发光的效果。哈哈这样明了吧。
这里再介绍一下这个half。CG里还有类似的float和fixed。half是一种低精度的float,但有时也会被选择成与float一样的精度。先就说这么多吧,后面还会遇到的,到时候再讲。
我们将此Shader编译后赋给材质,得到如下效果,除了凹凸纹理的选择之外,还有边缘发光颜色和强度可供自由定制:
凹凸纹理+颜色可调
接下来我们看看凹凸纹理+颜色可调的Shader怎么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Shader "浅墨Shader编程/Volume6/30.凹凸纹理+颜色可调+边缘光照" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {} _RimColor ("【边缘颜色】Rim Color", Color) = (0.26,0.19,0.16,0.0) _RimPower ("【边缘颜色强度】Rim Power", Range(0.5,8.0)) = 3.0 _ColorTint ("【色泽】Tint", Color) = (0.6, 0.3, 0.6, 0.3) } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数 #pragma surface surf Lambert finalcolor:setcolor //【2】输入结构 struct Input { //主纹理的uv值 float2 uv_MainTex; //凹凸纹理的uv值 float2 uv_BumpMap; //当前坐标的视角方向 float3 viewDir; }; //变量声明 sampler2D _MainTex;//主纹理 sampler2D _BumpMap;//凹凸纹理 float4 _RimColor;//边缘颜色 float _RimPower;//边缘颜色强度 float4 _ColorTint;//色泽 void setcolor(Input IN, SurfaceOutput o, inout fixed4 color) { //将自选的颜色值乘给color color *= _ColorTint; } //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //从凹凸纹理获取法线值 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); //从_RimColor参数获取自发光颜色 half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); o.Emission = _RimColor.rgb * pow (rim, _RimPower); } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
细节纹理
接着我们来看一个在屏幕上显示纹理细节的Shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
Shader "浅墨Shader编程/Volume6/31.细节纹理" { //--------------------------------【属性】---------------------------------------- Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _Detail ("【细节纹理】Detail", 2D) = "gray" {} } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式 #pragma surface surf Lambert //【2】输入结构 struct Input { //主纹理的uv值 float2 uv_MainTex; //细节纹理的uv值 float2 uv_Detail; }; //变量声明 sampler2D _MainTex; sampler2D _Detail; //【3】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //先从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //设置细节纹理 o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2; } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
凹凸纹理+颜色可调+边缘光照+细节纹理
结合上面的8个Shader,我们可以完成本期文章这个结合了凹凸纹理+颜色可调+边缘光照+细节纹理的稍微复杂一点的Surface Shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
Shader "浅墨Shader编程/Volume6/32.凹凸纹理+颜色可调+边缘光照+细节纹理" { Properties { _MainTex ("【主纹理】Texture", 2D) = "white" {} _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {} _Detail ("【细节纹理】Detail", 2D) = "gray" {} _ColorTint ("【色泽】Tint", Color) = (0.6, 0.3, 0.6, 0.3) _RimColor ("【边缘颜色】Rim Color", Color) = (0.26,0.19,0.16,0.0) _RimPower ("【边缘颜色强度】Rim Power", Range(0.5,8.0)) = 3.0 } //--------------------------------【子着色器】---------------------------------- SubShader { //-----------子着色器标签---------- Tags { "RenderType" = "Opaque" } //-------------------开始CG着色器编程语言段----------------- CGPROGRAM //【1】光照模式声明:使用兰伯特光照模式+自定义颜色函数 #pragma surface surf Lambert finalcolor:setcolor //【2】输入结构 struct Input { //主纹理的uv值 float2 uv_MainTex; //凹凸纹理的uv值 float2 uv_BumpMap; //细节纹理的uv值 float2 uv_Detail; //当前坐标的视角方向 float3 viewDir; }; //变量声明 sampler2D _MainTex; sampler2D _BumpMap; sampler2D _Detail; fixed4 _ColorTint; float4 _RimColor; float _RimPower; //【3】自定义颜色函数setcolor的编写 void setcolor (Input IN, SurfaceOutput o, inout fixed4 color) { color *= _ColorTint; } //【4】表面着色函数的编写 void surf (Input IN, inout SurfaceOutput o) { //先从主纹理获取rgb颜色值 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //设置细节纹理 o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2; //从凹凸纹理获取法线值 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); //从_RimColor参数获取自发光颜色 half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); o.Emission = _RimColor.rgb * pow (rim, _RimPower); } //-------------------结束CG着色器编程语言段------------------ ENDCG } //“备胎”为普通漫反射 Fallback "Diffuse" } |
- 本文固定链接: http://www.u3d8.com/?p=2468
- 转载请注明: 网虫虫 在 u3d8.com 发表过