写在前面:

这个系列来源于书籍冯乐乐老师的《unity shader 入门精要》,
参考的代码库“https://github.com/candycat1992/Unity_Shaders_Book”
我自己跟着写了一遍代码,并在代码块里给shader代码加上了比较详细的注释
更为仔细的解释和unity shader原理和知识书里都有,本blog不做详细解释,推荐买书来看。
本项目的所有代码在https://github.com/takashiwangbh/Unity-shader-effect-reproduction/tree/main

介绍

Shader Basic Texture 旨在为 3D 物体应用基本的纹理贴图。通过采样材质中的纹理(_MainTex),将纹理颜色直接映射到物体表面并呈现到屏幕上。这里实现了四种基本的纹理效果:
基础纹理:直接将图案贴到物体表面,完成最基本的纹理映射。
凹凸纹理:无需增加实际几何面数,通过法线贴图模拟物体表面的凹凸效果,并支持可调节的凹凸强度。
渐变纹理:允许指定两种颜色,在物体表面实现平滑的颜色渐变过渡,为材质增加层次感。
遮罩纹理:通过控制光照的特定属性,提供更精确的材质调整能力,实现特定的视觉效果。

物体和shader的创建

这里要引入纹理,首先创建胶囊体。然后创建材质应用于物体,然后创建shader应用于材质,在材质应用shader以后,在材质的属性里能够添加纹理图或者是更改颜色,针对不同的shader或者想要表达的效果去更改属性就行。详细可以看我的项目里是怎么弄的。

shader代码

单张纹理

Shader "Unity Shaders Book/Chapter 7/Single Texture" {
    Properties {
        // 用于控制整个材质颜色的颜色属性
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        // 主纹理,用于叠加纹理颜色
        _MainTex ("Main Tex", 2D) = "white" {}
        // 镜面反射的颜色属性
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        // 控制镜面反射光泽度的范围属性
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader {        
        Pass { 
            // 指定渲染通道为 ForwardBase
            Tags { "LightMode"="ForwardBase" }
            
            CGPROGRAM
            
            #pragma vertex vert // 指定顶点函数
            #pragma fragment frag // 指定片段函数

            #include "Lighting.cginc" // 引入 Unity 的光照函数库
            
            // 定义用于材质属性的变量
            fixed4 _Color; 
            sampler2D _MainTex; // 定义采样器,用于纹理采样
            float4 _MainTex_ST; // 用于纹理缩放和偏移的变换矩阵
            fixed4 _Specular;
            float _Gloss;

            // 顶点输入结构,包含顶点位置、法线和纹理坐标
            struct a2v {
                float4 vertex : POSITION; // 顶点位置
                float3 normal : NORMAL; // 顶点法线
                float4 texcoord : TEXCOORD0; // 顶点纹理坐标
            };
            
            // 顶点到片段的输出结构,包含世界空间信息和纹理坐标
            struct v2f {
                float4 pos : SV_POSITION; // 裁剪空间位置
                float3 worldNormal : TEXCOORD0; // 世界空间法线
                float3 worldPos : TEXCOORD1; // 世界空间顶点位置
                float2 uv : TEXCOORD2; // 纹理坐标
            };
            
            // 顶点函数
            v2f vert(a2v v) {
                v2f o;
                // 将顶点从对象空间变换到裁剪空间
                o.pos = UnityObjectToClipPos(v.vertex);
                
                // 将法线从对象空间变换到世界空间
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                // 将顶点从对象空间变换到世界空间
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                // 计算纹理坐标,应用纹理缩放和偏移
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                // 或者直接使用内置函数
                // o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                
                return o;
            }
            
            // 片段函数
            fixed4 frag(v2f i) : SV_Target {
                // 规范化法线向量
                fixed3 worldNormal = normalize(i.worldNormal);
                // 规范化光源方向
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                
                // 使用纹理采样获取漫反射颜色
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
                
                // 计算环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                
                // 计算漫反射
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                
                // 计算观察方向
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                // 计算半向量
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                // 计算镜面反射
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
                
                // 返回最终颜色,包含环境光、漫反射和镜面反射
                return fixed4(ambient + diffuse + specular, 1.0);
            }
            
            ENDCG
        }
    } 
    // 如果不支持此 Shader,回退到默认镜面 Shader
    FallBack "Specular"
}

凹凸纹理

Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
	Properties {
		// 控制整体材质颜色的属性
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		// 纹理属性,主纹理
		_MainTex ("Main Tex", 2D) = "white" {}
		// 法线贴图的纹理属性
		_BumpMap ("Normal Map", 2D) = "bump" {}
		// 控制法线贴图的缩放强度
		_BumpScale ("Bump Scale", Float) = 1.0
		// 镜面反射的颜色属性
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		// 镜面高光的范围
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert // 顶点着色器函数
			#pragma fragment frag // 片段着色器函数
			
			#include "Lighting.cginc" // 引入 Unity 的光照函数库
			
			// 定义材质相关属性的变量
			fixed4 _Color;
			sampler2D _MainTex; // 主纹理采样器
			float4 _MainTex_ST; // 主纹理的缩放与偏移
			sampler2D _BumpMap; // 法线贴图采样器
			float4 _BumpMap_ST; // 法线贴图的缩放与偏移
			float _BumpScale; // 法线缩放强度
			fixed4 _Specular;
			float _Gloss;
			
			// 输入顶点数据
			struct a2v {
				float4 vertex : POSITION; // 顶点位置
				float3 normal : NORMAL; // 顶点法线
				float4 tangent : TANGENT; // 切线信息
				float4 texcoord : TEXCOORD0; // 纹理坐标
			};
			
			// 传递到片段着色器的数据
			struct v2f {
				float4 pos : SV_POSITION; // 裁剪空间位置
				float4 uv : TEXCOORD0; // 主纹理和法线纹理坐标
				float3 lightDir: TEXCOORD1; // 切线空间的光方向
				float3 viewDir : TEXCOORD2; // 切线空间的视角方向
			};

			// 顶点着色器
			v2f vert(a2v v) {
				v2f o;
				// 转换顶点到裁剪空间
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// 计算主纹理和法线贴图的纹理坐标
				o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

				// 构建从切线空间到世界空间的变换矩阵
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
				fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
				fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
				
				// 构建从世界空间到切线空间的变换矩阵
				float3x3 worldToTangent = float3x3(worldTangent, worldBinormal, worldNormal);

				// 将光方向和视角方向从世界空间转换到切线空间
				o.lightDir = mul(worldToTangent, WorldSpaceLightDir(v.vertex));
				o.viewDir = mul(worldToTangent, WorldSpaceViewDir(v.vertex));
				
				return o;
			}
			
			// 片段着色器
			fixed4 frag(v2f i) : SV_Target {
				// 规范化光方向和视角方向
				fixed3 tangentLightDir = normalize(i.lightDir);
				fixed3 tangentViewDir = normalize(i.viewDir);
				
				// 从法线贴图中获取纹理数据
				fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
				fixed3 tangentNormal;
				
				// 解压法线贴图数据到切线空间的法线
				tangentNormal = UnpackNormal(packedNormal);
				// 应用法线缩放强度
				tangentNormal.xy *= _BumpScale;
				// 重新计算 z 分量,确保法线单位化
				tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
				
				// 获取主纹理颜色并乘以整体颜色调节
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				
				// 计算环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				// 计算漫反射光
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));

				// 计算镜面高光
				fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
				
				// 返回最终颜色,合成环境光、漫反射和高光
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	// 如果当前设备不支持此 Shader,使用默认镜面 Shader
	FallBack "Specular"
}


