漫反射
漫反射用于描述当光线从光源照射到模型表面时,该部分会向每个方向散射多少幅射量。
漫反射光照是用于对那些被物体表面随机散射到各个方向的辐射度进行建模的。在漫反射中,视角的位置是不重要的,因为反射是完全随机的。因此可以认为在任何反射方向上的分布都是一样的。但是,入射光线的角度很重要。
兰伯特模型
反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。
$$c_{diffuse}=(c_{light}·m_{diffuse})max(0, \hat{n}·I)$$
$\hat{n}$是表面法线,$I$是指向光源方向的单位矢量,$c_{diffuse}$是材质的漫反射颜色,$m_{light}$是光源颜色。需要注意的是,需要防止法线和光源方向点乘的结果为负值,为此,我们使用max
函数来将其截取到0,这可以防止物体被从后面来的光源照亮。
顶点着色器处理的兰伯特模型1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| Shader "Unlit/VertexLambertDiffuse" { Properties { _DiffuseColor ("Diffuse Color", Color) = (1, 1, 1, 1) }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * saturate(dot(worldNormal, worldLight));
o.color = ambient + diffuse;
return o; }
fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); }
ENDCG } }
FallBack "Diffuse" }
|

顶点着色兰伯特漫反射
片元着色器处理的兰伯特模型1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| Shader "Unlit/FragLamberDiffuse" { Properties { _DiffuseColor ("Diffuse", Color) = (1, 1, 1, 1) }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o; }
fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0); }
ENDCG } }
FallBack "Diffuse" }
|

片元着色兰伯特漫反射
半兰伯特模型
与半兰伯特模型相比,半兰伯特光照模型没有使用max
操作来防止$\hat{n}$和$I$的点积为负数,而是对其结果进行了一个$α$倍的缩放在加上一个$β$大小的偏移。绝大数情况下,$α$和$β$的平均值为0.5,即公式:
$$c_{diffuse}=(c_{light}·m_{diffuse})(α(\hat{n}·I)+β)$$
通过这样的方式,我们可以把$\hat{n}·I$的结果范围从$[-1,1]$映射到$[0,1]$范围内。对于模型的背光面,在原兰伯特光照模型中点积结果将映射到同一个值,即0出;而在ban兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射在不同的值上。
片元着色器处理的半兰伯特模型1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| Shader "Unlit/FragHalfLamberDiffuse" { Properties { _DiffuseColor ("Diffuse", Color) = (1, 1, 1, 1) }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o; }
fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5; fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0); }
ENDCG } }
FallBack "Diffuse" }
|

片元着色半兰伯特漫反射
高光反射(specular)
高光反射用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少幅射量。
高光反射是一种经验模型,也就是说,它并不完全符合真实世界中的高光反射现象。它可用于计算那些沿着完全镜面反射方向被反射的光线,这可以让物体看起来是有光泽的,例如金属材质。
计算高光反射需要知道的信息比较多,如表面法线、视角方向、光源方向、反射方向等。
phong高光模型

phong高光反射图
$$c_{specular}=(c_{light}·m_{specular})max(0, \hat{v}·r)^{m_{gloss}}$$
公式中$c_{light}$是入射光线的颜色和强度,$m_{specular}$是材质的高光反射颜色,它用于控制该材质对于高光反射的强度和颜色,$\hat{v}$是视角方向,$r$是反射方向,$m_{gloss}$是材质光泽度,也被称为反光度(shininess),它用于控制高光区域的亮点有多宽,gloss越大,亮点越小,同样,这了也需要放置$\hat{v}·r$的结果为负数。其中,反射方向$r$可以由表面法线$\hat{v}$和光源方向$\hat{I}$通过公式计算得到:
$$r=2(\hat{n}·\hat{I})\hat{n}-\hat{I}$$
在cg里面也可以通过reflect(i,n)
函数计算得到。
顶点着色器处理的phong高光1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| Shader "Unlit/VertexPhoneSpecular" { Properties { _DiffuseColor ("Diffuse Color", Color) = (1, 1, 1, 1) _SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss ("Specular Gloss", Range(8.0, 256)) = 20 }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor; fixed4 _SpecularColor; float _SpecularGloss;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(reflectDir, viewDir)), _SpecularGloss);
o.color = ambient + diffuse + specular;
return o; }
fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); }
ENDCG } }
FallBack "Specular" }
|

顶点着色phong高光
片元着色器处理的phong高光模型1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| Shader "Unlit/FragPhongSpecular" { Properties { _DiffuseColor ("Diffuse Color", Color) = (1, 1, 1, 1) _SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss ("Specular Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor; fixed4 _SpecularColor; float _SpecularGloss;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o; }
fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(reflectDir, viewDir)), _SpecularGloss);
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } }
FallBack "Specular" }
|

