[先行版]基础渲染光照介绍
Lambert
漫反射:lambert数值为法线和光照的点乘
float3 worldLight = normalize(_WorldSpaceLightPos0.xyz) float3 worldNormal = normalize(i.worldNormal)
float NdotL = max(0.0, dot(finiNormal, worldLight))
|
Phong
环境光+漫反射+高光
高光:光照颜色高光反射颜色视线方向与反射方向的点乘的gloss次幂
Gloss用于控制高光反射范围
float3 reflectDir = normalize(reflect(-worldLight, finiNormal)); float3 viewDir = normalzie(_WorldSpaceCameraPos.xyz - i.worldPOs.xyz); float VdotR = max(0.0, dor(reflectDir, viewDir); float3 specular = _LightColor0.rgb * _Specular.rgb * pow(VdotR, _Gloss);
float3 color = diffuse + ambient + specular;
|
Blinn-Phong
与Phong模型的区别在于计算specular时,使用的是半角向量而非反射向量
比起Phong模型,计算效率提升至少三分之一以上
float3 halfDir = normalize(worldLight + viewDir); float3 NdotH = saturate(dot(halfDir, viewDir); float specular = _LightColor0.rgb * _Specular.rgb * pow(NdotH, _Gloss);
|
法线贴图
UnpackNormal:空间转换(【0-1】空间→【-1-1】空间)
Unity内置的法线转换:
inline fixed3 UnpackNormalDXT5nm(fixed4 packednormal) { fixed3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; }
inline fixd3 UnpackNormal(fixed4 packednormal) { ##ifdef UNITY_NO_DXT5nm return packednormal.xyz * 2 - 1; ##els return UnpackNormalDXT5nm(packednormal); ##endif }
|
在世界空间计算光照:计算切线空间→世界空间的变换矩阵,在frag中读取法线贴图然后将切线空间的法线信息转换到世界空间
法线的空间变换(法线空间→世界空间):
struct a2v { float3 normal : NORMAL; float4 tangent : TANGENT; }
struct v2f { float3 worldNormalDir : TEXCOORD1; float4 worldTangentDir : TEXCOORD2; float3 worldbiTangentDir : TEXCOORD3; }
v2f vert(a2v v) { o.worldNormalDir = UnityObjectToWorldNormal(v.normal); o.worldTangentDir = normalize(mul(unity_ObjectToWorld, float4( v.tangent.xyz, 0.0)).xyz); o.worldbiTangentDir = normalize(cross(o.worldNormalDir, o.worldTangentDir) * v.tangent.w); }
fixed4 frag(v2f i) : SV_Target { float3x3 tangentTransform = float3x3(i.worldTangentDir, i.worldbiTangentDir, i.worldNormalDir); float3 normalLocal = UnpackNormal(tex2D(_NormalMap, TRANSFORM_TEX(i.uv0, _NormalMap))); float3 normalWorld = normalize(mul(normalLocal.rgb, tangentTransform)); }
|
CUBE Map
做反射效果、环境光效果时会用到CUBE Map
用反射向量来读取CUBE Map
float3 worldRefl = normalize(reflect(-viewDir, finiNormal));
float4 refCol = texCUBElod(_Cubemap, float4(worldRefl.rgb, (255-_Gloss)*8/(255)))*_EnvScale;
|
IBL
IBL:image based lighting,基于图像的光照,把一张图当成光源来加入物体的光照计算中
通常会用到texCUBElod来模拟PBR中粗糙度的表现(同上方CUBE Map中对_Gloss的解释,只不过换成了对光的反射率/显示出的亮度)
处理漫反射暗面死黑的trick
通过插值计算漫反射(暗部以环境光作为光源,亮部以真是光源作为光源)
float3 diffuse = lerp(ambient.rgb*_Diffuse.rgb*MainTex.rgb, _LightColor.rgb*_Diffuse.rgb*MainTex.rgb, NdotL);
|
有金属质感,同时兼具非金属质感的trick
高光计算的trick
模拟PBR效果:通过gloss的值对specular和diffuse进行插值,当Gloss很高(金属质感)时高光颜色与漫反射无关,当Gloss很低时(非金属质感)高光颜色会被漫反射影响
float3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(NdotH, _Gloss); specular = lerp(diffuse * specular, specular, _Gloss/255);
|
环境映射的trick
和高光计算同理
reflColor = lerp(diffuse * reflColor, reflColor, _Gloss/255);
|
很不标准,不建议用,只是一种强行对PBR的仿制