
Shader
渲染流程二维坐标旋转旋转坐标系,坐标不变,获取坐(x,y)标在新坐标下的坐标(x,y)。二维旋转矩阵原先坐标系通过矩阵拉伸获取新的坐标系,新坐标系的基向量x(3,1),y(1,2), 新坐标系中的的向量(-1,2)在原坐标系中值为(-1,3)。(-1,2)为新坐标系中的向量,与新坐标的基向量相乘,获得原坐标系该向量的坐标。矩阵相当于对原坐标进行缩放得到旋转坐标系=反向旋转物体phong光照模型环境
渲染流程
二维坐标旋转
旋转坐标系,坐标不变,获取坐(x,y)标在新坐标下的坐标(x,y
)。
二维旋转矩阵
原先坐标系通过矩阵拉伸获取新的坐标系,新坐标系的基向量x(3,1),y(1,2) , 新坐标系中的的向量(-1,2)在原坐标系中值为(-1,3)。
(-1,2)为新坐标系中的向量,与新坐标的基向量相乘,获得原坐标系该向量的坐标。
矩阵相当于对原坐标进行缩放得到
旋转坐标系 = 反向旋转物体
phong光照模型
环境光:UNITY_LIGHTMODEL_AMBIENT.xyz * _Color;
漫反射:入射光 点乘 法线 max(0, dot(worldNormal, worldLightDir)) * _Color;
高光反射:视角方向 点乘 反射方向pow(max(0, dot(worldLightReflectDir, worldViewDir)),_Speclur) * _Color;
自发光:就是自己输入颜色
Shader "Unlit/Phong"
{
Properties
{
_Color("Color",Color)=(1,1,1,1)
_EmissiveColor("EmissiveColor",Color)=(0,0,0,0)
_Speclur("Speclur",int)=2
}
SubShader
{
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _Color;
float4 _EmissiveColor;
float _Speclur;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//使用内置宏获取当前片元到光源的方向,里面已经处理了不同光源
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//世界坐标下的法线
float3 worldNormal = normalize(i.worldNormal);
//世界坐标下的视角方向
float3 worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
//光线在当前片元对应的法线下的反射方向
float3 worldLightReflectDir = normalize(reflect(-worldLightDir, worldNormal));
//环境光分量,直接用宏定义获取 此值是在unity编辑器内的光照窗口设定
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color;
//漫反射分量,使用表面法线点乘光源方向
float3 diffuse = max(0, dot(worldNormal, worldLightDir)) * _Color;
//高光分量,使用视角方向点乘光的反射方向
float3 speclur = pow(max(0, dot(worldLightReflectDir, worldViewDir)),_Speclur) * _Color;
//自发光分量,直接使用设定值
float3 emissive = _EmissiveColor.xyz;
return fixed4(diffuse + ambient + speclur+emissive, 1);
//return fixed4(speclur , 1);
}
ENDCG
}
}
}
Blinn Phong
Phong模型会出现光照截断或者过度不自然的现象。这是由于反射光方向和视线方向夹角小于90,才会使镜面反射的值为非零。
BlinnPhong使用视线与反射光线的中间向量与法线的点乘进行计算,这样做在任何情况下镜面反射都不会小于0。从而解决Phong模型面临的问题。
//blinn-phong模型下的新变量 光照方向加上视角方向之后归一化
float3 halfDir = normalize(worldLightDir+worldViewDir);
//高光分量,使用视角方向点乘光的反射方向
float3 speclur = pow(max(0, dot(halfDir, worldNormal)),_Speclur) * _Color;
Shader "Unlit/Blinn_phong"
{
Properties
{
_Color("Color",Color)=(1,1,1,1)
_EmissiveColor("EmissiveColor",Color)=(0,0,0,0)
_Speclur("Speclur",int)=10
}
SubShader
{
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _Color;
float4 _EmissiveColor;
float _Speclur;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//使用内置宏获取当前片元到光源的方向,里面已经处理了不同光源
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//世界坐标下的法线
float3 worldNormal = normalize(i.worldNormal);
//世界坐标下的视角方向
float3 worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
//blinn-phong模型下的新变量 光照方向加上视角方向之后归一化
float3 halfDir = normalize(worldLightDir+worldViewDir);
//环境光分量,直接用宏定义获取 此值是在unity编辑器内的光照窗口设定
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color;
//漫反射分量,使用表面法线点乘光源方向
float3 diffuse = max(0, dot(worldNormal, worldLightDir)) * _Color;
//高光分量,使用视角方向点乘光的反射方向
float3 speclur = pow(max(0, dot(halfDir, worldNormal)),_Speclur) * _Color;
//自发光分量,直接使用设定值
float3 emissive = _EmissiveColor.xyz;
return fixed4(ambient + diffuse + speclur+emissive, 1);
//return fixed4(speclur , 1);
}
ENDCG
}
}
}
纹理采样
_MainTex(“Texture”,2D) = “white” {}
sampler2D _MainTex;
float4 _MainTex_ST;
o.uv = v.uv.xy*_MainTex_ST.xy+_MainTex_ST.zw; //TRANSFORM_TEX(v.uv,_MainTex);
fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
Shader "Unlit/009" //纹理采样
{
Properties
{
_MainTex("Texture",2D) = "white" {}
_Color("Color",Color) = (1,1,1,1)
_Specular("Specular",Int) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Specular;
fixed4 _Gloss;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldPos : TEXCOORD2;
};
v2f vert(appdata v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
o.vertex = UnityObjectToClipPos(v.vertex);
// 计算纹理坐标(缩放和平移)
//_MainTex_ST.xy为纹理图Tiling(缩放), zw为纹理中的offset(偏移)
// 如果Tiling 和 Offset留的是默认值,即Tiling为(1,1) Offset为(0,0)的时候,可以不用TRANSFORM_TEX运算
o.uv = v.uv.xy*_MainTex_ST.xy+_MainTex_ST.zw; //TRANSFORM_TEX(v.uv,_MainTex);
// 将法线从模型空间变换到世界空间 等价于o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// 对纹理进行采样
fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
// 获得单位世界光向量
// 在 "LightMode" = "ForwardBase"情况下:
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 worldNormal = normalize(i.worldNormal);
// 获得单位观察向量
// 等价于fixed3 viewDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos);
float3 worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
float3 worldLightReflectDir = normalize(reflect(-worldLightDir,worldNormal));
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color;
float3 diffuse = _LightColor0.rgb * albedo.rgb * (dot(worldNormal,worldLightDir) * 0.5 +0.5) * _Color;
float3 speclur = pow(max(0,dot(worldLightReflectDir,worldViewDir)),_Specular)*_Color;
return fixed4(ambient + diffuse + speclur,1);
}
ENDCG
}
}
}
凹凸映射
不改变顶点位置前提下,修改模型表面法线方向,为模型提供更多细节
方法一:高度纹理
使用一张高度纹理来模拟表面位移(Displacement),然后得到一个修改后的法线值。此方法也叫做高度映射(Height Mapping)。
颜色越浅,越向外凸;颜色越深,越向内凹。能明确表面的凹凸信息,缺点是计算复杂。
方法二:法线纹理
使用一张法线纹理来直接存储表面法线。此方法也叫做法线映射(Normal Mapping)。用于存储表面的法线向量,法线向量的取值范围为 [ -1 , 1] 。但是像素分量的取值范围是 [ 0 , 1]
切线空间,即使用顶点的切线作为x轴,法线作为z轴,法线与切线的叉积作为y轴。
切线空间的法线纹理:实际上美术一般使用的是切线空间的法线纹理。切线空间即是每个顶点自己的空间。切线空间纹理往往是蓝色。的,因为不需要做凹凸的就都是一个方向的。
优点:由于是切线空间,用在别的模型上也会得到一个相对合理的效果。可进行UV动画,可重用法线纹理,可压缩(只记录XY即可)。
1.在顶点着色器中
(1)通过模型空间法线方向和切线方向叉积得到副切线方向,获取切线坐标变换矩阵 TANGENT_SPACE_ROTATION
(2) 将模型空间下的光向量和 模型空间下的观察向量变换到切线空间
2.在片元着色器中
(1)获取切线空间法线: 压缩后的纹理UnpackNormal(tex2D(_BumpMap, i.uv.zw))
获取切线坐标法线
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
Shader "Unlit/012" //凹凸映射 切线空间计算法线
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("MainTex", 2D) = "white" { }
// 法线纹理(凹凸贴图),"bump"是Unity内置的法线纹理,当没有提供法线纹理时,
// "bump"就对应了模型自带的法线信息
_BumpMap ("NormalMap", 2D) = "bump" { }
// 控制凹凸程度,为0时意味着法线纹理不会对光照产生影响
_BumpScale ("BumpScale", float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
// _MainTex纹理的缩放和偏移系数
float4 _MainTex_ST;
sampler2D _BumpMap;
// _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类型,xy存储_MainTex的纹理坐标,zw存储_BumpMap的纹理坐标
float4 uv: TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir: TEXCOORD2;
};
v2f vert (a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// xy存储_MainTex的纹理坐标
// 等价于o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
// zw存储_BumpMap的纹理坐标
// 等价于o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
// TANGENT_SPACE_ROTATION(切线空间到模型空间的变换矩阵)等价于:
// 使用模型空间下的法线方向和切线方向叉积得到副切线方向
// float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
//定义 3x3 变换矩阵 rotation,分别将切线方向、副切线方向和法线方向按行摆放组成了这个矩阵。
// float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
TANGENT_SPACE_ROTATION;
// 获得模型空间下的光向量
float3 objectLightDir = ObjSpaceLightDir(v.vertex);
// 将光向量从模型空间变换到切线空间
o.lightDir = mul(rotation, objectLightDir).xyz;
// 获得模型空间下的观察向量
float3 objectViewDir = ObjSpaceViewDir(v.vertex);
// 将观察向量从模型空间变换到切线空间
o.viewDir = mul(rotation, objectViewDir);
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;
// 若法线纹理Texture Type未设置成Normal map,
// 要从像素映射回法线,即[0, 1]转化到[-1, 1]
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// 如果设置了Normal map类型,Unity会根据平台使用不同的压缩方法,
// _BumpMap.rbg值不是对应的切线空间的xyz值了,要用Unity内置函数
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
// 因为法线都是单位矢量。所以 z = 根号下(1 - x*x + y*y )
tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// 对主纹理采样
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
// 获得环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射系数 ( 0 ≤ Kd ≤ 1);
// N:单位法向量,
// L:单位光向量
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangentNormal, tangentLightDir));
// 获得半角向量
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
// 计算高光反射
// Blinn-Phong高光反射公式:
// Cspecular=(Clight ⋅ Mspecular)max(0,n.h)^mgloss
// Clight:入射光颜色;
// Mspecular:高光反射颜色;
// n: 单位法向量;
// h: 半角向量:光线和视线夹角一半方向上的单位向量
// h = (V + L)/(| V + L |)
// mgloss:反射系数;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1);
}
ENDCG
}
}
}
世界空间计算法线:
Shader "Unlit/013" //凹凸映射 世界空间计算法线
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("MainTex", 2D) = "white" { }
// 法线纹理(凹凸贴图),"bump"是Unity内置的法线纹理,当没有提供法线纹理时,
// "bump"就对应了模型自带的法线信息
_BumpMap ("NormalMap", 2D) = "bump" { }
// 控制凹凸程度,为0时意味着法线纹理不会对光照产生影响
_BumpScale ("BumpScale", float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
// _MainTex纹理的缩放和偏移系数
float4 _MainTex_ST;
sampler2D _BumpMap;
// _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类型,xy存储_MainTex的纹理坐标,zw存储_MainTex的纹理坐标
float4 uv: TEXCOORD0;
float4 TtoW0: TEXCOORD1;
float4 TtoW1: TEXCOORD2;
float4 TtoW2: TEXCOORD3;
};
//1. 获取 材质,法线贴图 的纹理坐标 2.将 顶点坐标,法线,切线,副切线 转世界坐标 3.使用3个float4 传递数据
v2f vert (a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// xy存储_MainTex的纹理坐标
// 等价于o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
// zw存储_MainTex的纹理坐标
// den等价于o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
//将顶点坐标从模型空间变换到世界空间
float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
// 将法线从模型空间变换到世界空间
// 等价于fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
// 将切线从模型空间变换到世界空间
// 等价于fixed3 worldTangent = normalize(mul(v.tangent.xyz, (float3x3)unity_WorldToObject)) ;
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
// 获得世界空间下副切线(副法线):(法向量 x 切线向量) * w
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
// 将切线、副切线、法线按列摆放得到从切线空间到世界空间的变换矩阵
// 把该矩阵的每一行分别存储在TtoW0、TtoW1、TtoW2中
// 把世界空间下的顶点位置的xyz分量分别存储在这些变量的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);
return o;
}
//1. 获取世界空间下 顶点坐标,光向量,观察向量,切线空间法线转世界空间,主纹理
fixed4 frag (v2f i) : SV_Target
{
// 获得世界空间下顶点坐标
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
// 获得世界空间下单位光向量
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
// 获得世界空间下单位观察向量
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// 获得压缩后的法线像素
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
fixed3 bump;
// 若法线纹理Texture Type未设置成Normal map,
// 要从像素映射回法线,即[0, 1]转化到[-1, 1]
// bump.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// 如果设置了Normal map类型,Unity会根据平台使用不同的压缩方法,
// _BumpMap.rbg值不是对应的切线空间的xyz值了,要用Unity内置函数
bump = UnpackNormal(packedNormal);
bump.xy *= _BumpScale;
// 因为法线都是单位矢量。所以 z = 根号下(1 - (x*x + y*y) )
bump.z = sqrt(1 - saturate(dot(bump.xy, bump.xy)));
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
// 对主纹理采样
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
// 获得环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射系数 ( 0 ≤ Kd ≤ 1);
// N:单位法向量,
// L:单位光向量
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(bump, worldLightDir));
// 获得半角向量
fixed3 halfDir = normalize(worldLightDir + worldViewDir);
// 计算高光反射
// Blinn-Phong高光反射公式:
// Cspecular=(Clight ⋅ Mspecular)max(0,n.h)^mgloss
// Clight:入射光颜色;
// Mspecular:高光反射颜色;
// n: 单位法向量;
// h: 半角向量:光线和视线夹角一半方向上的单位向量
// h = (V + L)/(| V + L |)
// mgloss:反射系数;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(bump, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
}
世界空间 切线空间 正常
渐变映射
使用半角向量和贴图模拟纹理渐变
定义:
使用一张渐变图片来控制漫反射的结果
纹理坐标uv都在[0,1]的范围之内,需要将光强映射到[0,1]
在片元函数中:
1、通过半兰伯特公式计算光照强度 。
2、通过光照强度的值对渐变纹理进行采样。
Shader "Unlit/014" //渐变映射
{
Properties
{
_Color ("Color",Color) = (1,1,1,1)
// 渐变纹理
_RampTex ("RampTex", 2D) = "white" { }
// 高光反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1)
// 高光区域大小
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_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;
};
//1. 将 法线,顶点 变换到世界空间 2. 计算纹理坐标
v2f vert (a2v v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_VP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 将法线从模型空间变换到时间空间
// 等价于o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// 计算平铺和平移后的纹理坐标
// 等价于o.uv = v.texcoord.xy * _RampTex_ST.xy + _RampTex_ST.zw;
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
}
//1. 根据半兰伯特关照值 取 渐变纹理的值
fixed4 frag (v2f i) : SV_Target
{
// 获得世界光线向量
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// 获得环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 获得半兰伯特光照光
fixed halfLambert = dot(i.worldNormal, worldLightDir) * 0.5 + 0.5;
// 使用halfLambert构建纹理坐标,获得漫反射颜色
fixed3 diffuseColor = tex2D(_RampTex, float2(halfLambert, halfLambert)).rgb * _Color.rgb;
// 获得漫反射光
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
// 获得世界空间下观察向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
// 获得Blinn-Phong高光反射半角向量
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
}
纹理遮挡
想要某些区域颜色变深,可以使用深颜色遮罩
定义:
使用一张遮罩图 来保护某些区域,免于高光修改
常见应用:
1、更加细腻的控制高光的效果,让某些区域高光更加强烈,让某些区域较弱
2、当材质需要混合多张照片时,使用遮罩纹理可以控制如何混合这些纹理
在凹凸纹理实现的基础上添加遮罩纹理的应用。
在片元函数中,将采样的得到的遮罩纹理的r通道的值与高光相乘。
Shader "Unlit/015" //遮罩纹理
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
// 主纹理
_MainTex ("MainTex", 2D) = "white" { }
// 凹凸纹理(法线纹理)
_BumpMap ("NormalMap", 2D) = "bump" { }
// 凹凸程度
_BumpScale ("BumpScale", float) = 1.0
// 高光反射遮罩纹理
_SpecularMask ("SpecularMask", 2D) = "white" { }
_SpecularScale ("SpecularScale", float) = 1.0
// 高光反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1)
// 高光区域大小
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
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;
};
// 顶点传递给片元着色器的数据
struct v2f
{
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir: TEXCOORD2;
};
//1. 将 法线,顶点 变换到世界空间 2. 计算纹理坐标
v2f vert (a2v v)
{
v2f o;
// 将顶点坐标从模型空间转换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 存储_MainTex的纹理坐标
// 等价于o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
// TANGENT_SPACE_ROTATION(切线空间到模型空间的变换矩阵)等价于:
// 使用模型空间下的法线和切线叉积得到副切线方向
// float3 binormal = cross(normalize(v.normal), normalize(v.tangent)) * normalize(v.tangent.w)
// 定义 3x3 变换矩阵 rotation,分别将切线方向、副切线方向和法线方向按行摆放组成了这个矩阵。
// float3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
TANGENT_SPACE_ROTATION;
// 获得切线空间下光照方向
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
// 获得切线空间下视角方向
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
//1. 根据半兰伯特关照值 取 渐变纹理的值
fixed4 frag (v2f i) : SV_Target
{
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
// 获得压缩后法线
fixed4 packedNormal = tex2D(_BumpMap, i.uv);
fixed3 tangentNormal;
// 若法线纹理Texture Type未设置成Normal map
// 要从像素映射回法线,即[0, 1]转化到[-1, 1]
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// 如果设置了Normal map类型,Unity会根据平台使用不同的压缩方法
// _BumpMap.rbg值不是对应的切线空间的xyz值了,要用Unity内置函数
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
// 因为法线都是单位矢量。所以 z = 根号下(1 - x*x + y*y )
tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// 对主纹理采样
fixed3 albedo = tex2D(_MainTex, i.uv).rgg * _Color.rgb;
// 获得环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射系数 ( 0 ≤ Kd ≤ 1);
// N:单位法向量,
// L:单位光向量
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangentNormal, tangentLightDir));
// 获得半角向量
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
// 对遮罩纹理采样
fixed3 specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
// 计算高光反射
// Blinn-Phong高光反射公式:
// Cspecular=(Clight ⋅ Mspecular)max(0,n.h)^mgloss
// Clight:入射光颜色;
// Mspecular:高光反射颜色;
// n: 单位法向量;
// h: 半角向量:光线和视线夹角一半方向上的单位向量
// h = (V + L)/(| V + L |)
// mgloss:反射系数;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal, halfDir)), _Gloss) * specularMask;
return fixed4(ambient + diffuse + specular, 1);
//return fixed4(specularMask , 1);
}
ENDCG
}
}
}
透明度测试
tag中声明 透明测试Queue , RenderType设置为透明剔除 在片元着色器中进行测试。
当一个片元的透明度不满足条件(通常是小于某个阈值),那么就舍弃它对应的片元。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响。否则就按照普通的不透明物体方式来处理。
渲染队列:决定渲染顺序
ZTest :将对象自身的深度值与当前该像素点缓存的深度值进行比较,如果通过了,本对象在该像素点才会将颜色写入颜色缓冲区,否则否则不会写入颜色缓冲。(深度测试决定是否写入颜色缓冲)
ZWrite :是否要将像素的深度写入到深度缓冲中,前提是通过了深度测试。
Shader "Unlit/016" //透明度测试
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("MainTex", 2D) = "white" { }
// 透明度
_Cutoff ("AlpheCutoff", Range(0, 1)) = 0.5
}
SubShader
{
Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
// 应用传递给顶点着色器的数据
struct a2v
{
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
};
// 顶点着色器传递给片元着色器的数据
struct v2f
{
float4 pos: SV_POSITION;
float3 worldNormal: TEXCOORD0;
float worldPos: TEXCOORD1;
float2 uv: TEXCOORD2;
};
// 顶点着色器
v2f vert(a2v v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 将法线从模型空间变换到时间空间
// 等价于o.worldNormal = normalize(mul(unity_ObjectToWorld, v.normal));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// 获取纹理uv坐标
// 等价于o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
// 片元着色器
fixed4 frag(v2f i): SV_TARGET
{
// 获得世界空间下光照方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
// 透明度测试
clip(texColor.a - _Cutoff);
// 等价于
// if (texColor.a - _Cutoff < 0.0) {
// discard;
// }
// 主纹理颜色
fixed3 albedo = texColor.rgb * _Color.rgb;
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射系数 ( 0 ≤ Kd ≤ 1);
// N:单位法向量,
// L:单位光向量
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(i.worldNormal, worldLightDir));
return fixed4(ambient + diffuse, 1.0);
}
ENDCG
}
}
}
透明度混合
设置Queue 和 RenderType为透明,关闭深度写入,开启混合
透明物体需要深度测试:实现正常的遮挡关系。
透明物体需要关闭深度写入:因为交叉透明的物体,在开启深度写入时,后面部分网格无法通过深度测试,被GPU剔除。
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="Ture" "RenderType"="Transparent" }
Pass
{
Tags{ "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
透明度混合 增加一个Pass开启深度写入实现自身遮挡
Pass
{
ZWrite On //打开深度写入
ColorMask 0 //此 Pass 不写入任何颜色
}
透明度测试 双面渲染 关闭剔除
Cull Off
透明度混合双面渲染
两个pass
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
值得注意的是,在默认情况下,GPU 执行背面剔除,即 Cull Back 。为了得到双面渲染的结果,我们需要把渲染工作分为两个Pass,第一个Pass只渲染背面,第二个只渲染正面。由于Unity会顺序直星SubShader的各个Pass,因此这样先渲染背面再渲染正面,可以保证正确的深度渲染关系。
Shader "Unlit/017_3" //透明度混合 双面渲染
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0,1)) = 1
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="Ture" "RenderType"="Transparent" }
Pass
{
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
fixed3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldVertex : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _AlphaScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldDir(v.normal);
o.worldVertex = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 albedo = tex2D(_MainTex, i.uv);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
fixed3 woldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldVertex));
fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(worldLightDir,woldNormal));
fixed4 col = fixed4( ambient + diffuse, albedo.a * _AlphaScale);
return col;
}
ENDCG
}
Pass
{
Tags{ "LightMode"="ForwardBase" }
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
fixed3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldVertex : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _AlphaScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldDir(v.normal);
o.worldVertex = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 albedo = tex2D(_MainTex, i.uv);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
fixed3 woldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize( UnityWorldSpaceLightDir(i.worldVertex));
fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(worldLightDir,woldNormal));
fixed4 col = fixed4( ambient + diffuse, albedo.a * _AlphaScale);
return col;
}
ENDCG
}
}
}
outLine描边
基于观察角度和表面法线
视角方向和法线点乘,结果与一个值进行比较,满足则输出颜色
Shader "Unlit/Outline" //基于观察角度和表面法线
{
Properties
{
_Outline ("Outline",Range(0,1)) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 ObjViewDir = normalize(ObjSpaceViewDir(v.vertex));
float3 normal = normalize(v.normal);
float factor = step(_Outline,dot(normal,ObjViewDir));
o.color = float4(1,1,1,1) * factor;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
}
}
step (a, x) { if (x < a) { return 0; } else { return 1; } }
物体空间外拓 视角空间法线外拓 裁剪空间法线外拓
物体空间直接算
视角空间法线外拓:将顶点unity_ObjectToWorld 模型空间 -> 到世界空间,UNITY_MATRIX_V 世界空间 -> 转换到视角空间 。将法线UNITY_MATRIX_IT_MV 模型空间 -> 视角空间。
裁剪空间法线外拓:将法线 TransformViewToProjection(normal.xy)模型空间->裁剪空间
//物体空间法线外拓
//v.vertex.xyz += v.normal * _Outline;
//o.vertex = UnityObjectToClipPos(v.vertex);
//视角空间法线外拓 UNITY_MATRIX_V 世界空间 -> 转换到视角空间 右乘则相反
// unity_ObjectToWorld 模型空间 -> 到世界空间 右乘则相反
//float4 pos = mul(UNITY_MATRIX_V,mul(unity_ObjectToWorld,v.vertex));
// UNITY_MATRIX_IT_MV 模型空间 -> 视角空间
//float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
//pos = pos + float4(normal,0) * _Outline;
//o.vertex = mul(UNITY_MATRIX_P, pos);
//裁剪空间法线外拓
o.vertex = UnityObjectToClipPos(v.vertex);
//将法线从模型空间转换到视角空间
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
//将法线从视角空间转换到剪裁空间
//转换到剪裁空间的函数只需要法线的xy值
//z值则表示深度信息,由于这里外拓计算不需要法线在剪裁空间下的深度
float2 viewNoraml = TransformViewToProjection(normal.xy);
o.vertex.xy += viewNoraml * _Outline;
通过计算分色阶 通过渐进纹理颜色值分色阶 边缘光 XRay
计算分色阶:1.半兰伯特计算漫反射 2.将颜色平滑在[0,1] 3.floor(difLight * _Steps) / _Steps;分色阶
纹理分色阶:fixed4 rampColor = tex2D(_RampTex, fixed2(difLight, difLight));
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * rampColor;
边缘光:float rim = 1 - dot(i.worldNormal, viewDir);
fixed3 rimColor = _RimColor * pow(rim, 1/_RimPower);
Shader "Unlit/Outline1" //物体空间外拓 视角空间法线外拓 裁剪空间法线外拓
{
Properties
{
//主纹理贴图
_MainTex ("Texture", 2D) = "white" {}
//漫反射主体颜色
_Diffuse("Color", Color) = (1,1,1,1)
//外描边区域
_Outline("Outline", Range(0,0.2)) = 0.1
//外描边颜色
_OutlineColor("OutlineColor", Color) =(0,0,0,0)
//渐进效果区分色段数量
_Steps("Steps",Range(1,30)) = 1
//渐进效果明显程度
_ToonEffect("ToonEffect", Range(0,1)) = 0.5
//渐进纹理
_RampTex("RampTex", 2D) ="white" {}
//边缘光颜色
_RimColor("RimColor", Color) =(1,1,1,1)
//边缘光范围
_RimPower("RimPower", Range(0.0001,3)) = 1
//遮挡Xray效果颜色
_XRayColor("XRayColor", Color) =(1,1,1,1)
//遮挡Xray效果强度
_XRayPower("XRayPower", Range(0.0001,3)) = 1
}
SubShader
{
//Queue是Geometry+1000 在一般情况下其在普通不透明物体渲染后才进行渲染
//保证它能检测到所有遮挡它的物体 如果小于Geometry 则其渲染的时候
//不透明物体还没开始渲染 其不知道哪些物体遮挡了他
Tags {"Queue"= "Geometry+1000" "RenderType"="Opaque" }
LOD 100
//这个pass是用来在被遮挡时候显示XRay效果的
Pass
{
//指定这个pass的名称
Name "XRay"
Tags{ "ForceNoShadowCasting" = "true" }
//指定shader的混合方式
//将这个shader渲染的片元的颜色与这个shader渲染的片元的透明度相乘
//然后结果与本身在颜色缓冲中的颜色相加后进行渲染
//所以透明度会影响Xray的明显范围
//合并混合是在最后阶段了
Blend SrcAlpha One
//关闭深度写入
//如果开启了深度写入,由于渲染队列是Geometry+1000
//其在遮挡物之后进行渲染,渲染之后被遮挡物的深度缓存会变成
//这个shader的深度,因为默认的ZTest 是 LEqual 对于下面的几个pass
//其深度测试结果会通过,那么显然即使被遮挡住了
//下面的几个pass因为这里的ZWrite开启了还是会进行的
ZWrite Off
//在当前片元所在像素位置进行深度测试
//并且测试大于当前屏幕深度缓冲值的时候 使用这个Shader,
//深度测试大于说明这个片元前面有更靠近摄像机的片元遮挡住了该片元
ZTest Greater
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _XRayColor;
float _XRayPower;
struct v2f
{
float4 vertex : SV_POSITION;
float3 viewDir : TEXCOORD0;
float3 normal : TEXCOORD1;
};
//appdata_base是UnityCG.cginc库里面的结构体
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.viewDir = ObjSpaceViewDir(v.vertex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
float3 normal = normalize(i.normal);
float3 viewDir = normalize(i.viewDir);
//边缘光的原理是这样的
//在物体的绘制区域中,越靠近区域边缘的地方
//边缘光越强,从边缘到内部区域就逐渐减弱
//越靠近绘制区域边缘的地方的片元的法线就越与
//摄像机观察向量垂直,
//将这种强弱关系与垂直关系以及cos结合起来思考就会发现
//cos值越小的片元就是越靠近绘制区域边缘的,其边缘光应该越强
//所以下面的rim变量使用了一减这个操作来表示边缘光的基本强度
float rim = 1 - max (0, dot(normal, viewDir));
//因为rim是个大于0小于1的小数,其指数越大反而越小,
//并且在指数大于0小于1的时候 指数越小变化越明显
//所以我们使用的是 1 / XRayPower 作为指数
//可以使用函数图像绘制工具 查看 pow(0.1, 1/x)
//这样的一个函数的绘制情况即可明白
//下面公式随着_XRayPower增大 其边缘光放大效果越明显
return _XRayColor * pow(rim, 1 / _XRayPower);
//return _XRayColor * rim * _XRayPower;
}
ENDCG
}
//这个pass负责正常的漫反射 纹理读取以及 渐变和边缘光
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
fixed3 worldNormal:TEXCOORD1;
float3 worldPos: TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffuse;
float _Steps;
float _ToonEffect;
sampler2D _RampTex;
fixed4 _RimColor;
float _RimPower;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//UNITY_LIGHTMODEL_AMBIENT在Lighting.cginc里面 所以要include一下
//UNITY_LIGHTMODEL_AMBIENT表示环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//纹理采样 读取主纹理上i.uv坐标下的颜色
fixed4 albedo = tex2D(_MainTex, i.uv);
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//卡通颜色部分
//半兰伯特漫反射的初步计算值
float difLight = dot(worldLightDir, i.worldNormal) * 0.5 + 0.5;
///通过计算分色阶部分//
//颜色平滑在[0,1]之间
//smoothstep(min,max,x):返回值x位于min、max区间中。如果x=min,返回0;
//如果x=max,返回1;如果x在两者之间
//返回的值为–2*(( x – min )/( max – min ))3 +3*(( x – min )/( max – min ))2
//difLight = smoothstep(0, 1, difLight);
//颜色分阶部分
//将初步计算值靠拢到其最接近的向下取整的步值
//即0到1的部分分成_Steps段,toon是difLight值所落在段的开始值
//例如步值是5 difLight是0.75 则toon为0.6
//float toon = floor(difLight * _Steps) / _Steps;
//将半兰伯特初始值与分阶颜色之间进行插值
//若_ToonEffect等于0 则difLight相当于最开始的初步值
//若为1 则完全是卡通颜色值
//_ToonEffect实际上控制的半兰伯特初步值向卡通颜色的靠拢情况
//越靠拢则边界越明显
//difLight = lerp(difLight, toon, _ToonEffect);
//fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * difLight;
//
///通过渐进纹理颜色值分色阶部分//
//通过fixed2(difLight, difLight)作为uv坐标对渐进纹理进行采用
//在上述的渐进纹理贴图的特点中,决定了fixed2(difLight, difLight)这个值
//只由u这个坐标对渐进颜色有影响
//根据半兰伯特漫反射的特性可知
//越靠近光源的顶点其difLight值越大,越远离光源的顶点其值越小
//所以越靠近光源的顶点读取的是渐进纹理贴图的浅色部分
//远离光源的顶点读取的是渐进纹理贴图的深色部分
fixed4 rampColor = tex2D(_RampTex, fixed2(difLight, difLight));
fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * rampColor;
//
//这里原理与Xray的一样
float rim = 1 - dot(i.worldNormal, viewDir);
fixed3 rimColor = _RimColor * pow(rim, 1/_RimPower);
//颜色的叠加用加法处理
//颜色的融合用乘法处理
return float4( ambient + diffuse + rimColor,1);
}
ENDCG
}
//这个pass是显示外描边效果的
Pass
{
Name "Outline"
//剔除正面 渲染背面
//一般背面的深度值比正面的要大,
//即同一个shader里面如果同时开启了正面
//和背面的渲染,一般先渲染背面再渲染正面,
//重合的部分正面部分会挡住背面部分
//正面一般指模型外部的面,背面一般指模型内部的面
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct v2f
{
float4 vertex :SV_POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
//模型空间法线外拓
//将顶点位置往法线方向拓展_Outline表示的程度
//因为appdata_base结构体里面的normal和vertex都是法线方向的
//所以可以直接计算
//v.vertex.xyz += v.normal * _Outline;
//外拓后的位置转换到剪裁空间坐标
//o.vertex = UnityObjectToClipPos(v.vertex);
//视角空间法线外拓
//将顶点位置先从模型空间转换到世界空间
//再从世界空间转换到视角空间
//UNITY_MATRIX_V 左乘一个向量一般表示将其从世界空间转换到视角空间 右乘则相反
//需要注意向量是模型空间下的,其他空间下的话转换不会显示出理想结果
//unity_ObjectToWorld 左乘一个向量一般表示将其从模型空间转换到世界空间 右乘则相反
//需要注意向量是世界空间下的,其他空间下的话转换不会显示出理想结果
//float4 pos = mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, v.vertex));
//UNITY_MATRIX_IT_MV 左乘一个向量一般表示将其从模型空间转换到视角空间
//因为normal是三维变量,所以UNITY_MATRIX_IT_MV需要转换成三乘三的矩阵
//与上面的UNITY_MATRIX_V一起推测起来 如果UNITY_MATRIX_M存在的话
//则其意义与unity_ObjectToWorld是一样的
//float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
//视角空间下 将顶点位置往法线方向拓展_Outline表示的程度
//三维向量与四维向量计算一般会将三维向量末尾添加一位组合成四维向量后再计算
//两者都是在视角空间所以可以直接计算
//pos = pos + float4(normal,0) * _Outline;
//将计算后的位置转换到剪裁空间
//o.vertex = mul(UNITY_MATRIX_P, pos);
//裁剪空间法线外拓
o.vertex = UnityObjectToClipPos(v.vertex);
//将法线从模型空间转换到视角空间
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
//将法线从视角空间转换到剪裁空间
//转换到剪裁空间的函数只需要法线的xy值
//z值则表示深度信息,由于这里外拓计算不需要法线在剪裁空间下的深度
//所以只传了xy 这里xy表示的是屏幕上的横竖方向上的方向
float2 viewNoraml = TransformViewToProjection(normal.xy);
//裁剪空间法线外拓
//这里xy表示的是屏幕上的横竖方向上的vertex顶点坐标
o.vertex.xy += viewNoraml * _Outline;
return o;
}
float4 frag(v2f i):SV_Target
{
//这里只需返回颜色即可,外拓操作在顶点函数已经做好
//渲染引擎会根据做好的外拓操作用返回的颜色进行绘制
return _OutlineColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
卡通着色 积雪
Shader "Unlit/020" //卡通着色 积雪
{
Properties
{
_MainTex ("Texture",2D) = "white" {}
_Color ("Color",Color) = (1,1,1,1)
_Outline ("Outline",Range(0,0.2)) = 0.1
_OutlineColor ("OutlineColor",Color) = (0,0,0,0)
// "bump"就对应了模型自带的法线信息
_BumpMap ("NormalMap", 2D) = "bump" { }
// 控制凹凸程度,为0时意味着法线纹理不会对光照产生影响
_BumpScale ("BumpScale", float) = 1.0
//渐进效果区分色段数量
_Steps("Steps",Range(1,30)) = 1
//渐进效果明显程度
_ToonEffect("ToonEffect", Range(0,1)) = 0.5
_Snow("Snow Level",Range(0,1)) = 0.5
_SnowColor("SnowColor",Color) = (1,1,1,1)
_SnowDir("SnowDir",Vector) = (0,1,0)
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="Ture" "RenderType"="Transparent" }
UsePass "Unlit/Outline1/Outline"
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
float _Steps;
float _ToonEffect;
float _Snow;
fixed4 _SnowColor;
float4 _SnowDir;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 uv : TEXCOORD0;
float4 tangent: TANGENT;
};
struct v2f
{
float4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldPos : TEXCOORD2;
float4 TtoW0 : TEXCOORD3;
float4 TtoW1 : TEXCOORD4;
float4 TtoW2 : TEXCOORD5;
};
v2f vert(appdata v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv,_MainTex);
o.uv.zw = TRANSFORM_TEX(v.uv,_BumpMap);
// 将法线从模型空间变换到世界空间 等价于o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
// 将顶点坐标从模型空间变换到世界空间
float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
o.worldPos = worldPos;
//将切线从模型空间变换到世界空间
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
//获得世界空间下副切线(副法线):(法向量 x 切线向量) * w
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);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// 对主纹理进行采样
fixed3 albedo = tex2D(_MainTex,i.uv.xy).rgb;
// 获得压缩后的法线像素
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
fixed3 bump;
bump = UnpackNormal(packedNormal);
bump.xy *= _BumpScale;
// 因为法线都是单位矢量。所以 z = 根号下(1 - (x*x + y*y) )
bump.z = sqrt(1 - saturate(dot(bump.xy, bump.xy)));
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
// 获得单位世界光向量
// 在 "LightMode" = "ForwardBase"情况下:
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 worldNormal = normalize(i.worldNormal);
// 获得单位观察向量
// 等价于fixed3 viewDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos);
float3 worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
//通过计算分色阶
float difLight = dot(worldLightDir, bump) * 0.5 + 0.5;
difLight = smoothstep(0, 1, difLight);
float toon = floor(difLight * _Steps) / _Steps;
difLight = lerp(difLight, toon, _ToonEffect);
fixed3 diffuse = _LightColor0.rgb * albedo * _Color.rgb * difLight;
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color;
fixed4 color = fixed4(ambient + diffuse,1);
if(dot(bump,_SnowDir.xyz) > _Snow)
{
color.rgb = _SnowColor.rgb;
}
else
{
color.rgb = color.rgb;
}
return fixed4(color.rgb,1);
}
ENDCG
}
}
}
常用语句
混合语句
Blend SrcFactor DstFactor
常见的混合操作句法示例
Blend SrcAlpha OneMinusSrcAlpha
Alpha混合
Blend One One
相加
Blend One OneMinusDstColor
比较柔和的相加
Blend DstColor Zero
乘法
Blend DstColor SrcColor
2倍乘法
内置函数
pow(x, y) = xy 。在x1/y 中 y越大值越小
step(a, x) 如果x<a, 返回0;否则返回1
Unity渲染路径
前向渲染
描述:每进行一次前向渲染,需要利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲中的颜色值。针对每个物体,对于每个光源都会分别进行一次光照计算,最后的颜色值是由所有光源的光照结果混合而成的,比如场景中有M个物体,N个光源,则渲染整个场景需要N × M个Pass,可以看到如果光源数目多,前向渲染的开销是非常巨大的
前向渲染的Pass被分为两类:Base Pass 和 Additional Pass
Base Pass只会执行一次(如果定义了多个Bass Pass,也可能执行多次,环境光和自发光的是在Base Pass中实现的),Additional Pass根据光源个数多次执行,Additional Pass需要对其余所有的逐像素光源进行处理,即每个逐像素光源调用一次Additional Pass。
Additional Pass需要开启混合模式,因为每个逐顶点光源是依次计算的,我们不能直接覆盖上一次的计算颜色,而需要进行混合
#ifdef USING_DIRECTIONAL_LIGHT,它可以判断当前是否正在处理平行光
光照衰减
Tags { "LightMode" = "ForwardAdd" }
// 开启混合,
Blend One One
LIGHTING_COORDS(2,3)
TRANSFER_VERTEX_TO_FRAGMENT(o)
LIGHT_ATTENUATION(i)
Shader "Unlit/ForwardRendering" //前向渲染 复杂光照 关照衰减
{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1) // 漫反射颜色
_Specular ("Specular", Color) = (1, 1, 1, 1) // 高光反射颜色
_Gloss ("Gloss", Range(8, 256)) = 20 // 高光区域大小
}
SubShader
{
Tags { "RenderType" = "Opaque" }
// Base Pass 计算平行光、环境光
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
// 编译指令,保证在pass中得到Pass中得到正确的光照变量
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
// 应用传递给顶点着色器的数据
struct a2v
{
float4 vertex: POSITION; // 语义: 顶点坐标
float3 normal: NORMAL; // 语义: 法线
};
// 顶点着色器传递给片元着色器的数据
struct v2f
{
float4 pos: SV_POSITION; // 语义: 裁剪空间的顶点坐标
float3 worldNormal: TEXCOORD;
float3 worldPos: TEXCOORD1;
};
// 顶点着色器
v2f vert(a2v v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 将法线从模型空间变换到世界空间
// 等价于o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
// 片元着色器
fixed4 frag(v2f i): SV_TARGET
{
fixed3 worldNormal = normalize(i.worldNormal);
// 获得世界空间下单位光向量 (是ForwardBase的pass,光一定是平行光)
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射颜色;
// N:单位法向量,L:单位光向量
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
// 观察方向
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// 半角向量
float3 halfDir = normalize(worldLightDir + viewDir);
// 计算高光反射
// Blinn-Phong高光反射公式:
// Cspecular=(Clight * Mspecular) * max(0,n.h)^mgloss
// Clight:入射光颜色;
// Mspecular:高光反射颜色;
// n: 单位法向量;
// h: 半角向量:光线和视线夹角一半方向上的单位向量 h = (V + L)/(| V + L |)
// mgloss:反射系数;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
return fixed4(ambient + diffuse + specular, 1);
}
ENDCG
}
// Add pass 计算额外的逐像素光源(点光源、聚光灯等), 每个pass对应1个光源
pass
{
Tags { "LightMode" = "ForwardAdd" }
// 开启混合,
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
// 应用传递给顶点着色器的数据
struct a2v
{
float4 vertex: POSITION;
float3 normal: NORMAL;
};
// 顶点着色器传递给片元着色器的数据
struct v2f
{
float4 pos: SV_POSITION;
float3 worldNormal: TEXCOORD0;
float3 worldPos: TEXCOORD1;
LIGHTING_COORDS(2,3)//
};
// 顶点着色器
v2f vert(a2v v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 将法线从模型空间变换到世界空间
// 等价于o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_VERTEX_TO_FRAGMENT(o);//
return o;
}
// 片元着色器
fixed4 frag(v2f i): SV_TARGET
{
fixed3 worldNormal = normalize(i.worldNormal);
// 世界空间光向量一般直接用Unity内置函数计算
// 即:fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos))
#ifdef USING_DIRECTIONAL_LIGHT
// 平行光
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
// 非平行光
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
// 计算漫反射颜色
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射颜色;
// N:单位法向量,L:单位光向量
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
// 观察向量
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// 半角向量
fixed3 halfDir = normalize(worldLightDir + viewDir);
// 计算高光反射
// Blinn-Phong高光反射公式:
// Cspecular=(Clight * Mspecular) * max(0,n.h)^mgloss
// Clight:入射光颜色;
// Mspecular:高光反射颜色;
// n: 单位法向量;
// h: 半角向量:光线和视线夹角一半方向上的单位向量 h = (V + L)/(| V + L |)
// mgloss:反射系数;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
//#ifdef USING_DIRECTIONAL_LIGHT // 平行光
// 平行光,光照衰减不变
//fixed atten = 1.0;
//#else
//#if defined(POINT) // 点光源
// 把顶点坐标从世界空间变换到点光源坐标空间中
// unity_WorldToLight由引擎代码计算后传递到shader中,这里包含了对点光源范围的计算,具体可参考Unity引擎源码。
// 经过unity_WorldToLight变换后,在点光源中心处lightCoord为(0, 0, 0),在点光源的范围边缘处lightCoord模为1。
//float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
// 使用点到光源中心距离的平方dot(lightCoord, lightCoord)构成二维采样坐标(r,r),对衰减纹理_LightTexture0采样。
// UNITY_ATTEN_CHANNEL是衰减值所在的纹理通道,可以在内置的HLSLSupport.cginc文件中查看。
// 一般PC和主机平台的话UNITY_ATTEN_CHANNEL是r通道,移动平台的话是a通道。
//fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
//#elif defined(SPOT) // 聚光灯
//float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
// 与点光源不同,由于聚光灯有更多的角度等要求,因此为了得到衰减值,除了需要对衰减纹理采样外,还需要对聚光灯的范围、张角和方向进行判断。
// 此时衰减纹理存储到了_LightTextureB0中,这张纹理和点光源中的_LightTexture0是等价的。
// 聚光灯的_LightTexture0存储的不再是基于距离的衰减纹理,而是一张基于张角范围的衰减纹理。在张角中心,即坐标0.5处衰减值为1,而在两侧是接近0的。
//fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
//#else
//fixed atten = 1.0;
//#endif
//#endif
fixed atten = LIGHT_ATTENUATION(i);//
return fixed4((diffuse + specular)*atten, 1.0);
}
ENDCG
}
}
}
Unity光照-阴影
原理:将摄像机放到光源位置,场景中摄像机看不到区域就为阴影区域。
如果场景中最重要的平行光开启了阴影,Unity 就会为该光源计算它的阴影映射纹理
Unity使用一个额外的Pass来专门更新光源的阴影映射纹理
接收阴影 投射阴影
物体接收来自其他物体的阴影: 就必须在Shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,把采样结果和最后的光照结果相乘来产生阴影效果。
物体向其他物体投射阴影: 必须把该物体加入到光源的阴影映射纹理的计算中
Shader中计算阴影步骤
1、在顶点着色器输出体中,声明一个宏SHADOW_COORDS用于对阴影纹理采样的坐标;
2、在顶点着色器中,使用宏TRANSFER_SHADOW计算声明的阴影纹理坐标;
3、在片元着色器中,使用宏SHADOW_ATTENUATION计算阴影值。
// Pass1
// Shadow Pass 将该物体加入到光源的阴影映射纹理的计算中。通常此Pass不需要写,
// 由于这个Pass功能通常是在多个Shader中通用,因此一般直接通过FallBack一个Unity内置Specular来完成,
// 即: FallBack "Specular",因为Specular会继续FallBack回调内置的VertexLit
pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
float4 frag(v2f i): SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
透明度测试阴影
FallBack设置为"Transparent/Cutout/VertexLit"。
由于Transparent/Cutout/VertexLit中计算透明度测试时,使用了名为_Cutoff的属性,因此我们的 Shader 中也必须提供名为_Cutoff的属性,否则无法得到正确的阴影结果。
Shader "Unlit/AlphaTestWithShadow" //透明度测试 阴影
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("MainTex", 2D) = "white" { }
// 透明度测试阈值
_Cutoff ("AlpheCutoff", Range(0, 1)) = 0.5
}
SubShader
{
Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
// 应用传递给顶点着色器的数据
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;
SHADOW_COORDS(3) // 声明一个用于对阴影纹理采样的坐标 (这个宏参数需要是下一个可用的插值寄存器的索引值,这里是3)
};
// 顶点着色器
v2f vert(a2v v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
// 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
// 将法线从模型空间变换到时间空间
// 等价于o.worldNormal = normalize(mul(unity_ObjectToWorld, v.normal));
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// 将顶点坐标从模型空间变换到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// 获取纹理uv坐标
// 等价于o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
// 计算声明的阴影纹理坐标
TRANSFER_SHADOW(o);
return o;
}
// 片元着色器
fixed4 frag(v2f i): SV_TARGET
{
// 获得世界空间下光照方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
// 透明度测试
clip(texColor.a - _Cutoff);
// 等价于
// if (texColor.a - _Cutoff < 0.0) {
// discard;
// }
// 主纹理颜色
fixed3 albedo = texColor.rgb * _Color.rgb;
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 计算漫反射
// 兰伯特公式:Id = Ip * Kd * N * L
// IP:入射光的光颜色;
// Kd:漫反射系数 ( 0 ≤ Kd ≤ 1);
// N:单位法向量,
// L:单位光向量
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(i.worldNormal, worldLightDir));
// 计算阴影和光照衰减
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + diffuse * atten, 1.0);
}
ENDCG
}
}
FallBack "Transparent/Cutout/VertexLit"
}
透明度混合阴影
由于透明度混合需要关闭深度写入,由此带来的问题也影响了阴影的生成。总体来说,要想为这些半透明物体产生正确的阴影,需要在每个光源空间下仍然严格按照从后往前的顺序进行渲染,这会让阴影处理变得非常复杂,而且也会影响性能。因此,在Unity中,所有内置的半透明Shader 是不会产生任何阴影效果的。
可以强制为半透明物体生成阴影 FallBack “VertexLit”
延迟渲染
在第一个Pass中,不进行任何光照计算,而是仅仅计算哪些片元可见,这主要通过深度缓冲技术实现,当发现一个片元可见,就将其相关信息存储到G缓冲区中。
第二个Pass中,利用G缓冲的各个片元信息,如表面法线、视角方向、漫反射系数等,进行真正的光照计算。
模板缓冲
例: 模板缓冲通过,输出颜色并更改缓冲值为1
ColorMask 0 //不输出颜色
ZWrite Off //深度写入关闭,防止后面物体不渲染
Stencil{
Ref 1
Comp Always
Pass Replace
}
Stencil //模板缓冲值等于1则输出颜色
{
Ref 1
Comp Equal
}
Ref 模板参考值,与当前模板缓冲值比较
Comp 参考值和缓冲值做比较
Pass 模板测试成功操作
Surface Shader结构
定义:自动生成了以前必须手写的代码,Surface Shader实际上就是对顶点和像素着色器的一种包装,它让我们不用去关注更多的顶点和片段程序的细节,能够快速地得到想要的着色器。
#pragma surface surf Standard fullforwardshadows
//“surface”关键词表示该shader以Surface Shader格式来编写
//surf surface的函数
//光照模型
//阴影
Input结构体
SurfaceOutput结构体
struct SurfaceOutput {
half3 Albedo; //像素的颜色
half3 Normal; //像素的法向值
half3 Emission; //像素的发散颜色
half Specular; //像素的镜面高光
half Gloss; //像素的发光强度
half Alpha; //像素的透明度
};
SurfaceShader
法线贴图纹理采样
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
像素颜色:o.Albedo = c.rgb;
Shader "Custom/Surface02" //法线贴图纹理采样 最终颜色
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_BumpMap("Bumpmap", 2D) = "bump"{}
_BunmScale("BunmScale",float) = 1
_ColorTint("Tint", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows finalcolor:final
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
float _BunmScale;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _ColorTint;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
normal.xy *= _BunmScale;
o.Normal = normal;
}
void final(Input IN, SurfaceOutputStandard o, inout fixed4 color)
{
color *= _ColorTint;
}
ENDCG
}
FallBack "Diffuse"
}

