下载Unity官方的提供的Shader资源,里面有个文件叫Standar.shader 就是Unity5.0的之后的用的基于BDRF的shader源代码。
里面包含的多个SubShader 和Pass 这里我们挑 Shader2.0 和 ForwardBase Pass 具体Shader2.0 和ForwardBase Pass 后面文章说。
File:Standard.shader
Pass { Name "FORWARD" Tags { "LightMode" = "ForwardBase" } Blend [_SrcBlend] [_DstBlend] ZWrite [_ZWrite] CGPROGRAM #pragma target 2.0 #pragma shader_feature _NORMALMAP #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON #pragma shader_feature _EMISSION #pragma shader_feature _METALLICGLOSSMAP #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A #pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF #pragma shader_feature _ _GLOSSYREFLECTIONS_OFF // SM2.0: NOT SUPPORTED shader_feature ___ _DETAIL_MULX2 // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED #pragma multi_compile_fwdbase #pragma multi_compile_fog #pragma vertex vertBase #pragma fragment fragBase #include "UnityStandardCoreForward.cginc" ENDCG }
这里看到,代码里面
#pragma vertex vertBase #pragma fragment fragBase
可以看到顶点着色器和像素着色器
File:UnityStandardCoreForward.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
这里分为两个分支,简单的和标准,我们这里看标准的。
VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }
File:UnityStandardCore.cginc
VertexOutputForwardBase vertForwardBase (VertexInput v) { UNITY_SETUP_INSTANCE_ID(v); VertexOutputForwardBase o; UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); float4 posWorld = mul(unity_ObjectToWorld, v.vertex); #if UNITY_REQUIRE_FRAG_WORLDPOS #if UNITY_PACK_WORLDPOS_WITH_TANGENT o.tangentToWorldAndPackedData[0].w = posWorld.x; o.tangentToWorldAndPackedData[1].w = posWorld.y; o.tangentToWorldAndPackedData[2].w = posWorld.z; #else o.posWorld = posWorld.xyz; #endif #endif o.pos = UnityObjectToClipPos(v.vertex); o.tex = TexCoords(v); o.eyeVec = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos); float3 normalWorld = UnityObjectToWorldNormal(v.normal); #ifdef _TANGENT_TO_WORLD float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w); float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w); o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0]; o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1]; o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2]; #else o.tangentToWorldAndPackedData[0].xyz = 0; o.tangentToWorldAndPackedData[1].xyz = 0; o.tangentToWorldAndPackedData[2].xyz = normalWorld; #endif //We need this for shadow receving UNITY_TRANSFER_SHADOW(o, v.uv1); o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld); #ifdef _PARALLAXMAP TANGENT_SPACE_ROTATION; half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex)); o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x; o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y; o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z; #endif UNITY_TRANSFER_FOG(o,o.pos); return o; }
half4 fragForwardBaseInternal (VertexOutputForwardBase i) { UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy); FRAGMENT_SETUP(s) UNITY_SETUP_INSTANCE_ID(i); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); UnityLight mainLight = MainLight (); UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld); half occlusion = Occlusion(i.tex.xy); UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight); half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect); c.rgb += Emission(i.tex.xy); UNITY_APPLY_FOG(i.fogCoord, c.rgb); return OutputForward (c, s.alpha); }
像素着色器中UNITY_BRDF_PBS 这个函数有三挡的分支
File:UnityPBSLighting.cginc
// Default BRDF to use: #if !defined (UNITY_BRDF_PBS) // allow to explicitly override BRDF in custom shader // still add safe net for low shader models, otherwise we might end up with shaders failing to compile #if SHADER_TARGET < 30 #define UNITY_BRDF_PBS BRDF3_Unity_PBS #elif defined(UNITY_PBS_USE_BRDF3) #define UNITY_BRDF_PBS BRDF3_Unity_PBS #elif defined(UNITY_PBS_USE_BRDF2) #define UNITY_BRDF_PBS BRDF2_Unity_PBS #elif defined(UNITY_PBS_USE_BRDF1) #define UNITY_BRDF_PBS BRDF1_Unity_PBS #elif defined(SHADER_TARGET_SURFACE_ANALYSIS) // we do preprocess pass during shader analysis and we dont actually care about brdf as we need only inputs/outputs #define UNITY_BRDF_PBS BRDF1_Unity_PBS #else #error something broke in auto-choosing BRDF #endif #endif
我们看完全体的
UNITY_BRDF_PBS BRDF1_Unity_PBS 函数 在文件 UnityStandardBRDF.cginc中
File:UnityStandardBRDF.cginc
half4 BRDF1_Unity_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness, half3 normal, half3 viewDir, UnityLight light, UnityIndirect gi) { half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness); half3 halfDir = Unity_SafeNormalize (light.dir + viewDir); // NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping // In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts. // but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too). // Following define allow to control this. Set it to 0 if ALU is critical on your platform. // This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface // Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree. #define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0 #if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV // The amount we shift the normal toward the view vector is defined by the dot product. half shiftAmount = dot(normal, viewDir); normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal; // A re-normalization should be applied here but as the shift is small we don't do it to save ALU. //normal = normalize(normal); half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here #else half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact #endif half nl = saturate(dot(normal, light.dir)); half nh = saturate(dot(normal, halfDir)); half lv = saturate(dot(light.dir, viewDir)); half lh = saturate(dot(light.dir, halfDir)); // Diffuse term half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl; // Specular term // HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm! // BUT 1) that will make shader look significantly darker than Legacy ones // and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH half roughness = PerceptualRoughnessToRoughness(perceptualRoughness); #if UNITY_BRDF_GGX // GGX with roughtness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughtness remapping. roughness = max(roughness, 0.002); half V = SmithJointGGXVisibilityTerm (nl, nv, roughness); half D = GGXTerm (nh, roughness); #else // Legacy half V = SmithBeckmannVisibilityTerm (nl, nv, roughness); half D = NDFBlinnPhongNormalizedTerm (nh, PerceptualRoughnessToSpecPower(perceptualRoughness)); #endif half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later # ifdef UNITY_COLORSPACE_GAMMA specularTerm = sqrt(max(1e-4h, specularTerm)); # endif // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value specularTerm = max(0, specularTerm * nl); #if defined(_SPECULARHIGHLIGHTS_OFF) specularTerm = 0.0; #endif // surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1) half surfaceReduction; # ifdef UNITY_COLORSPACE_GAMMA surfaceReduction = 1.0-0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1] # else surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1] # endif // To provide true Lambert lighting, we need to be able to kill specular completely. specularTerm *= any(specColor) ? 1.0 : 0.0; half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity)); half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm) + specularTerm * light.color * FresnelTerm (specColor, lh) + surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv); return half4(color, 1); }