片元着色phong高光
blin-phong高光模型
相比于phong高光模型,布林冯氏光照模型的高光效果反射部分看起来更大、更亮一些。在实际渲染中,绝大多数情况我们都会选择Blinn-Phong光照模型。
blinn模型没有使用反射方向,而是引入一个新的矢量$\hat{h}$我们常说的半角向量,它是通过对$\hat{v}$和$\hat{I}$的取平均后再归一化得到。
$$\hat{h}=\dfrac{\hat{v}+\hat{I}}{|\hat{v}+\hat{I}|}$$
$$c_{specular}=(c_{light}·m_{specular})max(0, \hat{n}·\hat{h})$$
在硬件实现时,如果摄像机和光源距离模型足够远的话,Blinn模型会快于Phong模型,这是因为,此时可以认为v和I都是定值,因此h将是一个常量,但是,当v和I不是定值时,Phong模型可能反而更快一些。
片元着色器处理的BlinnPhong高光1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| Shader "Unlit/FragBlinnPhongSpecular" { Properties { _DiffuseColor ("Diffuse Color", Color) = (1, 1, 1, 1) _SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss ("Gloss Color", Range(8.0, 256)) = 20 }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor; fixed4 _SpecularColor; float _SpecularGloss;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; };
struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o; }
fixed4 frag(v2f i) : SV_Target {
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(worldNormal, halfDir)), _SpecularGloss);
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } }
FallBack "Specular" }
|

片元着色blinnPhong高光
凹凸映射
凹凸映射的实现主要有2种实现方式:一种是使用一张高度纹理来模拟表面位移,然后得到一个修改后的法线值。另一种是使用一张法线纹理来直接存储表面法线。
高度贴图
高度图中存储的是强度值,它用于表示模型表面局部的海拔高度。颜色越浅表面该位置的表面越向外凸起,而颜色越深表示该位置越往里凹。这种方法的好处是非常直观,可以明确知道一个模型表面的凹凸情况,但是计算会比较复杂,在实时计算时不能直接得到表面法线,需要由像素的灰度值计算而得,因此消耗更多的性能。
法线贴图
法线纹理中存储的是表面的法线方向。由于法线方向的分量范围在$[-1,1]$,而像素的分量范围在$[0,1]$,因此要得到像素对应的法线值,需要进行简单运算。
$$pixel=\dfrac{normal+1}{2}$$
$$normal=pixel*2-1$$
方向是相对于坐标空间来说的,对于法线纹理,一种就是直接将模型空间的法线存储在一张纹理里面,这是模型空间的法线纹理,另一种是存储切线空间的法线到纹理里面,这是切线空间的法线纹理,也是常用的处理方式。
法线纹理空间,原点是模型的顶点位置,z轴是订单的法线方向(n),x轴是顶点的切线方向(t),y轴是法线与切线叉积得到的副切线(b)。
模型空间法线特点
- 计算量少,不需要模型的原始的法线和切线等信息
- 法线直接可以通过模型的顶点的法线获得,生成简单
- 在模型空间下的法线纹理存储的是同一坐标系下的法线信息,在边界处通过插值得到的法线可以平滑变换,可以提供更平滑的边界,在纹理坐标的缝合处和尖锐的边角部分,可见的缝隙较少。
- 模型空间下的法线纹理记录是绝对的法线信息,仅可用于创建它的那个模型,而应用到其他模型上效果完全错误,复用不好。
切线空间法线特点
- 模型的切线一般是和UV方向相同,想要得到效果比较好的法线映射就要求纹理映射也是映射的,生成较复杂。
- 切线空间下的法线纹理中的法线是依靠纹理坐标的方向得到的结果,可能会在边缘处或尖锐的部分造成更多的缝合迹象。
- 切线空间下的法线纹理记录是相对法线信息,即便把纹理应用到一个完全不一样的网格上,也可以得到一个合理的结果。
- 可以进行UV动画
- 纹理丢到其它模型上,效果也不会太差,服用性比较好
- 由于切线空间下的法线纹理中的法线的Z方向总是正方向,因此可以仅存储XY方向,推导出Z的方向,而不存储Z方向数据,来压缩数据。
切线空间下的计算
在片元着色器中通过纹理采样得到切线空间下的法线,然后在与切线空间下的视角方向、光照方向等进行计算,得到最终的光照效果。这期间需要在顶点着色器中把视角方向和光照方向从模型空间变换到切线空间中,即需要知道从模型空间到切线空间的变换矩阵,可以通过切线空间到模型空间的变换矩阵的逆矩阵来得到。
切线空间处理法线贴图1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| Shader "Unlit/normalInTangent" { Properties { _Color("Color", Color) = (1, 1, 1, 1) _MainTex("Main Tex", 2D) = "white" {} _NormalTex("Normal Tex", 2D) = "bump" {} _NormalFac("Normal Fac", Float) = 1.0 _SpecularColor("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss("Specular Gloss", Range(1.0, 256)) = 8 }
SubShader { Pass { Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; float4 _NormalTex_ST; float _NormalFac; fixed4 _SpecularColor; float _SpecularGloss;
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 * _NormalTex_ST.xy + _NormalTex_ST.zw;
TANGENT_SPACE_ROTATION;
float3 oSpaceLightDir = ObjSpaceLightDir(v.vertex); o.lightDir = mul(rotation, oSpaceLightDir).xyz;
float3 oSpaceViewDir = ObjSpaceViewDir(v.vertex); o.viewDir = mul(rotation, oSpaceViewDir).xyz;
return o; }
fixed4 frag(v2f i) : SV_Target{ fixed3 tangentLightDir = normalize(i.lightDir); fixed3 tangentViewDir = normalize(i.viewDir);
fixed4 packedNormal = tex2D(_NormalTex, i.uv.zw);
fixed3 tNormal = UnpackNormal(packedNormal);
tNormal.xy *= _NormalFac; tNormal.z = sqrt(1.0 - saturate(dot(tNormal.xy, tNormal.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(tNormal, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tNormal, halfDir)), _SpecularGloss);
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } }
FallBack "Specular" }
|

法线贴图效果
世界空间下的计算
在顶点着色器中计算切线空间到世界空间的矩阵,并把他传递给片元着色器。变换矩阵的计算可以由顶点的切线,副切线和法线在世界空间下的表示得到,最后在片元着色器中把法线纹理中的法线方向从切线空间变换到世界空间下便可。
世界空间处理法线贴图1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| Shader "Unlit/normalInWorld" { Properties { _Color("Color", Color) = (1, 1, 1, 1) _MainTex("Main Tex", 2D) = "white" {} _NormalTex("Normal Tex", 2D) = "bump" {} _NormalFac("Normal Fac", Float) = 1.0 _SpecularColor("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss("Specular Gloss", Range(1.0, 256)) = 8 }
SubShader { Pass { Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; float4 _NormalTex_ST; float _NormalFac; fixed4 _SpecularColor; float _SpecularGloss;
struct a2v{ float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float4 texcoord : TEXCOORD0; };
struct v2f{ float4 pos : SV_POSITION; float4 uv : TEXCOORD0; float4 TtoW0 : TEXCOORD1; float4 TtoW1 : TEXCOORD2; float4 TtoW2 : TEXCOORD3; };
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 * _NormalTex_ST.xy + _NormalTex_ST.zw;
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o; }
fixed4 frag(v2f i) : SV_Target{
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
fixed3 tNormal = UnpackNormal(tex2D(_NormalTex, i.uv.zw));
tNormal.xy *= _NormalFac; tNormal.z = sqrt(1.0 - saturate(dot(tNormal.xy, tNormal.xy)));
tNormal = normalize(half3(dot(i.TtoW0.xyz, tNormal), dot(i.TtoW1.xyz, tNormal), dot(i.TtoW2.xyz, tNormal)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tNormal, lightDir));
fixed3 halfDir = normalize(lightDir + viewDir); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tNormal, halfDir)), _SpecularGloss);
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } }
FallBack "Specular" }
|

法线贴图效果
渐变纹理
通过计算漫反射角度来采样过渡纹理的颜色表现漫反射的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| Shader "Unlit/RampTexture" { Properties { _DiffuseColor ("Diffuse Color", Color) = (1, 1, 1, 1) _RampTex ("Ramp Tex", 2D) = "white" {} _SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss ("Specular Gloss", Range(8.0, 256)) = 20 }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor; sampler2D _RampTex; float4 _RampTex_ST; fixed4 _SpecularColor; float _SpecularGloss;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; };
struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
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; fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _DiffuseColor.rgb; fixed3 diffuse = _LightColor0.rgb * diffuseColor;
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(worldNormal, halfDir)), _SpecularGloss);
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } } }
|

