UnityShader学习7 vertex的 光照
我们先了解一下颜色的加法和乘法。加法表示颜色叠加;乘法表示颜色的混合。
首先,一个完整的光照需要有:能够反射环境光、能够有镜面高光、漫反射。
漫反射思路:漫反射就是利用一个主光源(平行光)向量与顶点向量的法向量的点积,来得知它们的夹角。
如果点积结果等于0,那么法向量与光线夹角等于90度,此时,顶点接收的光照率最小(用点积结果去乘光照颜色);
如果点积结果小于1并且大于0,那么,法向量与光线夹角小于90度;
如果点积结果小0,那么,法向量与光线夹角大于90度,顶点不受光照影响。
注意,在求点积之前,我们要先却把两个向量都是单位向量,保证结果的准确性。
以下给出漫反射的代码:
1 //漫反射 2 Shader "ShaderLearn/Light/light_1" { 3 4 SubShader { 5 pass{ 6 Tags{"LightMode" = "ForwardBase"}//光照模型,一定要写 7 CGPROGRAM 8 #pragma vertex vert 9 #pragma fragment frag 10 #include "UnityCG.cginc" 11 #include "Lighting.cginc" 12 13 struct v2f 14 { 15 float4 pos : POSITION; 16 fixed4 color : COLOR; 17 }; 18 //在顶点计算光照效率高,片段程序比较慢,但是效果更好 19 //漫反射、点光源、环境光、镜面反射,这里只给出了环境光, 20 v2f vert(appdata_base v) 21 { 22 v2f o; 23 o.pos = UnityObjectToClipPos(v.vertex); 24 25 26 float3 N = normalize(v.normal);//法向量,这是在模型坐标系中的 27 float3 L = normalize(_WorldSpaceLightPos0);//光照 _WorldSpaceLightPos0为内建变量,描述的是世界坐标的平行光的坐标 28 N = mul(float4(N, 0), unity_WorldToObject).xyz;//法向量的变换等于变换的逆的转置,就是变换到了世界坐标系 29 N = normalize(N);//点积之前要归一化 30 31 float ndotl = saturate(dot(N, L));//法线和光线的点积 32 o.color = _LightColor0 * ndotl;//光照颜色 33 34 float3 wpos = mul(unity_ObjectToWorld, v.vertex).xyz;//把顶点变换成世界空间 35 //对点光源进行反射的Unity的内建辅助函数 36 o.color.rgb += Shade4PointLights( 37 unity_4LightPosX0, 38 unity_4LightPosY0, 39 unity_4LightPosZ0, 40 unity_LightColor[0].rgb, 41 unity_LightColor[1].rgb, 42 unity_LightColor[2].rgb, 43 unity_LightColor[3].rgb, 44 unity_4LightAtten0, 45 wpos,N); 46 47 return o; 48 } 49 50 fixed4 frag(v2f IN) : COLOR 51 { 52 53 return IN.color + UNITY_LIGHTMODEL_AMBIENT;//加上了环境光照 54 } 55 56 ENDCG 57 } 58 59 } 60 FallBack "Diffuse" 61 }
镜面反射思路:镜面反射总的来说就是光线照射到平面以后再反射到我们摄像机的光的照射率。那么,我们可以假设我们的摄像机的点有一条法向量N,N的单位向量与反射光R的单位向量的点积:
dot(N, R) < 0,摄像机向量与反射光向量夹角大于90度;
dot(N, R) = 0,摄像机向量与反射光向量夹角等于90度;
dot(N, R) > 0 && dot(N, R) <= 1,摄像机向量与反射光向量夹角等于小于90度;
镜面反射代码:(切记,规范化向量,即单位向量一定不要忘记求)
Shader "ShaderLearn/Light/light_2" { Properties { _SpecularColor("SpecularColor", Color) = (1, 1, 1, 1) _Shininess ("Shininess", range(1, 64)) = 1 } SubShader { pass{ Tags{"LightMode" = "ForwardBase"}//光照模型,一定要写 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _SpecularColor; float _Shininess; struct v2f { float4 pos : POSITION; fixed4 color : COLOR; }; //在顶点计算光照效率高,片段程序比较慢,但是效果更好 //一个物体要计算光照颜色、点光源、环境光 v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex);//mvp float3 L = normalize(WorldSpaceLightDir(v.vertex));//平行光的法向量,顶点到光源方向 float3 N = UnityObjectToWorldNormal(v.normal);//法向量 float3 V = normalize(WorldSpaceViewDir(v.vertex));//从顶点到摄像机的向量 //ambient color o.color = UNITY_LIGHTMODEL_AMBIENT;//加上了环境光照 //diffuse color float ndotl = saturate(dot(N, L));//法线和光线的点积,光照强度 o.color += _LightColor0 * ndotl;//光照颜色 //specular color /*1.不使用cg函数reflect计算反射光 float3 wpos = mul(unity_ObjectToWorld, v.vertex); float3 I = -WorldSpaceLightDir(v.vertex);//光指向顶点 float3 R = reflect(I, N);//计算发射光向量*/ //2.自己实现reflect函数 //float3 R = 2 * dot(N, L) * N - L;//反着光向量 /*R = normalize(R); V = normalize(V);*/ //3.使用半角向量实现镜面高光 float3 H = L + V;//半角向量 H = normalize(H); //pow是为了让摄像机转向导致的镜面反射效果衰减更明显 //下面这句属于1和2方法的 //float specularScale = pow(saturate(dot(R, V)), _Shininess);//反射系数 //这句属于方法3的 float specularScale = pow(saturate(dot(H, N)), _Shininess);//反射系数 o.color.rgb += _LightColor0 * specularScale * _SpecularColor; return o; } fixed4 frag(v2f IN) : COLOR { return IN.color; } ENDCG } } FallBack "Diffuse" }
浙公网安备 33010602011771号