边缘光
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
自发光:o.Emission = _RimColor.rgb * pow(rim, _RimPower);
Shader "Custom/Surface03" //边缘光
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_BumpMap("Bumpmap", 2D) = "bump"{}
_BunmScale("BunmScale",float) = 1
_ColorTint("Tint", Color) = (1,1,1,1)
_RimColor("RimColor", Color) = (1,0,0,1)
_RimPower("RimPower", Range(0.1,8.0)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows finalcolor:final
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
float _BunmScale;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _ColorTint;
fixed4 _RimColor;
float _RimPower;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
normal.xy *= _BunmScale;
o.Normal = normal;
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
}
void final(Input IN, SurfaceOutputStandard o, inout fixed4 color)
{
color *= _ColorTint;
}
ENDCG
}
FallBack "Diffuse"
}
卡通分色阶 描边 XRay
自定义光照中分色阶 自发光中实现边缘光 两个pass分别实现描边 xRay
Shader "Custom/Surface04" //卡通渲染 描边 XRay
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,150)) = 120
_Metallic ("Metallic", Range(0,1)) = 0.0
_BumpMap("Bumpmap", 2D) = "bump"{}
_BunmScale("BunmScale",float) = 1
_ColorTint("Tint", Color) = (1,1,1,1)
_RimColor("RimColor", Color) = (1,0,0,1)
_RimPower("RimPower", Range(0.1,8.0)) = 1
_Steps("Steps", Range(1,30)) = 1
_ToonEffect("ToonEffect", Range(0,1)) = 0.5
_Outline("Outline", Range(0,1)) = 0.5
_OutlineColor("OutlineColor", Color)= (0,0,0,0)
//Xray
_XRayColor("XRayColor", Color) =(1,1,1,1)
_XRayPower("XRayPower", Range(0.0001,3)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
Pass
{
Blend SrcAlpha One
ZWrite Off
ZTest Greater
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _XRayColor;
float _XRayPower;
struct v2f
{
float4 vertex: SV_POSITION;
float3 viewDir :TEXCOORD0;
float3 normal :TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.viewDir = ObjSpaceViewDir(v.vertex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
float rim = 1 - dot(normalize(i.normal),normalize( i.viewDir));
return _XRayColor * pow(rim,1/_XRayPower);
}
ENDCG
}
Pass
{
Name "Outline"
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float2 viewNormal = TransformViewToProjection(normal.xy);
o.vertex.xy += viewNormal * _Outline;
return o;
}
float4 frag(v2f i) : SV_Target
{
return _OutlineColor;
}
ENDCG
}
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Toon fullforwardshadows nolightmap finalcolor:final
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
float _BunmScale;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _ColorTint;
fixed4 _RimColor;
float _RimPower;
float _Steps;
float _ToonEffect;
half4 LightingToon(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
{
float difLight = dot(lightDir, s.Normal) * 0.5 + 0.5;
difLight = smoothstep(0,1,difLight);
float toon = floor(difLight * _Steps)/ _Steps;
difLight = lerp(difLight, toon, _ToonEffect);
fixed3 diffuse = _LightColor0 * s.Albedo * difLight;
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Color.rgb * pow(max(0,dot(s.Normal, halfDir)), s.Gloss );
return half4(diffuse + specular,1);
}
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Specular = _Metallic;
o.Gloss = _Glossiness;
o.Alpha = c.a;
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
normal.xy *= _BunmScale;
o.Normal = normal;
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
}
void final(Input IN, SurfaceOutput o, inout fixed4 color)
{
color *= _ColorTint;
}
ENDCG
}
FallBack "Diffuse"
}
流动效果
对火焰纹理进行采样,采样火焰纹理的uv随时间移动
float2 uv = IN.uv_MainTex + _Time.x * _FireSpeed;
o.Emission = ((tex2D(_Mask, IN.uv_MainTex) * tex2D(_Fire, uv)) * (_FireIntensity * (_SinTime.w + 2.5))).rgb;
Shader "Custom/Surface05" //流动效果
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Smoothness ("Smoothness", Range(0,1)) = 0.5
_Normal("Normal", 2D) = "bump" {}
_Mask("Mask",2D) = "white"{}
_Specular("Specular", 2D) = "white" {}
_Fire("Fire",2D) = "white" {}
_FireIntensity("FireIntensity", Range(0,2)) = 1
_FireSpeed("FireSpeed", Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue" = "Geometry"}
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf StandardSpecular fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _Normal;
sampler2D _Mask;
sampler2D _Specular;
sampler2D _Fire;
struct Input
{
float2 uv_MainTex;
};
half _Smoothness;
half _FireIntensity;
half2 _FireSpeed;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandardSpecular o)
{
// Albedo comes from a texture tinted by color
o.Albedo = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Normal = UnpackNormal(tex2D(_Normal,IN.uv_MainTex));
float2 uv = IN.uv_MainTex + _Time.x * _FireSpeed;
o.Emission = ((tex2D(_Mask, IN.uv_MainTex) * tex2D(_Fire, uv)) * (_FireIntensity * (_SinTime.w + 2.5))).rgb;
// Metallic and smoothness come from slider variables
o.Specular = tex2D(_Specular ,IN.uv_MainTex ).rgb;
o.Smoothness = _Smoothness;
o.Alpha = 1;
}
ENDCG
}
FallBack "Diffuse"
}
基于法线UV扭曲 锡箔纸
Shader "Custom/Surface06" //基于法线UV扭曲 锡箔纸
{
Properties
{
[HDR]_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_DistortTexture("Distort Texture", 2D) = "bump"{}
_Speed ("_Speed", float) = 0
_UVDisIntensity("UVDisIntensity", Range(0,1)) = 0
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _DistortTexture;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
half _Speed;
half _UVDisIntensity;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
float2 uv1 = IN.uv_MainTex + _Time.y * _Speed * float2(-1,-1); //声明两个UV坐标
float2 uv2 = IN.uv_MainTex + _Time.y * _Speed * float2(1,1);
float3 Distort = UnpackScaleNormal(tex2D(_DistortTexture,IN.uv_MainTex),_UVDisIntensity);
float4 mainTex1 = tex2D(_MainTex, (float3(uv1,0) + Distort).xy); //基于法线改变UV 再纹理取样
float4 mainTex2 = tex2D(_MainTex, (float3(uv2,0) + Distort).xy);
float4 color = _Color * mainTex1 * mainTex2;
// Albedo comes from a texture tinted by color
//fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = color;
o.Emission = color;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = 1;
}
ENDCG
}
FallBack "Diffuse"
}
法线外拓
定义顶点:vertex:vertFun
在顶点中设置v.vertex.xyz += normal * max(sin((vertexPos.y + _Time.x) * _ExtrusionFrency) / _ExtrusionSwing ,0)
Shader "Custom/Surface08" //法线外拓
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_ExtrusionFrency("Frency" , float) = 0 //变化速度
_ExtrusionSwing("Swing", Range(0,50)) = 1 //变化程度
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue" = "Geometry"}
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows vertex:vertFun
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
half _ExtrusionFrency;
half _ExtrusionSwing;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
void vertFun (inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o); //初始化
float3 normal = v.normal.xyz;
float3 vertexPos = v.vertex.xyz;
v.vertex.xyz += normal * max(sin((vertexPos.y + _Time.x) * _ExtrusionFrency) / _ExtrusionSwing ,0);
}
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
简单模糊 均匀采样
对主纹理进行多次采样 然后融合
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
fixed4 offset1 = tex2D (_MainTex, IN.uv_MainTex + float2(0, _BlurSize));
fixed4 offset2 = tex2D (_MainTex, IN.uv_MainTex + float2(_BlurSize, 0));
fixed4 offset3 = tex2D (_MainTex, IN.uv_MainTex + float2(_BlurSize, _BlurSize));
fixed4 offsetColor = c*0.4 + offset1*0.2 + offset2*0.2 + offset3*0.2; //当前片元纹理采样偏移结果
fixed4 color = lerp(c,offsetColor,step(0.5,_ToggleBulr));
o.Albedo = color.rgb;
Shader "Custom/Surface09" //简单模糊 均值采样
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
[Toggle]_ToggleBulr("ToggleBulr", Range(0,1)) = 0 //是否模糊
_BlurSize("Bulr Size", Range(0,0.1)) = 0 //偏移程度
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
half _ToggleBulr;
half _BlurSize;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
fixed4 offset1 = tex2D (_MainTex, IN.uv_MainTex + float2(0, _BlurSize));
fixed4 offset2 = tex2D (_MainTex, IN.uv_MainTex + float2(_BlurSize, 0));
fixed4 offset3 = tex2D (_MainTex, IN.uv_MainTex + float2(_BlurSize, _BlurSize));
fixed4 offsetColor = c*0.4 + offset1*0.2 + offset2*0.2 + offset3*0.2; //当前片元纹理采样偏移结果
fixed4 color = lerp(c,offsetColor,step(0.5,_ToggleBulr));
o.Albedo = color.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
消融
裁剪
float cutout = tex2D(_DisolveTex, IN.uv_DisolveTex).r;
clip(cutout - _Threshold);
使用差值运算设置边缘颜色
float temp = saturate((cutout - _Threshold) / _EdgeLength); // temp每个片元值不同
fixed4 edgeColor = tex2D(_BurnTex,float2(temp,temp)); //采样边缘颜色
fixed4 finalColor = _BurnInstensity * lerp(edgeColor, fixed4(0,0,0,0), temp); //不同片元,根据temp取颜色
自发光o.Emission = finalColor.rgb;
Shader "Custom/Surface10" //消融
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Normal("Normal", 2D) = "bump" {}
_NormalScale("NormalScale", Range(0,5)) = 1
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_DisolveTex("DisolveTex",2D) = "white"{}
_Threshold("Threshold",Range(0,1)) = 0 //用于透明测试
_EdgeLength("EdgeLength", Range(0,0.2)) = 0.1
_BurnTex("BurnTex", 2D) = "white"{}
_BurnInstensity("BurnInstensity", Range(0,5)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _Normal;
half _NormalScale;
sampler2D _DisolveTex;
half _Threshold;
sampler2D _BurnTex;
half _EdgeLength;
half _BurnInstensity;
struct Input
{
float2 uv_MainTex;
float2 uv_Normal;
float2 uv_DisolveTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Normal = UnpackScaleNormal(tex2D(_Normal, IN.uv_Normal),_NormalScale);
o.Albedo = c.rgb;
float cutout = tex2D(_DisolveTex, IN.uv_DisolveTex).r;
clip(cutout - _Threshold);
float temp = saturate((cutout - _Threshold) / _EdgeLength); // temp每个片元值不同
fixed4 edgeColor = tex2D(_BurnTex,float2(temp,temp)); //采样边缘颜色
fixed4 finalColor = _BurnInstensity * lerp(edgeColor, fixed4(0,0,0,0), temp); //不同片元,根据temp取颜色
o.Emission = finalColor.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
区域过渡
使用两张纹理
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
定义与物体Y有关变量 float temp = saturate((IN.worldPos.y + _StartPoint)/_Dis)
使用差值运算Lerp 和 temp来决定最终输出纹理
float clampTemp = clamp(temp, _UnderInfluence,1);
fixed4 color = lerp(tex2D(_Tex2,IN.uv_Tex2),c,clampTemp);
Shader "Custom/Surface11" //区域过渡
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Dis("Dis",Range(0.1,10)) = 1
_StartPoint("StratPoint",Range(-10,10)) = 1 //
_Tex2("Tex2",2D) = "white" {}
_UnderInfluence("UnderInfluence",Range(0,1)) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
half _StartPoint;
fixed4 _Tint;
half _Dis;
half _UnderInfluence;
sampler2D _Tex2;
struct Input
{
float2 uv_MainTex;
float3 worldPos;
float2 uv_Tex2;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
float temp = saturate((IN.worldPos.y + _StartPoint)/_Dis); //间接控制最终纹理
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
float clampTemp = clamp(temp, _UnderInfluence,1);
fixed4 color = lerp(tex2D(_Tex2,IN.uv_Tex2),c,clampTemp);
o.Albedo = color.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
AlphaTest AlphaBlend
定义透明度测试#pragma surface surf Standard alphatest:_Cutoff noshadow addshadow
Shader "Custom/Surface12" //透明度测试
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Cutoff("Cutoff",Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="AlphaTest"}
LOD 200
Cull off
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard alphatest:_Cutoff noshadow addshadow
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
定义透明度混合:#pragma surface surf Standard alpha:blend
Shader "Custom/Surface13" //透明混合
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"= "Transparent"}
LOD 200
Cull off
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard alpha:blend
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
//FallBack "Diffuse"
}
雪
将切线空间法线信息转世界空间:
float3 normalTex = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
o.Normal = normalTex;
fixed3 wNormal = WorldNormalVector(IN, normalTex);
用世界法线和雪方向向量点乘,来区分雪的区域:
float lerpVal = saturate(dot(wNormal, _SnowDir.xyz));
使用作为差值运算中变量来区分:
fixed4 c = lerp( tex2D (_MainTex, IN.uv_MainTex), tex2D(_SonwTex, IN.uv_SonwTex), lerpVal * _SnowAmount) * _Color;
Shader "Custom/Surface14" //雪
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SonwTex("SnowTex",2D) = "white"{}
_NormalTex("Noraml", 2D) = "bump"{}
_SnowDir("SnowDir",Vector) = (0,1,0)
_SnowAmount("_SnowAmount", Range(0,2)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf StandardSpecular fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _NormalTex;
sampler2D _SonwTex;
struct Input
{
float2 uv_MainTex;
float2 uv_NormalTex;
float2 uv_SonwTex;
float3 worldNormal;
INTERNAL_DATA
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
float4 _SnowDir;
half _SnowAmount;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandardSpecular o)
{
float3 normalTex = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
o.Normal = normalTex;
fixed3 wNormal = WorldNormalVector(IN, normalTex); //切线空间法线信息转世界空间
//fixed3 wNormal = WorldNormalVector(IN, float3(0,0,1)); //模型顶点世界法线
float lerpVal = saturate(dot(wNormal, _SnowDir.xyz)); //将颜色值 与 世界法线和雪向量 的点乘联系起来
// Albedo comes from a texture tinted by color
fixed4 c = lerp( tex2D (_MainTex, IN.uv_MainTex), tex2D(_SonwTex, IN.uv_SonwTex), lerpVal * _SnowAmount) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Shader "Custom/Surface15"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_SonwTex("SnowTex",2D) = "white"{}
_NormalTex("Noraml", 2D) = "bump"{}
_SnowNormal("SnowNoraml",2D) = "bump"{}
_SnowDir("SnowDir",Vector) = (0,1,0)
_SnowAmount("_SnowAmount", Range(0,2)) = 1
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf StandardSpecular fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _NormalTex;
sampler2D _SonwTex;
sampler2D _SnowNormal;
struct Input
{
float2 uv_MainTex;
float2 uv_NormalTex;
float2 uv_SonwTex;
float2 uv_SnowNormal;
float3 worldNormal;
INTERNAL_DATA
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
float4 _SnowDir;
half _SnowAmount;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf(Input IN, inout SurfaceOutputStandardSpecular o)
{
float3 normalTex = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex)); //模型法线
float3 snowNorTex = UnpackNormal(tex2D(_SnowNormal, IN.uv_SnowNormal)); //雪法线
fixed3 wNormal = WorldNormalVector(IN, float3(0,0,1)); //模型顶点世界法线
fixed3 finalNormal = lerp(normalTex, snowNorTex, saturate(dot(wNormal, _SnowDir.xyz)) * _SnowAmount);
o.Normal = finalNormal;
fixed3 fWNormal = WorldNormalVector(IN, finalNormal); //切线空间法线转世界空间
float lerpVal = saturate(dot(fWNormal, _SnowDir.xyz));
// Albedo comes from a texture tinted by color
fixed4 c = lerp(tex2D(_MainTex, IN.uv_MainTex), tex2D(_SonwTex, IN.uv_SonwTex), lerpVal * _SnowAmount);
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
GI
Quality设置Shadowmask Mode:
- Distance Shadowmask:距离阴影遮罩,超过距离使用阴影贴图(静态物体有阴影,动态物体无阴影),小于距离使用实时阴影(静态物体和动态物体都实时阴影)。
- Shadowmask:静态物体没有距离限制,使用贴图。动态物体超过距离没有阴影。
灯光设置LightMode:
- Realtime:实时光
- Mixed:烘焙部分光,并提供实时光。
- Baked:完全烘焙所有光。
灯光烘焙 MixedLighting中LightingMode:
- Back Indirect:静态物体只烘焙间接光
- Subtractive:静态物体烘焙间接光和阴影。动态物体接受实时光,可以投射阴影,只能使用一个方向光。
- ShadowMask:同上。间接光烘焙到光照贴图和光探针里,阴影烘焙到阴影遮罩贴图和遮挡探针里。
CubeMap
CubeMap通常被用来作为具有反射属性物体的反射源。可以使用方向向量对它们索引和采样
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);
fixed3 col = ambient + lerp(diffuse, refection, saturate(fresnel));
菲涅尔反射
用来描述光在不同折射率的介质之间的行为。用公式推导出的光的反射称之为“菲涅尔反射”。
Shader "MyShader/009"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_FresnelScale("FresnelScale",Range(0,1))=0.5
_Cubemap("Cubemap",Cube)="_Skybox"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 worldPos:TEXCOORD2;
float3 worldNormal:TEXCOORD3;
float3 worldViewDir:TEXCOORD4;
float3 worldReflection:TEXCOORD5;
};
sampler2D _MainTex;
samplerCUBE _Cubemap;
float4 _MainTex_ST;
float _FresnelScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
o.worldReflection = reflect(-o.worldViewDir, o.worldNormal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 worldViewDir = normalize(i.worldViewDir);
fixed3 diffuse = tex2D(_MainTex, i.uv).rgb*_LightColor0.rgb*saturate(dot(worldNormal, worldLightDir));
fixed3 reflection = texCUBE(_Cubemap, i.worldReflection).xyz;
float fresnel = _FresnelScale + (1 - _FresnelScale)*pow(1 - dot(worldViewDir, worldNormal), 5);//菲涅尔系数
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel));
UNITY_APPLY_FOG(i.fogCoord, color);
return fixed4(color, 1);
}
ENDCG
}
}
}
普通反射:
使用世界空间光的反射向量对CubMap贴图采样
fixed3 refection = texCUBE(_CubeMap, i.worldRefl).rgb * _ReflectionColor;
使用差值运算取原纹理和反射颜色
fixed3 diffuse = tex2D(_MainTex, i.uv).rgb * _LightColor0.rgb * max(0,dot(worldNormal,worldLightDir));
fixed3 col = ambient + lerp(diffuse, refection, _ReflectionAmount);
Shader "Unlit/012-Reflection"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_CubeMap("Cubemap", Cube) = "_Skybox"{}
_ReflectionColor("ReflectionColor",Color) = (1,1,1,1)
_ReflectionAmount("ReflectionAmount", Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal: NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD02;
float3 worldNormal : TEXCOORD03;
float3 worldViewDir : TEXCOORD04;
float3 worldRefl : TEXCOORD05;
};
fixed4 _ReflectionColor;
samplerCUBE _CubeMap;
half _ReflectionAmount;
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// sample the texture
fixed3 diffuse = tex2D(_MainTex, i.uv).rgb * _LightColor0.rgb * max(0,dot(worldNormal,worldLightDir));
fixed3 refection = texCUBE(_CubeMap, i.worldRefl).rgb * _ReflectionColor;
fixed3 col = ambient + lerp(diffuse, refection, _ReflectionAmount);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return fixed4(col,1);
}
ENDCG
}
}
}
折射:
对世界空间光反射向量进行折射:
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefracRotio);
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefracRotio);
后处理实现GrabPass
Unity的GrabPass(抓屏)功能是个极其方便的功能,相信写过UnityShader的同学都接触过,90%的书和教程上都使用GrabPass来实现半透材质折射、热空气扭曲等效果。但是其性能却堪忧,特别是受限于移动端硬件,每次抓屏得到的数据要从GPU拷贝回CPU进行计算,这个数据传输过程是非常耗时的,加之GrabPass获取到的图像分辨率和显示屏是一致的,这意味着在高分辨率的设备上,这个过程极易造成带宽阻塞。这也是在中低端安卓机上GrabPass会有卡顿的原因。
GrabPass、OnRenderImage、RenderTexture三者的性能分析,参考下文。结论是,GrabPass最耗费性能,OnRenderImage次之,RenderTexture最省。但是RenderTexture需要两个摄像机,DC会翻倍,所以在复杂的场景中,OnRenderImage是最合适做抓屏的。我们本次案例就使用OnRenderImage方法。
GrabPass
玻璃效果
(1)通过法线偏移屏幕采样坐标
float2 offset = tangentNormal.xy * _Distortion * GrabPass_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy; //偏移抓屏贴图uv坐标
(2)对抓屏进行采样
fixed3 refrCol = tex2D(GrabPass, i.scrPos.xy / i.scrPos.w).rgb;
(3)反射
fixed3 reflCol = texCUBE(_Cubemap, reflect(-viewDir, worldNormal)).rgb * albedo; //反射颜色与物体颜色融合
(4)输出fixed3 color = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;
Shader "Unlit/016-GrabPass"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Diffuse("Color",Color) = (1,1,1,1)
_BumpMap("Normal Map",2D) = "white"{}
_BumpScale("Bump Scale", float) = 1
_Cubemap("CubeMap",Cube) = "_Skybox"{}
_Distortion("Distortion",Range(0,100)) = 10 //控制折射时图像的扭曲程度
_RefractAmount("RefractAmount",Range(0,1)) = 1 //反射和折射的平衡值
}
SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Transparent+100"}
LOD 100
GrabPass{"GrabPass"} //通过GrabPass定义一个屏幕抓取的图像
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile __ SNOW_ON
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float4 uv :TEXCOORD0;
float4 TtoW0 : TEXCOORD1;
float4 TtoW1 :TEXCOORD2;
float4 TtoW2 :TEXCOORD3;
float4 scrPos: TEXCOORD4;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Diffuse;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
sampler2D GrabPass;
float4 GrabPass_TexelSize; //获取宽高像素值
float _Distortion;
samplerCUBE _Cubemap;
float _RefractAmount;
v2f vert(appdata_tan v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); //裁剪空间坐标
o.scrPos = ComputeGrabScreenPos(o.vertex); //ComputeGrabScreenPos 得到对应抓取屏幕图像的采样坐标。
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
fixed3 worldPos = mul(unity_ObjectToWorld, v.vertex);
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);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT;
fixed4 albedo = tex2D(_MainTex, i.uv);
float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
fixed3 lightDir = UnityWorldSpaceLightDir(worldPos);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//求法线 将切线坐标法线转世界坐标
fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
fixed3 tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
fixed3 worldNormal = normalize(float3(dot(i.TtoW0.xyz, tangentNormal), dot(i.TtoW1.xyz, tangentNormal),dot(i.TtoW2.xyz,tangentNormal)));
//采样抓屏贴图
float2 offset = tangentNormal.xy * _Distortion * GrabPass_TexelSize.xy; //采样点更根据切线空间法线方向偏移
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy; //偏移抓屏贴图uv坐标 i.scrPos.z代表深度, 深度越深 偏移越大
fixed3 refrCol = tex2D(GrabPass, i.scrPos.xy / i.scrPos.w).rgb; //抓屏贴图纹理采样
fixed3 reflCol = texCUBE(_Cubemap, reflect(-viewDir, worldNormal)).rgb * albedo; //反射颜色与物体颜色融合
fixed3 color = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;
return fixed4(color,1);
}
ENDCG
}
}
}
动画
序列帧动画
改变采样uv坐标,再对纹理进行采样
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorAmount);
float column = time - row * _HorAmount;
half2 uv = i.uv + half2(column, -row);
uv.x /= _HorAmount;
uv.y /= _VerAmount;
// sample the texture
fixed4 col = tex2D(_MainTex, uv);
Shader "Unlit/017-Animation"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_HorAmount("HorAmount", float) = 4
_VerAmount("VerAmount", float) = 4
_Speed("Speed",Range(1,100)) = 30
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
LOD 100
Pass
{
Zwrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _HorAmount;
float _VerAmount;
float _Speed;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorAmount);
float column = time - row * _HorAmount;
half2 uv = i.uv + half2(column, -row);
uv.x /= _HorAmount;
uv.y /= _VerAmount;
// sample the texture
fixed4 col = tex2D(_MainTex, uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
滚动动画
动态改变采样x轴
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_ScrollX,0) * _Time.y;
Shader "Unlit/018-Scroll"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollX("Scroll Speed", Range(0,10)) = 2
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _ScrollX;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_ScrollX,0) * _Time.y;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
顶点动画
顶点更根据顶点的v.vertex.x改变y
v.vertex.y = v.vertex.y + _Arange * sin(_Time.y * _Speed + v.vertex.x * _Frequency); //每个顶点y值 +
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
Shader "Unlit/019-VertAnim"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Arange("Arange",float) = 1
_Frequency("Frequency",float) = 1
_Speed("Speed",float) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Arange;
float _Frequency;
float _Speed;
v2f vert (appdata v)
{
v2f o;
v.vertex.y = v.vertex.y + _Arange * sin(_Time.y * _Speed + v.vertex.x * _Frequency); //每个顶点y值 +
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
广告牌
(1)物体到视角向量作为法线
float3 normalDir = view - center; //用物体到视角的向量作为法线
normalDir.y = normalDir.y * _Verical;
normalDir = normalize(normalDir);
(2)向上的向量 和 法线 获取右边方向向量
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
(3)法线 和 右边向量 获取向上向量
upDir = normalize(cross(normalDir, rightDir));
(4)使原来顶点偏移,获取新的坐标
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
(5)将新坐标当做当前顶点,生成裁剪空间坐标
o.vertex = UnityObjectToClipPos(float4(localPos,1));
Shader "Unlit/020-guanggao"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
[MaterialToggle]_Verical("Verical", Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBacthing"="True"} //关闭合批
LOD 100
Pass
{
Zwrite off
Blend SrcAlpha OneMinusSrcAlpha
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Verical;
v2f vert (appdata v)
{
v2f o;
float3 center = float3(0, 0, 0);
float3 view = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); //将视角方向从世界空间转模型空间
float3 normalDir = view - center; //用物体到视角的向量作为法线
normalDir.y = normalDir.y * _Verical;
normalDir = normalize(normalDir);
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.vertex = UnityObjectToClipPos(float4(localPos,1));
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
更多推荐
所有评论(0)