Search Unity

How to switch from GGX to Blinn-Phong?

Discussion in 'Shaders' started by tmr2, Oct 27, 2019.

  1. tmr2

    tmr2

    Joined:
    Oct 27, 2019
    Posts:
    53
    Hi, I've read that as of version 5.3,the specular model was switched to GGX. I'm aware that I could use an older version of Unity, but a lot of assets on the asset store are incompatible with earlier versions. Do you know of simple of a way to switch to blinn-phong as the default specular model?

    (or possibly a way to make assets be compatiblemore with earlier unity versions?)
     
  2. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    1. Use specular from Legacy Shaders (for example Legacy Shaders / Bumped Specular), they use blinn-phong lightmodel.

    Or
    2. Modify Standard shader to replace GGX with blinn phong, file:
    Unity-Built-in-Shaders/CGIncludes/UnityStandardCore.cginc

    Or
    3. Modify custom PBR roughness shader (you have to use all four texture slots) , replace line "float3 specular = directSpecular + indirectSpecular;" with blinn-phong specular term equation.

    Code (CSharp):
    1. Shader "PBR specular roughness workflow"
    2. {
    3.     Properties
    4.     {
    5.         _Albedo ("Albedo Map (RGB)", 2D) = "white" {}
    6.         _BumpMap ("Normal Map (RGB)", 2D) = "bump" {}
    7.         _SpecularMap ("Specular Map (RGB)", 2D) = "white" {}
    8.         _Roughness ("Roughness Map (R)", 2D) = "black" {}
    9.     }
    10.     SubShader
    11.     {
    12.         Pass
    13.         {
    14.             Tags {"LightMode"="ForwardBase"}
    15.             CGPROGRAM
    16.             #pragma vertex vertex_shader
    17.             #pragma fragment pixel_shader
    18.             #pragma target 3.0
    19.             #define SHOULD_SAMPLE_SH ( defined (LIGHTMAP_OFF) && defined(DYNAMICLIGHTMAP_OFF) )
    20.             #include "UnityPBSLighting.cginc"
    21.             #pragma multi_compile_fwdbase_fullshadows
    22.             #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
    23.             #pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
    24.             #pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON
    25.             uniform sampler2D _BumpMap; uniform float4 _BumpMap_ST;
    26.             uniform sampler2D _SpecularMap; uniform float4 _SpecularMap_ST;
    27.             uniform sampler2D _Albedo; uniform float4 _Albedo_ST;
    28.             uniform sampler2D _Roughness; uniform float4 _Roughness_ST;
    29.            
    30.             struct VertexInput
    31.             {
    32.                 float4 vertex : POSITION;
    33.                 float3 normal : NORMAL;
    34.                 float4 tangent : TANGENT;
    35.                 float2 uv0 : TEXCOORD0;
    36.                 float2 uv1 : TEXCOORD1;
    37.                 float2 uv2 : TEXCOORD2;
    38.             };
    39.            
    40.             struct VertexOutput
    41.             {
    42.                 float4 vertex : SV_POSITION;
    43.                 float2 uv0 : TEXCOORD0;
    44.                 float2 uv1 : TEXCOORD1;
    45.                 float2 uv2 : TEXCOORD2;
    46.                 float4 posWorld : TEXCOORD3;
    47.                 float3 normalDir : TEXCOORD4;
    48.                 float3 tangentDir : TEXCOORD5;
    49.                 float3 bitangentDir : TEXCOORD6;      
    50.                 float4 lightmap : TEXCOORD7;            
    51.             };
    52.  
    53.             fixed3 UnpackNormals(fixed4 packednormal)
    54.             {
    55.                 packednormal.x *= packednormal.w;
    56.                 fixed3 normal;
    57.                 normal.xy = packednormal.xy * 2 - 1;
    58.                 normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
    59.                 return normal;
    60.             }
    61.                        
    62.             half VisibilitySmith(half NdotL, half NdotV, half roughness)
    63.             {
    64.                 half a = roughness;
    65.                 half a2  = a * a;
    66.                 half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
    67.                 half lambdaL  = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
    68.                 return 0.5f / (lambdaV + lambdaL + 1e-5f);
    69.             }
    70.            
    71.             half DistributionGGX(half NdotH, half roughness)
    72.             {
    73.                 half a2 = roughness * roughness;
    74.                 half d = (NdotH * a2 - NdotH) * NdotH + 1.0f;
    75.                 return  0.31830988618f * a2 / (d * d + 1e-7f);                                          
    76.             }
    77.  
    78.             half3 EnergyConservation (half3 albedo, half3 specular, out half oneMinusReflectivity)
    79.             {
    80.                 oneMinusReflectivity = 1.0 - max (max (specular.r, specular.g), specular.b);
    81.                 return albedo * oneMinusReflectivity;
    82.             }
    83.            
    84.             VertexOutput vertex_shader (VertexInput v)
    85.             {
    86.                 VertexOutput o = (VertexOutput)0;
    87.                 o.uv0 = v.uv0;
    88.                 #ifdef LIGHTMAP_ON
    89.                     o.lightmap.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    90.                     o.lightmap.zw = 0;
    91.                 #elif UNITY_SHOULD_SAMPLE_SH
    92.                 #endif
    93.                 #ifdef DYNAMICLIGHTMAP_ON
    94.                     o.lightmap.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
    95.                 #endif
    96.                 o.normalDir = UnityObjectToWorldNormal(v.normal);
    97.                 o.tangentDir = normalize( mul( unity_ObjectToWorld, float4( v.tangent.xyz, 0.0 ) ).xyz );
    98.                 o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);
    99.                 o.posWorld = mul(unity_ObjectToWorld, v.vertex);
    100.                 float3 lightColor = _LightColor0.rgb;
    101.                 o.vertex = UnityObjectToClipPos( v.vertex );
    102.                 return o;
    103.             }
    104.            
    105.             float4 pixel_shader (VertexOutput i) : COLOR
    106.             {
    107.                 i.normalDir = normalize(i.normalDir);
    108.                 float3x3 tangentTransform = float3x3( i.tangentDir, i.bitangentDir, i.normalDir);
    109.                 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
    110.                 float3 normalLocal = UnpackNormals(tex2D(_BumpMap,i.uv0));
    111.                 float3 normalDirection = normalize(mul( normalLocal, tangentTransform ));
    112.                 float3 viewReflectDirection = reflect( -viewDirection, normalDirection );
    113.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    114.                 float3 lightColor = _LightColor0.rgb;
    115.                 float3 halfDirection = normalize(viewDirection+lightDirection);
    116.                 float attenuation = 1.0;
    117.                 float3 attenColor = attenuation * _LightColor0.xyz;
    118.                 float Pi = 3.141592654;
    119.                 float InvPi = 0.31830988618;
    120.                 float4 _Roughness_var = tex2D(_Roughness,i.uv0);
    121.                 float gloss = 1.0 - _Roughness_var.r;
    122.                 float perceptualRoughness = _Roughness_var.r;
    123.                 float roughness = perceptualRoughness * perceptualRoughness;
    124.                 float specPow = exp2( gloss * 10.0 + 1.0 );
    125.                 UnityLight light;
    126.                 #ifdef LIGHTMAP_OFF
    127.                     light.color = lightColor;
    128.                     light.dir = lightDirection;
    129.                     light.ndotl = LambertTerm (normalDirection, light.dir);
    130.                 #else
    131.                     light.color = half3(0.f, 0.f, 0.f);
    132.                     light.ndotl = 0.0f;
    133.                     light.dir = half3(0.f, 0.f, 0.f);
    134.                 #endif
    135.                 UnityGIInput d;
    136.                 d.light = light;
    137.                 d.worldPos = i.posWorld.xyz;
    138.                 d.worldViewDir = viewDirection;
    139.                 d.atten = attenuation;
    140.                 #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
    141.                     d.ambient = 0;
    142.                     d.lightmapUV = i.lightmap;
    143.                 #else
    144.                     d.ambient = i.lightmap;
    145.                 #endif
    146.                 #if UNITY_SPECCUBE_BLENDING || UNITY_SPECCUBE_BOX_PROJECTION
    147.                     d.boxMin[0] = unity_SpecCube0_BoxMin;
    148.                     d.boxMin[1] = unity_SpecCube1_BoxMin;
    149.                 #endif
    150.                 #if UNITY_SPECCUBE_BOX_PROJECTION
    151.                     d.boxMax[0] = unity_SpecCube0_BoxMax;
    152.                     d.boxMax[1] = unity_SpecCube1_BoxMax;
    153.                     d.probePosition[0] = unity_SpecCube0_ProbePosition;
    154.                     d.probePosition[1] = unity_SpecCube1_ProbePosition;
    155.                 #endif
    156.                 d.probeHDR[0] = unity_SpecCube0_HDR;
    157.                 d.probeHDR[1] = unity_SpecCube1_HDR;
    158.                 Unity_GlossyEnvironmentData ugls_en_data;
    159.                 ugls_en_data.roughness = 1.0 - gloss;
    160.                 ugls_en_data.reflUVW = viewReflectDirection;
    161.                 UnityGI gi = UnityGlobalIllumination(d, 1, normalDirection, ugls_en_data );
    162.                 lightDirection = gi.light.dir;
    163.                 lightColor = gi.light.color;
    164.                 float NdotL = saturate(dot( normalDirection, lightDirection ));
    165.                 float LdotH = saturate(dot(lightDirection, halfDirection));
    166.                 float3 specularColor =tex2D(_SpecularMap,i.uv0).xyz;
    167.                 float specularMonochrome;
    168.                 float3 diffuseColor = tex2D(_Albedo,i.uv0).xyz;
    169.                 diffuseColor = EnergyConservation(diffuseColor, specularColor, specularMonochrome);
    170.                 specularMonochrome = 1.0-specularMonochrome;
    171.                 float NdotV = abs(dot( normalDirection, viewDirection ));
    172.                 float NdotH = saturate(dot( normalDirection, halfDirection ));
    173.                 float VdotH = saturate(dot( viewDirection, halfDirection ));
    174.                 float visTerm = VisibilitySmith( NdotL, NdotV, roughness );
    175.                 float normTerm = DistributionGGX(NdotH, roughness);
    176.                 float specularPBL = (visTerm*normTerm) * UNITY_PI;
    177.                 specularPBL = sqrt(max(1e-4h, specularPBL));
    178.                 specularPBL = max(0, specularPBL * NdotL);
    179.                 half s = 1.0-0.28*roughness*perceptualRoughness;
    180.                 specularPBL *= any(specularColor) ? 1.0 : 0.0;
    181.                 float3 directSpecular = attenColor*specularPBL*FresnelTerm(specularColor, LdotH);
    182.                 half grazingTerm = saturate( gloss + specularMonochrome );
    183.                 float3 indirectSpecular = (gi.indirect.specular)*FresnelLerp (specularColor, grazingTerm, NdotV);
    184.                 indirectSpecular *= s;
    185.                 float3 specular = directSpecular + indirectSpecular;
    186.                 NdotL = saturate(dot( normalDirection, lightDirection ));
    187.                 half fd90 = 0.5 + 2 * LdotH * LdotH * (1-gloss);
    188.                 float nlPow5 = Pow5(1-NdotL);
    189.                 float nvPow5 = Pow5(1-NdotV);
    190.                 float3 directDiffuse = ((1 +(fd90 - 1)*nlPow5) * (1 + (fd90 - 1)*nvPow5) * NdotL) * attenColor;
    191.                 float3 indirectDiffuse = gi.indirect.diffuse;
    192.                 diffuseColor *= 1-specularMonochrome;
    193.                 float3 color = (directDiffuse + indirectDiffuse)*diffuseColor+specular;
    194.                 return float4(color,1);
    195.             }
    196.             ENDCG
    197.         }
    198.     }
    199. }
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The older blinn-phong code is all still there. If you make a copy of the Standard.shader file you can add a single line to the forward and forward add passes to make it not use GGX.

    Add this under the
    #pragma target 3.0
    line:
    #define UNITY_BRDF_GGX 0


    Note: If you're using deferred rendering, you'll have to make a copy of the Internal-DeferredShading.shader and add that same line to it, then make sure that shader is being pointed to in the graphics settings.
     
  4. tmr2

    tmr2

    Joined:
    Oct 27, 2019
    Posts:
    53
    Do you know where the standard shader file is located? I think I tried opening the standard shader file in visual studio and at the top there was a comment that said something along the lines of "This is not a real shader"
    Should I just ignore it?


    Luckily I am using the forward rendered for my project.


    Where is the pbr file located?
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The answer to that is literally the top locked post for this forum.
     
  6. tmr2

    tmr2

    Joined:
    Oct 27, 2019
    Posts:
    53
    Thank you.