Unity Shader应用
实验性工具 Shader Forge这里的连连看插件用的是Shader Forge,但是现在已经不更新了,最后更新是在2018的版本中,但是也可以用。 Shader Forge最后生成的Shader代码没有Shader Graph那么多,稍微方便一点,所以先用这个。
漫反射模型(兰伯特光照)
兰伯特光照是最基础的漫反射模型,其效果如下
Lambert光照模型
在兰伯特光照中,很明显,朝向光的点最亮(灰度为1),背向光的点最暗(灰度为0),中间则介于0~1之间。
通过光的反方向lDir和某个点的朝向(该点法向量)nDir的接近程度,就可以得知该点是否面朝光。
刚好,数学上的点乘Dot刚好可以用于表示两个向量方向的接近程度,数值越大,两者方向越接近。(两个向量前提都经过了归一化处理)
点乘的定义是:a·b = |a||b|cosθ
当某点面朝光时,θ = 0,那么cosθ = 1。最后点乘结果也是等于1。
对模型上的每个点都进行一次 灰度值 = a·b 的运算,就可以得到整个模型经过漫反射之后的外观。
[*]用到的函数:
UnityObjectToClipPos(点的局部坐标)
UnityObjectToWorldNormal( 点的局部法向量 )
dot(向量,向量)
代码
Shader "Shader Forge/02" { Properties { } SubShader { Tags { "RenderType"="Opaque" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; }; struct VertexOutput { float4 pos : SV_POSITION; float3 normalWS : TEXCOORD; }; VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.pos = UnityObjectToClipPos( v.vertex ); o.normalWS = UnityObjectToWorldNormal( v.normal ); return o; } float4 frag(VertexOutput i) : COLOR { float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float c = dot(i.normalWS,lightDir); c = max(0,c); return float4(c,c,c,1); } ENDCG } } FallBack "Diffuse" }镜面反射模型(高光反射)
Phong 模型
思想:亮度 = Dot(光折射方向, 视方向)
用到的函数:reflect(入射光,法线) 、mul( 矩阵,矩阵(或向量) ) 用到的矩阵:unity_ObjectToWorld 用到的全局变量:_WorldSpaceCameraPos、_WorldSpaceLightPos0
image.png
代码
Shader "Shader Forge/02" { Properties { _Color("颜色",Color) = (1.0,1.0,0.0,1.0) _SpecPow("高光次幂",Range(1,50)) = 30 } SubShader { Tags { "RenderType"="Opaque" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" uniform float4 _Color; uniform float _SpecPow; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; }; struct VertexOutput { float4 posCS : SV_POSITION; float4 posWS : TEXCOORD0; float3 normalWS : TEXCOORD1; }; VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.posCS = UnityObjectToClipPos( v.vertex ); o.posWS = mul(unity_ObjectToWorld, v.vertex); o.normalWS = UnityObjectToWorldNormal( v.normal ); return o; } float4 frag(VertexOutput i) : COLOR { //准备向量 float3 nDir = i.normalWS; float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS); float3 lDir = normalize(_WorldSpaceLightPos0.xyz); float3 rDir = reflect(-lDir,nDir); //准备点积结果 float nDotl = dot(nDir,lDir); float vDotr = dot(vDir,rDir); //光照模型 float lambert = max(0,nDotl); //float phong = pow(max(0,vDotr),_SpecPow);直接截断负值的方式 float phong = pow((vDotr+1)/2,_SpecPow);// 将[-1,1]的区间映射到的方式 //输出结果 float3 finalRGB = _Color * lambert + phong; return float4(finalRGB,1.0); } ENDCG } } FallBack "Diffuse" }Blinn-Phong 模型
思想:亮度 = Dot(半角方向, 法线方向)
代码(除了frag其他和Phone一样)
float4 frag(VertexOutput i) : COLOR { float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS); float3 lDir = normalize(_WorldSpaceLightPos0.xyz); float3 hDir = normalize(vDir+lDir); float3 nDir = i.normalWS; float nDotl = dot(nDir,lDir); float nDoth = dot(nDir,hDir); float lambert = max(0,nDotl); float B_Phong = pow(max(0,nDoth),_SpecPow); float3 finalRGB = _Color * lambert + B_Phong; return float4(finalRGB,1.0); } 将法线映射为颜色
float4 frag(VertexOutput i) : COLOR { return float4(i.normalWS,1.0); } 三色环境光
思路:
不同侧面的环境光:利用法线方向来确定朝向。 模型对环境光的遮挡导致的亮暗区别(例如耳朵朝内凹,因此内部容易被遮挡,会较暗):利用一张AO图(需要烘焙),来指明被遮挡的区域。
AO纹理
代码 (最后随意地结合了一下Lambert和Phong)
Shader "Shader Forge/03" { Properties { _Color("颜色",Color) = (1.0,1.0,0.0,1.0) _SpecPow("高光次幂",Range(1,90)) = 30 _EnvUpCol("上部环境光色",Color) = (1,1,1,1) _EnvDownCol("下部环境光色",Color) = (0.2,0.2,0.2,1) _EnvSideCol("侧边环境光色",Color) = (0.1,0.8,0.1,1) _Occlusion("环境遮罩图AO",2d) = "white"{} _EnvIntensity("环境光强度",Range(0,1)) = 0.3 } SubShader { Tags { "RenderType"="Opaque" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" uniform float4 _Color; uniform float _SpecPow; uniform float4 _EnvUpCol; uniform float4 _EnvDownCol; uniform float4 _EnvSideCol; uniform float _EnvIntensity; uniform sampler2D _Occlusion; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct VertexOutput { float4 posCS : SV_POSITION; float4 posWS : TEXCOORD0; float3 normalWS : TEXCOORD1; float2 uv : TEXCOORD2; }; VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.posCS = UnityObjectToClipPos( v.vertex ); o.posWS = mul(unity_ObjectToWorld, v.vertex); o.normalWS = UnityObjectToWorldNormal( v.normal ); o.uv = v.uv; return o; } float4 frag(VertexOutput i) : COLOR { //准备向量 float3 nDir = i.normalWS; float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS); float3 lDir = normalize(_WorldSpaceLightPos0.xyz); float3 rDir = reflect(-lDir,nDir); //准备点积结果 float nDotl = dot(nDir,lDir); float vDotr = dot(vDir,rDir); //光照模型 //漫反射 + 镜面反射 float lambert = max(0,nDotl); float phong = pow((vDotr+1)/2,_SpecPow); //环境光 float upMask = max(0,i.normalWS.y); float downMask = max(0,-i.normalWS.y); float sideMask = 1 - upMask - downMask; float occlusion = tex2D(_Occlusion,i.uv); float3 envCol = _EnvUpCol*upMask + _EnvSideCol*sideMask + _EnvDownCol*downMask; //输出结果 float3 finalRGB = (_Color * lambert *(1-_EnvIntensity) + envCol * _EnvIntensity * occlusion + phong );//这个暂时先用加法简单相加了,实际不知道是什么样的 return float4(finalRGB,1.0); } ENDCG } } FallBack "Diffuse" }
页:
[1]