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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
| Shader "Unity Shaders Book/Common/Bumped Diffuse" {
// Properties 中定义的属性会出现在材质面板中,支持进行动态控制 Properties { _Color ("Color Tint", Color) = (1, 1, 1, 1) // 控制物体的整体色调 _MainTex ("Main Tex", 2D) = "white" {} // 控制物体的漫反射纹理(MainTex) _BumpMap ("Normal Map", 2D) = "bump" {} // 控制物体的法线纹理(BumpMap) } SubShader {
// RenderType - 指定当前 shader 作用的对象的渲染类型 // Opaque: 用于大多数着色器(法线着色器、环境光着色器、反射着色器以及地形的着色器)。 // // Queue - 指定当前 shader 作用的对象的渲染顺序 // Geometry (2000) 不透明物体的渲染队列。大多数物体都应该使用该队列进行渲染,也是Unity Shader中默认的渲染队列。 Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
// LightMode 标签是 Pass 标签中的一种,它用于定义该 Pass 在 Unity 的流水线中的角色 (只有定义它才能获取到一些 Unity 内置的光照变量如 _LightColor0 // 告诉渲染管线,这个 pass 作为 ForwardBase 处理,缺少它将画不出任何东西 // ForwardBase 通道渲染一个逐像素方向光和所有的顶点/球面调和光。 // 此通道还负责渲染着色器中的环境光光照贴图。 // 在此通道中渲染的方向光可以产生阴影。 Tags { "LightMode"="ForwardBase" } // 使用 CGPROGRAM 和 ENDCG 来包围 CG 代码片 CGPROGRAM // multi_compile_fwdbase 是 Unity 专门为 forwardbase 预定义的 multi_compile #pragma multi_compile_fwdbase // 定义顶点着色器名字 - vert #pragma vertex vert
// 定义片元着色器名字 - frag #pragma fragment frag // 包含 Unity 的内置文件以使用 Unity 内置的一些变量 #include "Lighting.cginc" #include "AutoLight.cginc" fixed4 _Color; // 获取控制面板中物体的整体色调 sampler2D _MainTex; // 获取控制面板中漫反射纹理(MainTex)的属性值 float4 _MainTex_ST; // 获取控制面板中漫反射纹理(MainTex)属性的平铺和平移值 sampler2D _BumpMap; // 获取控制面板中法线纹理(BumpMap)的属性值 float4 _BumpMap_ST; // 获取控制面板中法线纹理(BumpMap)属性的平铺和平移值 struct a2v { float4 vertex : POSITION; // 模型空间的顶点坐标 float3 normal : NORMAL; // 模型空间的顶点法线方向 float4 tangent : TANGENT; // 模型空间的顶点切线方向,tangent.w 分量决定切线空间中的第三个坐标轴-副切线的方向性 float4 texcoord : TEXCOORD0; // 模型空间的第一套纹理坐标 }; struct v2f { float4 pos : SV_POSITION; // 顶点在裁剪空间中的位置信息 float4 uv : TEXCOORD0; // 用于存储纹理坐标的变量,以便在片元着色器中使用该坐标进行纹理采样 float4 TtoW0 : TEXCOORD1; // 存储顶点纹理坐标信息 float4 TtoW1 : TEXCOORD2; // 存储顶点纹理坐标信息 float4 TtoW2 : TEXCOORD3; // 存储顶点纹理坐标信息 SHADOW_COORDS(4) // 存储一个名为 _ShadowCoord 的阴影纹理坐标变量 }; v2f vert(a2v v) { v2f o; // 声明输出结构
// 将顶点位置从模型空间转换到裁剪空间中 o.pos = UnityObjectToClipPos(v.vertex); // 贴图 UV // xy 分量存储了漫反射纹理(MainTex)坐标 // 使用漫反射纹理(MainTex)的属性值 _MainTex_ST 对顶点漫反射纹理(MainTex)坐标进行转换,得到最终的漫反射纹理(MainTex)坐标 // 计算过程是首先使用缩放属性 _MainTex_ST.xy 对顶点漫反射纹理(MainTex)坐标进行缩放,然后再使用偏移属性 _MainTex_ST.zw 对结果进行偏移 o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 法线 UV // zw 分量存储了法线纹理(BumpMap)坐标 // 使用法线纹理(BumpMap)的属性值 _BumpMap_ST 对顶点法线纹理(BumpMap)坐标进行转换,得到最终的法线纹理(BumpMap)坐标 // 计算过程是首先使用缩放属性 _BumpMap_ST.xy 对顶点法线纹理(BumpMap)坐标进行缩放,然后再使用偏移属性 _BumpMap_ST.zw 对结果进行偏移 o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; // 获取世界空间中的顶点位置(模型空间转世界空间) float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 获取世界空间下的法线(模型空间转世界空间) fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
// 获取世界空间下的切线(模型空间转世界空间) fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
// 获取世界空间下的副法线(模型空间转世界空间) // 副法线计算,叉积(单位法线,单位切线)*切线⽅向 fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 构造切线空间到世界空间的旋转矩阵 o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); TRANSFER_SHADOW(o); // 得到一个用于采样阴影贴图的坐标 return o; // 返回输出结果 } fixed4 frag(v2f i) : SV_Target {
// 构建世界空间下的坐标 float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
// 计算世界空间下的光线方向 fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
// 计算世界空间下的视角方向 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // 使用 UnpackNormal 对法线纹理(BumpMap)进行采样和解码(需要把法线问题的格式识别为 Normal map) fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
// 使用 TtoW0、TtoW1、TtoW2 存储的变换矩阵把法线变换到世界空间下 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); // 使用 CG 的 tex2D 函数对漫反射纹理(MainTex)进行采样 // tex2D 函数参数一是需要被采样的漫反射纹理(MainTex) // tex2D 函数参数二是对应顶点的漫反射纹理(MainTex)坐标 // tex2D 函数返回值是计算得到的纹素值 // 材质的反射率 albedo 是采样结果和颜色属性 _Color 的乘积 fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb; // 环境光计算: ambient 是材质反射率与环境光照的乘积 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; // 漫反射计算:利用 albedo 计算漫反射光照的结果 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir)); // 计算光照衰减系数:实现代码位于 AutoLight 导入文件之中,使用之前需要 #include "AutoLight.cginc" 使用这个宏后,看起来衰减不再工作了 // 参数一为返回值(光照衰减系数) // 参数二用于阴影计算 // 参数三是世界坐标 UNITY_LIGHT_ATTENUATION(atten, i, worldPos); // 结果 = 环境光 + 漫反射 * 光照衰减系数 return fixed4(ambient + diffuse * atten, 1.0); } ENDCG } Pass { // LightMode 标签是 Pass 标签中的一种,它用于定义该 Pass 在 Unity 的流水线中的角色 (只有定义它才能获取到一些 Unity 内置的光照变量如 _LightColor0 // 告诉渲染管线,这个 pass 作为 ForwardBase 处理,缺少它将画不出任何东西 // 其他的像素光在附加通道中进行渲染,每个光源都需要一个通道。 // 在这些通道中渲染的光源无法产生阴影。 Tags { "LightMode"="ForwardAdd" } // 基于叠加模式(无透明效果) Blend One One // 使用 CGPROGRAM 和 ENDCG 来包围 CG 代码片 CGPROGRAM // multi_compile_fwdadd 是 Unity 专门为 ForwardAdd 预定义的 multi_compile #pragma multi_compile_fwdadd // Use the line below to add shadows for point and spot lights // #pragma multi_compile_fwdadd_fullshadows // 定义顶点着色器名字 - vert #pragma vertex vert
// 定义片元着色器名字 - frag #pragma fragment frag // 包含 Unity 的内置文件以使用 Unity 内置的一些变量 #include "Lighting.cginc" #include "AutoLight.cginc" fixed4 _Color; // 获取控制面板中物体的整体色调 sampler2D _MainTex; // 获取控制面板中漫反射纹理(MainTex)的属性值 float4 _MainTex_ST; // 获取控制面板中漫反射纹理(MainTex)属性的平铺和平移值 sampler2D _BumpMap; // 获取控制面板中法线纹理(BumpMap)的属性值 float4 _BumpMap_ST; // 获取控制面板中法线纹理(BumpMap)属性的平铺和平移值 struct a2v { float4 vertex : POSITION; // 模型空间的顶点坐标 float3 normal : NORMAL; // 模型空间的顶点法线方向 float4 tangent : TANGENT; // 模型空间的顶点切线方向,tangent.w 分量决定切线空间中的第三个坐标轴-副切线的方向性 float4 texcoord : TEXCOORD0; // 模型空间的第一套纹理坐标 }; struct v2f { float4 pos : SV_POSITION; // 顶点在裁剪空间中的位置信息 float4 uv : TEXCOORD0; // 用于存储纹理坐标的变量,以便在片元着色器中使用该坐标进行纹理采样 float4 TtoW0 : TEXCOORD1; // 存储顶点纹理坐标信息 float4 TtoW1 : TEXCOORD2; // 存储顶点纹理坐标信息 float4 TtoW2 : TEXCOORD3; // 存储顶点纹理坐标信息 SHADOW_COORDS(4) // 存储一个名为 _ShadowCoord 的阴影纹理坐标变量 }; v2f vert(a2v v) { v2f o; // 声明输出结构
// 将顶点位置从模型空间转换到裁剪空间中 o.pos = UnityObjectToClipPos(v.vertex); // 贴图 UV // xy 分量存储了漫反射纹理(MainTex)坐标 // 使用漫反射纹理(MainTex)的属性值 _MainTex_ST 对顶点漫反射纹理(MainTex)坐标进行转换,得到最终的漫反射纹理(MainTex)坐标 // 计算过程是首先使用缩放属性 _MainTex_ST.xy 对顶点漫反射纹理(MainTex)坐标进行缩放,然后再使用偏移属性 _MainTex_ST.zw 对结果进行偏移 o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 法线 UV // zw 分量存储了法线纹理(BumpMap)坐标 // 使用法线纹理(BumpMap)的属性值 _BumpMap_ST 对顶点法线纹理(BumpMap)坐标进行转换,得到最终的法线纹理(BumpMap)坐标 // 计算过程是首先使用缩放属性 _BumpMap_ST.xy 对顶点法线纹理(BumpMap)坐标进行缩放,然后再使用偏移属性 _BumpMap_ST.zw 对结果进行偏移 o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; // 获取世界空间中的顶点位置(模型空间转世界空间) float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 获取世界空间下的法线(模型空间转世界空间) fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
// 获取世界空间下的切线(模型空间转世界空间) fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
// 获取世界空间下的副法线(模型空间转世界空间) // 副法线计算,叉积(单位法线,单位切线)*切线⽅向 fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 构造切线空间到世界空间的旋转矩阵 o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); TRANSFER_SHADOW(o); // 得到一个用于采样阴影贴图的坐标 return o; // 返回输出结果 } fixed4 frag(v2f i) : SV_Target { // 构建世界空间下的坐标 float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
// 计算世界空间下的光线方向 fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
// 计算世界空间下的视角方向 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // 使用 UnpackNormal 对法线纹理(BumpMap)进行采样和解码(需要把法线问题的格式识别为 Normal map) fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
// 使用 TtoW0、TtoW1、TtoW2 存储的变换矩阵把法线变换到世界空间下 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); // 使用 CG 的 tex2D 函数对漫反射纹理(MainTex)进行采样 // tex2D 函数参数一是需要被采样的漫反射纹理(MainTex) // tex2D 函数参数二是对应顶点的漫反射纹理(MainTex)坐标 // tex2D 函数返回值是计算得到的纹素值 // 材质的反射率 albedo 是采样结果和颜色属性 _Color 的乘积 fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb; // 漫反射计算:利用 albedo 计算漫反射光照的结果 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir)); // 计算光照衰减系数:实现代码位于 AutoLight 导入文件之中,使用之前需要 #include "AutoLight.cginc" 使用这个宏后,看起来衰减不再工作了 // 参数一为返回值(光照衰减系数) // 参数二用于阴影计算 // 参数三是世界坐标 UNITY_LIGHT_ATTENUATION(atten, i, worldPos); // 结果 = 漫反射 * 光照衰减系数 return fixed4(diffuse * atten, 1.0); } ENDCG } }
// 设置回调 shader 为内置 Diffuse,上述 SubShader 失败后用于回调的 Unity Shader FallBack "Diffuse" }
|