渐变纹理
遮罩纹理
使用纹理图来控制和保护某些区域,使它们免于某些修改,例如使用遮罩图控制模型高光控制,非金属部分不让高光。
高光遮罩纹理处理1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| Shader "Unlit/SpecularMask" { Properties { _Color ("Color", Color) = (1, 1, 1, 1) _MainTex ("Main Tex", 2D) = "white" {} _NormalTex ("Normal Tex", 2D) = "bump" {} _NormalFac("Normal Fac", Float) = 1.0 _SpecularMaskTex ("Specular Mask Tex", 2D) = "white" {} _SpecularMaskFac ("Specular Mask Fac", Float) = 1.0 _SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) _SpecularGloss ("Specular Gloss", Range(8.0, 256)) = 20 }
SubShader { Pass { Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; float _NormalFac; sampler2D _SpecularMaskTex; float _SpecularMaskFac; fixed4 _SpecularColor; float _SpecularGloss;
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; };
v2f vert(a2v v) { v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
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(_NormalTex, i.uv)); tangentNormal.xy *= _NormalFac; 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(_SpecularMaskTex, i.uv).r * _SpecularMaskFac;
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, halfDir)), _SpecularGloss) * specularMask;
return fixed4(ambient + diffuse + specular, 1.0); }
ENDCG } }
FallBack "Specular" }
|

遮罩纹理