标准实现参考Unity Standard Shader源码阅读(一) BasePass
着色器定义在UnityStandardCoreForward.cginc中:
#include "UnityStandardConfig.cginc"
#if UNITY_STANDARD_SIMPLE
//简化版实现
#include "UnityStandardCoreForwardSimple.cginc"
VertexOutputBaseSimple vertBase (VertexInput v) { return vertForwardBaseSimple(v); }
VertexOutputForwardAddSimple vertAdd (VertexInput v) { return vertForwardAddSimple(v); }
half4 fragBase (VertexOutputBaseSimple i) : SV_Target { return fragForwardBaseSimpleInternal(i); }
half4 fragAdd (VertexOutputForwardAddSimple i) : SV_Target { return fragForwardAddSimpleInternal(i); }
#else
//标准实现
#include "UnityStandardCore.cginc"
VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
VertexOutputForwardAdd vertAdd (VertexInput v) { return vertForwardAdd(v); }
half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }
half4 fragAdd (VertexOutputForwardAdd i) : SV_Target { return fragForwardAddInternal(i); }
#endif
先看顶点着色器:
VertexOutputBaseSimple vertForwardBaseSimple (VertexInput v)
{
UNITY_SETUP_INSTANCE_ID(v);
VertexOutputBaseSimple o;
UNITY_INITIALIZE_OUTPUT(VertexOutputBaseSimple, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
o.tex = TexCoords(v);
//世界空间眼睛观察方向,指向观察的顶点
half3 eyeVec = normalize(posWorld.xyz - _WorldSpaceCameraPos);
half3 normalWorld = UnityObjectToWorldNormal(v.normal);
o.normalWorld.xyz = normalWorld;
o.eyeVec.xyz = eyeVec;
//在切线空间中计算光照
#ifdef _NORMALMAP
half3 tangentSpaceEyeVec;
TangentSpaceLightingInput(normalWorld, v.tangent, _WorldSpaceLightPos0.xyz, eyeVec, o.tangentSpaceLightDir, tangentSpaceEyeVec);
#if SPECULAR_HIGHLIGHTS
o.tangentSpaceEyeVec = tangentSpaceEyeVec;
#endif
#endif
//We need this for shadow receiving
TRANSFER_SHADOW(o);
//计算顶点球谐光照或用于存储光照贴图uv
o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
//视线在顶点反射方向
o.fogCoord.yzw = reflect(eyeVec, normalWorld);
//菲涅尔系数
o.normalWorld.w = Pow4(1 - saturate(dot(normalWorld, -eyeVec))); // fresnel term
#if !GLOSSMAP
//射掠角观察顶点时的反射系数
o.eyeVec.w = saturate(_Glossiness + UNIFORM_REFLECTIVITY()); // grazing term
#endif
UNITY_TRANSFER_FOG(o, o.pos);
return o;
}
该实现在切线空间计算光照:
//获取切线空间下光源方向,观察方向
void TangentSpaceLightingInput(half3 normalWorld, half4 vTangent, half3 lightDirWorld, half3 eyeVecWorld, out half3 tangentSpaceLightDir, out half3 tangentSpaceEyeVec)
{
half3 tangentWorld = UnityObjectToWorldDir(vTangent.xyz);
half sign = half(vTangent.w) * half(unity_WorldTransformParams.w);
half3 binormalWorld = cross(normalWorld, tangentWorld) * sign;
tangentSpaceLightDir = TransformToTangentSpace(tangentWorld, binormalWorld, normalWorld, lightDirWorld);
#if SPECULAR_HIGHLIGHTS
tangentSpaceEyeVec = normalize(TransformToTangentSpace(tangentWorld, binormalWorld, normalWorld, eyeVecWorld));
#else
tangentSpaceEyeVec = 0;
#endif
}
//从世界空间变换到切线空间
half3 TransformToTangentSpace(half3 tangent, half3 binormal, half3 normal, half3 v)
{
// Mali400 shader compiler prefers explicit dot product over using a half3x3 matrix
//传入切线空间各坐标轴在世界空间中的表示,对其按行排列构成变换矩阵
return half3(dot(tangent, v), dot(binormal, v), dot(normal, v));
}
再看片元着色器:
half4 fragForwardBaseSimpleInternal (VertexOutputBaseSimple i)
{
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
FragmentCommonData s = FragmentSetupSimple(i);
UnityLight mainLight = MainLightSimple(i, s);
//法线方向上的光
#if !defined(LIGHTMAP_ON) && defined(_NORMALMAP)
half ndotl = saturate(dot(s.tangentSpaceNormal, i.tangentSpaceLightDir));
#else
half ndotl = saturate(dot(s.normalWorld, mainLight.dir));
#endif
//we can't have worldpos here (not enough interpolator on SM 2.0) so no shadow fade in that case.
half shadowMaskAttenuation = UnitySampleBakedOcclusion(i.ambientOrLightmapUV, 0);
half realtimeShadowAttenuation = SHADOW_ATTENUATION(i);
half atten = UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, 0);
half occlusion = Occlusion(i.tex.xy);
//视角与光线直接的夹角,用于高光反射计算
half rl = dot(REFLECTVEC_FOR_SPECULAR(i, s), LightDirForSpecular(i, mainLight));
//计算直接光源和间接光源
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
half3 attenuatedLightColor = gi.light.color * ndotl;
half3 c = BRDF3_Indirect(s.diffColor, s.specColor, gi.indirect, PerVertexGrazingTerm(i, s), PerVertexFresnelTerm(i));
c += BRDF3DirectSimple(s.diffColor, s.specColor, s.smoothness, rl) * attenuatedLightColor;
c += Emission(i.tex.xy);
UNITY_APPLY_FOG(i.fogCoord, c);
return OutputForward (half4(c, 1), s.alpha);
}
展开FragmentSetupSimple:
FragmentCommonData FragmentSetupSimple(VertexOutputBaseSimple i)
{
half alpha = Alpha(i.tex.xy);
#if defined(_ALPHATEST_ON)
clip (alpha - _Cutoff);
#endif
FragmentCommonData s = UNITY_SETUP_BRDF_INPUT (i.tex);
// NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
s.diffColor = PreMultiplyAlpha (s.diffColor, alpha, s.oneMinusReflectivity, /*out*/ s.alpha);
s.normalWorld = i.normalWorld.xyz;
s.eyeVec = i.eyeVec.xyz;
s.posWorld = IN_WORLDPOS(i);
s.reflUVW = i.fogCoord.yzw;
#ifdef _NORMALMAP
//切线空间中法线
s.tangentSpaceNormal = NormalInTangentSpace(i.tex);
#else
s.tangentSpaceNormal = 0;
#endif
return s;
}
对于UNITY_SETUP_BRDF_INPUT,设置BRDF输入,包含漫反射颜色diffColor,高光反射颜色specColor,漫反射部分系数oneMinusReflectivity,光滑度smoothness,按工作流分为MetallicSetup,SpecularSetup,RoughnessSetup;
Standard shader使用金属工作流 MetallicSetup:
inline FragmentCommonData MetallicSetup (float4 i_tex)
{
half2 metallicGloss = MetallicGloss(i_tex.xy);
half metallic = metallicGloss.x;
half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
half oneMinusReflectivity;
half3 specColor;
half3 diffColor = DiffuseAndSpecularFromMetallic (Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
FragmentCommonData o = (FragmentCommonData)0;
o.diffColor = diffColor;
o.specColor = specColor;
o.oneMinusReflectivity = oneMinusReflectivity;
o.smoothness = smoothness;
return o;
}
inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
return albedo * oneMinusReflectivity;
}
inline half OneMinusReflectivityFromMetallic(half metallic)
{
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
//金属度越高,漫反射部分越弱
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
对于高光工作流:
inline FragmentCommonData SpecularSetup (float4 i_tex)
{
half4 specGloss = SpecularGloss(i_tex.xy);
half3 specColor = specGloss.rgb;
half smoothness = specGloss.a;
half oneMinusReflectivity;
half3 diffColor = EnergyConservationBetweenDiffuseAndSpecular (Albedo(i_tex), specColor, /*out*/ oneMinusReflectivity);
FragmentCommonData o = (FragmentCommonData)0;
o.diffColor = diffColor;
o.specColor = specColor;
o.oneMinusReflectivity = oneMinusReflectivity;
o.smoothness = smoothness;
return o;
}
// Diffuse/Spec Energy conservation
inline half3 EnergyConservationBetweenDiffuseAndSpecular (half3 albedo, half3 specColor, out half oneMinusReflectivity)
{
oneMinusReflectivity = 1 - SpecularStrength(specColor);
#if !UNITY_CONSERVE_ENERGY
return albedo;
#elif UNITY_CONSERVE_ENERGY_MONOCHROME
return albedo * oneMinusReflectivity;
#else
return albedo * (half3(1,1,1) - specColor);
#endif
}
half SpecularStrength(half3 specular)
{
#if (SHADER_TARGET < 30)
// SM2.0: instruction count limitation
// SM2.0: simplified SpecularStrength
return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint
#else
return max (max (specular.r, specular.g), specular.b);
#endif
}
对于PreMultiplyAlpha,diffColor 预乘alpha值,以及考虑高光反射会移除部分透明:
inline half3 PreMultiplyAlpha (half3 diffColor, half alpha, half oneMinusReflectivity, out half outModifiedAlpha)
{
#if defined(_ALPHAPREMULTIPLY_ON)
// NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
// Transparency 'removes' from Diffuse component
diffColor *= alpha;
#if (SHADER_TARGET < 30)
// SM2.0: instruction count limitation
// Instead will sacrifice part of physically based transparency where amount Reflectivity is affecting Transparency
// SM2.0: uses unmodified alpha
outModifiedAlpha = alpha;
#else
// Reflectivity 'removes' from the rest of components, including Transparency
//显示部分 = 1 - 透明部分 * (1 - 高光反射)
// outAlpha = 1-(1-alpha)*(1-reflectivity) = 1-(oneMinusReflectivity - alpha*oneMinusReflectivity) =
// = 1-oneMinusReflectivity + alpha*oneMinusReflectivity
//最终显示 = 高光占比 + alpha * 漫反射占比
outModifiedAlpha = 1-oneMinusReflectivity + alpha*oneMinusReflectivity;
#endif
#else
outModifiedAlpha = alpha;
#endif
return diffColor;
}
最后看BRDF实现:
half3 BRDF3_Indirect(half3 diffColor, half3 specColor, UnityIndirect indirect, half grazingTerm, half fresnelTerm)
{
half3 c = indirect.diffuse * diffColor;
c += indirect.specular * lerp (specColor, grazingTerm, fresnelTerm);
return c;
}
half3 BRDF3DirectSimple(half3 diffColor, half3 specColor, half smoothness, half rl)
{
#if SPECULAR_HIGHLIGHTS
return BRDF3_Direct(diffColor, specColor, Pow4(rl), smoothness);
#else
return diffColor;
#endif
}
sampler2D_float unity_NHxRoughness;
half3 BRDF3_Direct(half3 diffColor, half3 specColor, half rlPow4, half smoothness)
{
half LUT_RANGE = 16.0; // must match range in NHxRoughness() function in GeneratedTextures.cpp
// Lookup texture to save instructions
half specular = tex2D(unity_NHxRoughness, half2(rlPow4, SmoothnessToPerceptualRoughness(smoothness))).r * LUT_RANGE;
#if defined(_SPECULARHIGHLIGHTS_OFF)
specular = 0.0;
#endif
return diffColor + specular * specColor;
}