在一个基本模型里,一个物体表面的颜色是由放射(emissive)、环境反射(ambient)、漫反射(diffuse)和镜面反射(specular)等光照作用的总和。每种光照作用取决于表面材质的性质(如亮度和材质颜色)和光源的性质(如光的颜色和位置)的共同作用。
从数学上描述基本模型的高级公式如下所示:
surfaceColor = emissive +ambient + diffuse + specular
一、放射项
emissive = Ke
其中:
Ke代表材质的放射光颜色。
二、环境反射项
ambient = Ka * globalAmbient
其中:
Ka是材质的环境反射系数。
globalAmbient是入射环境光的颜色。
三、漫反射项
diffuse = Kd * lightColor * max(dot(N, L), 0)
其中:
Kd是材质的漫反射颜色。
lightColor是入射漫反射光的颜色。
N是规范化的表面法向量。
L是规范化的从顶点到光源的向量。
四、镜面反射项
specular = Ks * lightColor * facing * pow(max(dot(N, H), 0), shininess)
其中:
Ks 是材质的镜面反射颜色。
lightColor是入射镜面反射光的颜色。
N是规范化的表面法向量。
H是规范化的,顶点到光源的向量与顶点到眼睛的向量的中间向量。
facing是,如果dot(N,L)大于0则为1,否则为0。其中L是顶点到光源位置的向量。
shinniess是表面光泽度。
例如,在unity3d shaderLab,在顶点shader中计算光照的代码如下:
1 Shader "Custom/Test" 2 { 3 Properties 4 { 5 _Ke("Ke", Color) = (1,1,1,1) 6 _Ka("Ka", Color) = (1,1,1,1) 7 _GlobalAmbient("Global ambient", Color) = (1,1,1,1) 8 _Kd("Kd", Color) = (1,1,1,1) 9 _Ks("Ks", Color) = (1,1,1,1) 10 _Shininess("", float) = 1 11 } 12 13 SubShader 14 { 15 Pass 16 { 17 Tags 18 { 19 "RenderType" = "Opaque" 20 } 21 22 CGPROGRAM 23 #pragma vertex Vert 24 #pragma fragment Frag 25 26 #include "UnityCG.cginc" 27 #include "Lighting.cginc" 28 29 uniform float4 _Ke; 30 uniform float4 _Ka; 31 uniform float4 _GlobalAmbient; 32 uniform float4 _Kd; 33 uniform float4 _Ks; 34 uniform float _Shininess; 35 36 struct VertexInput 37 { 38 float4 pos : POSITION; 39 float2 uv : TEXCOORD0; 40 float3 nor : NORMAL; 41 float4 col : COLOR; 42 }; 43 44 struct FragmentInput 45 { 46 float4 pos : SV_POSITION; 47 float2 uv : TEXCOORD0; 48 float4 col : COLOR; 49 }; 50 51 FragmentInput Vert(VertexInput vi) 52 { 53 FragmentInput fi; 54 fi.pos = mul(UNITY_MATRIX_MVP, vi.pos); 55 fi.uv = vi.uv; 56 57 // compute emissive 58 float3 emissiveC = _Ke.rgb; 59 60 // compute ambient 61 float3 ambientC = _Ka.rgb * _GlobalAmbient.rgb; 62 63 // compute diffuse 64 float3 nor = mul(vi.nor, (float3x3)_World2Object); 65 float3 dir2Light = normalize(WorldSpaceLightDir(vi.pos)); 66 float nl = max(0, dot(nor, dir2Light)); 67 float3 diffuseC = _Kd.rgb * _LightColor0.rgb * nl; 68 69 // compute specular 70 float3 dir2Cam = normalize(WorldSpaceViewDir(vi.pos)); 71 float nh = max(0, dot(nor, dir2Cam + dir2Light)); 72 float specLight = nl > 0 ? pow(nh, _Shininess) : 0; 73 float3 specC = _Ks * _LightColor0.rgb * specLight; 74 75 fi.col.rgb = emissiveC + ambientC + diffuseC + specC; 76 fi.col.a = 1; 77 78 return fi; 79 } 80 81 float4 Frag(FragmentInput fi) : Color 82 { 83 return fi.col; 84 } 85 86 ENDCG 87 } 88 } 89 }
效果图如下: