unity shader效果教程附详细代码_基础纹理
·
写在前面:
这个系列来源于书籍冯乐乐老师的《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" // 设置备用着色器
}
效果

更多推荐


所有评论(0)