渐变纹理

Shader "Unity Shaders Book/Chapter 7/Ramp Texture" {
    Properties {
        // 定义用于调整的属性,允许用户在 Unity 编辑器中修改这些值
        _Color ("Color Tint", Color) = (1, 1, 1, 1) // 主纹理的颜色叠加
        _RampTex ("Ramp Tex", 2D) = "white" {} // 渐变纹理,用于控制漫反射效果
        _Specular ("Specular", Color) = (1, 1, 1, 1) // 高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 高光的光滑度值,控制高光的聚焦程度
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" } // 定义光照模式为 Forward 渲染路径

            CGPROGRAM

            #pragma vertex vert // 指定顶点着色器
            #pragma fragment frag // 指定片段着色器

            #include "Lighting.cginc" // 引入 Unity 的光照计算库

            // 定义属性对应的变量
            fixed4 _Color; // 颜色叠加
            sampler2D _RampTex; // 渐变纹理
            float4 _RampTex_ST; // 渐变纹理的 Tiling 和 Offset 设置
            fixed4 _Specular; // 高光颜色
            float _Gloss; // 高光的光滑度

            // 顶点输入结构体
            struct a2v {
                float4 vertex : POSITION; // 顶点位置
                float3 normal : NORMAL; // 顶点法线
                float4 texcoord : TEXCOORD0; // UV 坐标
            };

            // 片段着色器输入结构体
            struct v2f {
                float4 pos : SV_POSITION; // 裁剪空间的顶点位置
                float3 worldNormal : TEXCOORD0; // 世界空间的法线
                float3 worldPos : TEXCOORD1; // 世界空间的顶点位置
                float2 uv : TEXCOORD2; // 纹理 UV 坐标
            };

            // 顶点着色器
            v2f vert(a2v v) {
                v2f o;
                // 将顶点从对象空间转换到裁剪空间
                o.pos = UnityObjectToClipPos(v.vertex);

                // 计算世界空间的法线
                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                // 计算顶点的世界空间位置
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                // 应用 Tiling 和 Offset 到 UV 坐标
                o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);

                return o;
            }

            // 片段着色器
            fixed4 frag(v2f i) : SV_Target {
                // 归一化法线向量
                fixed3 worldNormal = normalize(i.worldNormal);

                // 计算世界空间的光照方向
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                // 环境光分量
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                // 使用渐变纹理对漫反射颜色进行采样
                fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5; // Half-Lambert 计算
                fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;

                // 漫反射分量
                fixed3 diffuse = _LightColor0.rgb * diffuseColor;

                // 计算视线方向和半角向量
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldLightDir + viewDir);

                // 高光分量
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                // 返回片段颜色值,包括环境光、漫反射和高光分量
                return fixed4(ambient + diffuse + specular, 1.0);
            }

            ENDCG
        }
    } 
    FallBack "Specular" // 设置备用着色器,当显卡不支持本着色器时使用 Specular 着色器
}

