- A+
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 |
Shader "chicai/specular"{ Properties{ _Specular("Specular",Color)=(1,1,1,1) _Diffuse("Diffuse",Color)=(1,1,1,1) _Gloss("Gloss",Range(5,100)) = 5 } SubShader{ Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #include "Lighting.cginc" fixed4 _Specular;//高光自身颜色 float4 _Diffuse;//漫反射自身颜色 float _Gloss;//高光范围 struct a2v{ float4 v:POSITION;//顶点 float3 nor:NORMAL;//法线 }; struct v2f{ float4 pos:SV_POSITION;//剪裁空间顶点 float3 nor:COLOR0;//世界坐标下的法线 float4 worldV:COLOR1;//世界坐标下的顶点 }; #pragma vertex vert #pragma fragment frag v2f vert(a2v i){ v2f o; o.pos = UnityObjectToClipPos(i.v); o.nor = normalize ( mul(unity_ObjectToWorld,i.nor) ); o.worldV = mul (unity_ObjectToWorld , i.v); return o; } fixed4 frag(v2f i):SV_Target{ fixed3 lightDir = normalize( UnityWorldSpaceLightDir(i.worldV) ); //fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //和上面代码一样效果,得到光照的方向 fixed3 reflectDir = normalize( reflect(-lightDir,i.nor) ); fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldV.xyz) ); //fixed3 viewDir = normalize( _WorldSpaceCameraPos - i.worldV.xyz ); //和上面代码一样效果,得到视野方向 fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT; fixed4 _SpecularCol = _Specular * _LightColor0 * (pow (max(dot(reflectDir,viewDir),0) , _Gloss)); fixed4 _DiffuseCol = _Diffuse * _LightColor0 * (dot(i.nor,lightDir) * 0.5 + 0.5); fixed4 Col = _SpecularCol + _DiffuseCol + ambient; return fixed4(Col.xyz,1); } ENDCG } } } |
在漫反射条件下再加多了一个高光,Blinn计算公式是 :直射光 * pow(max(cosβ,0),范围系数),β角指的是反射光的方向和该点到相机(视野)方向的夹角
1 2 3 4 5 6 7 |
v2f vert(a2v i){ v2f o; o.pos = UnityObjectToClipPos(i.v); o.nor = normalize ( mul(unity_ObjectToWorld,i.nor) ); o.worldV = mul (unity_ObjectToWorld , i.v); return o; } |
因为这里写的是逐像素计算的,所有在顶点函数中只计算了一些片元函数需要的数据,通过结构体v2f传递过去。主要是两个数据,一个是世界坐标下的模型顶点坐标,用来计算视野方向和光线方向。另一个是世界坐标下的法线向量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fixed4 frag(v2f i):SV_Target{ fixed3 lightDir = normalize( UnityWorldSpaceLightDir(i.worldV) ); //fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //和上面代码一样效果,得到光照的方向 fixed3 reflectDir = normalize( reflect(-lightDir,i.nor) );//反射光方向的计算 fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldV.xyz) ); //fixed3 viewDir = normalize( _WorldSpaceCameraPos - i.worldV.xyz ); //和上面代码一样效果,得到视野方向 fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT; fixed4 _SpecularCol = _Specular * _LightColor0 * (pow (max(dot(reflectDir,viewDir),0) , _Gloss)); fixed4 _DiffuseCol = _Diffuse * _LightColor0 * (dot(i.nor,lightDir) * 0.5 + 0.5); fixed4 Col = _SpecularCol + _DiffuseCol + ambient; return fixed4(Col.xyz,1); } |
主要处理的片元函数,因为计算高光反射的影响系数,需要一个反射光方向和视野方向,这里通过reflect()方法,传递一个入射光线的方向和法线向量计算出反射光方向,下面通过UnityWorldSpaceViewDir(世界坐标下的顶点坐标) 得到视野方向。最后,通过计算公式算出系数,在乘光的颜色那就行了
这里还是讲一下内置的一些方法
UnityWorldSpaceViewDir/UnityWorldSpaceLightDir ,使用Unity开头-》传递一个世界坐标,转化为世界空间视野/光照方向
WorldSpaceViewDir/WorldSpaceLightDir,没有Unity但还是有World-》传递一个模型空间坐标,转化为世界空间视野/光照方向
ObjSpaceViewDir/ObjSpaceLightDir,模型到模型视野/光照方向
最后要注意的是,表达方向的时候要使用3维的向量表示。今天用4维的来表示法线了,结果高光出现问题了。反正方向是不需要平移的w分量的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fixed4 frag(v2f i):SV_Target{ fixed3 lightDir = normalize( UnityWorldSpaceLightDir(i.worldV) ); fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldV) ); fixed3 avgDir = normalize (lightDir + viewDir);//计算平分线方向 fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT; fixed4 _SpecularCol = _Specular * _LightColor0 * (pow (max(dot(i.nor,avgDir),0) , _Gloss)); fixed4 _DiffuseCol = _Diffuse * _LightColor0 * (dot(i.nor,lightDir) * 0.5 + 0.5); fixed4 Col = _SpecularCol + _DiffuseCol + ambient; return fixed4(Col.xyz,1); } |
这是另外一个高光模型,主要就是计算系数的公式不一样,Blinn-Phone计算公式 : Specular = 直射光 * pow(max(cosβ,0),范围系数),β角指的是法线和x的夹角,x指的是直射光和视野方向的平分线
最后看看两者的效果
左边是Blinn模型,右边是Blinn-Phone模型