遮罩纹理

Shader "Unity Shaders Book/Chapter 7/Mask Texture" {
    Properties {
        _Color ("Color Tint", Color) = (1, 1, 1, 1) // 调整主纹理颜色的色彩叠加值
        _MainTex ("Main Tex", 2D) = "white" {} // 主要的贴图,用于定义表面颜色
        _BumpMap ("Normal Map", 2D) = "bump" {} // 法线贴图,用于模拟表面细节
        _BumpScale("Bump Scale", Float) = 1.0 // 法线强度的缩放因子
        _SpecularMask ("Specular Mask", 2D) = "white" {} // 高光遮罩贴图,用于控制表面高光的分布
        _SpecularScale ("Specular Scale", Float) = 1.0 // 高光遮罩强度的缩放因子
        _Specular ("Specular", Color) = (1, 1, 1, 1) // 高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 高光的光滑度值,控制高光的聚焦程度
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" } // 定义光照模式为 Forward 渲染路径

            CGPROGRAM

            #pragma vertex vert // 指定顶点着色器
            #pragma fragment frag // 指定片段着色器

            #include "Lighting.cginc" // 引入 Unity 的光照计算库

            // 定义属性对应的变量
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST; // 主纹理的 Tiling 和 Offset 设置
            sampler2D _BumpMap;
            float _BumpScale;
            sampler2D _SpecularMask;
            float _SpecularScale;
            fixed4 _Specular;
            float _Gloss;

            // 输入顶点结构体
            struct a2v {
                float4 vertex : POSITION; // 顶点位置
                float3 normal : NORMAL; // 顶点法线
                float4 tangent : TANGENT; // 顶点切线,用于计算切线空间
                float4 texcoord : TEXCOORD0; // UV 坐标
            };

            // 传递给片段着色器的结构体
            struct v2f {
                float4 pos : SV_POSITION; // 裁剪空间的顶点位置
                float2 uv : TEXCOORD0; // 纹理坐标
                float3 lightDir: TEXCOORD1; // 光照方向(切线空间)
                float3 viewDir : TEXCOORD2; // 视线方向(切线空间)
            };

            v2f vert(a2v v) {
                v2f o;
                // 将顶点从对象空间转换到裁剪空间
                o.pos = UnityObjectToClipPos(v.vertex);

                // 计算主纹理的 UV 坐标(应用 Tiling 和 Offset)
                o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                // 使用 Unity 提供的宏计算切线空间旋转矩阵
                TANGENT_SPACE_ROTATION;
                // 将光照方向和视线方向从对象空间转换到切线空间
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                // 将光照方向和视线方向归一化
                fixed3 tangentLightDir = normalize(i.lightDir);
                fixed3 tangentViewDir = normalize(i.viewDir);

                // 从法线贴图中获取切线空间的法线值
                fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
                // 使用缩放因子调整法线值的 x 和 y 分量
                tangentNormal.xy *= _BumpScale;
                // 重新计算 z 分量,确保法线向量长度为 1
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

                // 读取主纹理颜色,并应用颜色叠加
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;

                // 计算环境光反射分量
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                // 计算漫反射分量
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));

                // 计算高光分量
                fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); // 半角向量
                // 从高光遮罩贴图中获取遮罩值,并乘以缩放因子
                fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
                // 使用遮罩值计算高光分量
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;

                // 返回最终的颜色值,包括环境光、漫反射和高光
                return fixed4(ambient + diffuse + specular, 1.0);
            }

            ENDCG
        }
    } 
    FallBack "Specular" // 设置备用着色器
}

效果

在这里插入图片描述

更多推荐