Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Cook-Torrance

Discussion in 'Shaders' started by Nose, Nov 15, 2012.

  1. Nose

    Nose

    Joined:
    Jun 21, 2011
    Posts:
    36
    Hello, who can help with the conversion of this shader in Unity?:(
    I found many examples of shaders, but, more correctly, I do not quite understand, here are some of them
    http://www.gamedev.ru/code/articles/Cook-Torrance
    http://steps3d.narod.ru/tutorials/lighting-tutorial.html
    http://content.gpwiki.org/index.php/D3DBook:%28Lighting%29_Cook-Torrance

    Code (csharp):
    1.  
    2.  
    3. float CookTorrance(vec3 _normal, vec3 _light, vec3 _view, float roughness_val)
    4. {
    5.  if (roughness_val <= 0.0) return 0.0;
    6. }
    7.    vec3  half_vec = normalize( _view + _light );
    8.    float NdotL    = max( dot( _normal, _light ), 0.0 );
    9.    float NdotV    = max( dot( _normal, _view ), 0.0 );
    10.    float NdotH    = max( dot( _normal, half_vec ), 1.0e-7 );
    11.    float VdotH    = max( dot( _view,   half_vec ), 0.0 );
    12.  
    13.    float geometric = 2.0 * NdotH / VdotH;
    14.    geometric = min( 1.0, geometric * min(NdotV, NdotL) );
    15.  
    16.    float r_sq          = roughness_val * roughness_val;
    17.    float NdotH_sq      = NdotH * NdotH;
    18.    float NdotH_sq_r    = 1.0 / (NdotH_sq * r_sq);
    19.    float roughness_exp = (NdotH_sq - 1.0) * ( NdotH_sq_r );
    20.    float roughness     = exp(roughness_exp) * NdotH_sq_r / (4.0 * NdotH_sq );
    21.    float fresnel       = 1.0 / (1.0 + NdotV);
    22.    Rs = min(1.0, (fresnel * geometric * roughness) / (NdotV * NdotL + 1.0e-7));
    23.  
    24.    vResult = vAmbient + LdotN  * (vDiffuse + vSpecular * Rs)
    25.  
    26.  
     
    Last edited: Nov 15, 2012
  2. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    cook-torrance is a lighting model, and its best you apply it as a custom lighting function in a surface shader.
    Check the unity shader include files in unity/data/cgincludes folder and there you will see some examples of how to make your own custom lighting functions.

    Thing is, cook-torrance is mathematically heavy and unnecessary for most cases.
     
  3. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    I think that should do it...

    Code to generate the Beckmann lookup texture can be found at the bottom of this page;
    http://www.altdevblogaday.com/2011/12/31/skin-shading-in-unity3d/

    Roughness slider:
    Code (csharp):
    1.  
    2. Shader "Custom/CookTorrance" {
    3.     Properties {
    4.         _Color ("Main Color", Color) = (1,1,1,1)
    5.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    6.         _Roughness ("Roughness", Range(0,1)) = 0.5
    7.         _BumpMap ("Normal (Normal)", 2D) = "bump" {}
    8.         _Beckmann ("Beckmann Lookup (RGB)", 2D) = "gray" {}
    9.         _Fresnel ("Fresnel Value", Float) = 0.028
    10.         _Cutoff ("Alpha Cut-Off Threshold", Range(0,1)) = 0.5
    11.     }
    12.  
    13.     SubShader{
    14.         Tags { "Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
    15.  
    16.         CGPROGRAM
    17.  
    18.             #pragma surface surf CookTorrance exclude_path:prepass nolightmap nodirlightmap fullforwardshadows
    19.             #pragma target 3.0
    20.  
    21.             struct Input
    22.             {
    23.                 float2 uv_MainTex;
    24.             };
    25.  
    26.             sampler2D _MainTex, _BumpMap, _Beckmann;
    27.             float _Fresnel, _Cutoff, _Roughness;
    28.  
    29.             void surf (Input IN, inout SurfaceOutput o)
    30.             {
    31.                 fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
    32.                 o.Albedo = albedo.rgb;
    33.                 o.Alpha = albedo.a;
    34.  
    35.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    36.             }
    37.  
    38.             inline fixed4 LightingCookTorrance (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten)
    39.             {
    40.                 clip ( s.Alpha - _Cutoff );
    41.  
    42.                 viewDir = normalize ( viewDir );
    43.                 lightDir = normalize ( lightDir );
    44.                 float3 h = normalize ( lightDir + viewDir );
    45.                 float NdotL_unsat = dot ( s.Normal, lightDir );
    46.                 float NdotH_unsat = dot ( s.Normal, h );
    47.                 float NdotL = saturate( NdotL_unsat );
    48.                 float NdotH = saturate( NdotH_unsat );
    49.                 float NdotV = saturate( dot ( s.Normal, viewDir ) );
    50.                 float VdotH = saturate( dot ( viewDir, h ) );
    51.  
    52.                 float geo_numerator = 2.0 * NdotH;
    53.                 float geo_b = ( geo_numerator * NdotV ) / VdotH;
    54.                 float geo_c = ( geo_numerator * NdotL ) / VdotH;
    55.                 float geo = min( 1.0f, min( geo_b, geo_c ) );
    56.  
    57.                 fixed roughness = tex2D( _Beckmann, float2 ( NdotH_unsat * 0.5 + 0.5, _Roughness ) ).r;
    58.  
    59.                 float fresnel = pow( 1.0 - VdotH, 5.0 );
    60.                 fresnel *= ( 1.0 - _Fresnel );
    61.                 fresnel += _Fresnel;
    62.  
    63.                 float3 spec = float3 ( fresnel * geo * roughness ) / ( NdotV * NdotL );
    64.  
    65.                 fixed4 c;
    66.                 c.rgb = NdotL * (  _LightColor0.rgb * spec + s.Albedo ) * atten;
    67.                 c.a = s.Alpha;
    68.                 return c;
    69.             }
    70.  
    71.         ENDCG
    72.     }
    73.     FallBack "Transparent/Cutout/VertexLit"
    74. }
    75.  
    Rougness texture:
    Code (csharp):
    1.  
    2. Shader "Custom/CookTorrance" {
    3.     Properties {
    4.         _Color ("Main Color", Color) = (1,1,1,1)
    5.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    6.         _Roughness ("Roughness (R)", 2D) = "gray" {}
    7.         _BumpMap ("Normal (Normal)", 2D) = "bump" {}
    8.         _Beckmann ("Beckmann Lookup (RGB)", 2D) = "gray" {}
    9.         _Fresnel ("Fresnel Value", Float) = 0.028
    10.         _Cutoff ("Alpha Cut-Off Threshold", Range(0,1)) = 0.5
    11.     }
    12.  
    13.     SubShader{
    14.         Tags { "Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
    15.  
    16.         CGPROGRAM
    17.  
    18.             #pragma surface surf CookTorrance exclude_path:prepass nolightmap nodirlightmap fullforwardshadows
    19.             #pragma target 3.0
    20.  
    21.             struct Input
    22.             {
    23.                 float2 uv_MainTex;
    24.             };
    25.  
    26.             sampler2D _MainTex, _BumpMap, _Beckmann, _Roughness;
    27.             float _Fresnel, _Cutoff;
    28.  
    29.             void surf (Input IN, inout SurfaceOutput o)
    30.             {
    31.                 fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
    32.                 o.Albedo = albedo.rgb;
    33.                 o.Alpha = albedo.a;
    34.                 o.Specular = tex2D(_Roughness, IN.uv_MainTex).r;
    35.  
    36.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    37.             }
    38.  
    39.             inline fixed4 LightingCookTorrance (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten)
    40.             {
    41.                 clip ( s.Alpha - _Cutoff );
    42.  
    43.                 viewDir = normalize ( viewDir );
    44.                 lightDir = normalize ( lightDir );
    45.                 float3 h = normalize ( lightDir + viewDir );
    46.                 float NdotL_unsat = dot ( s.Normal, lightDir );
    47.                 float NdotH_unsat = dot ( s.Normal, h );
    48.                 float NdotL = saturate( NdotL_unsat );
    49.                 float NdotH = saturate( NdotH_unsat );
    50.                 float NdotV = saturate( dot ( s.Normal, viewDir ) );
    51.                 float VdotH = saturate( dot ( viewDir, h ) );
    52.  
    53.                 float geo_numerator = 2.0 * NdotH;
    54.                 float geo_b = ( geo_numerator * NdotV ) / VdotH;
    55.                 float geo_c = ( geo_numerator * NdotL ) / VdotH;
    56.                 float geo = min( 1.0f, min( geo_b, geo_c ) );
    57.  
    58.                 fixed roughness = tex2D( _Beckmann, float2 ( NdotH_unsat * 0.5 + 0.5, s.Specular ) ).r;
    59.  
    60.                 float fresnel = pow( 1.0 - VdotH, 5.0 );
    61.                 fresnel *= ( 1.0 - _Fresnel );
    62.                 fresnel += _Fresnel;
    63.  
    64.                 float3 spec = float3 ( fresnel * geo * roughness ) / ( NdotV * NdotL );
    65.  
    66.                 fixed4 c;
    67.                 c.rgb = NdotL * (  _LightColor0.rgb * spec + s.Albedo ) * atten;
    68.                 c.a = s.Alpha;
    69.                 return c;
    70.             }
    71.  
    72.         ENDCG
    73.     }
    74.     FallBack "Transparent/Cutout/VertexLit"
    75. }
    76.  
     
    Last edited: Nov 21, 2012
  4. Nose

    Nose

    Joined:
    Jun 21, 2011
    Posts:
    36
    This solution is not entirely correct. No need to use any textures except diffuse.
    Specular And Roughness should be managed by sliders or float
     
  5. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    If you want those values per-material, sure. If you want parts of your texture to be glossy and others to be matte, then... no.

    It shouldn't take you more than 1 minute to swap it out for sliders, though.

    I've edited it to make the version you want.
     
    Last edited: Nov 19, 2012
  6. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    What do you mean by correct?

    Uniform reflectance and roughness across the whole model would be unfortunately restrictive use of such a complex lighting model. Farfarer's use of textures for these values lets you have smoother, rougher, more and less reflective areas on the same model.

    The Beckmann lookup texture is a good trade of a tiny bit of VRAM for a constant speedup in the fragment shader. Two of your three links use this texture.
     
  7. lazygunn

    lazygunn

    Joined:
    Jul 24, 2011
    Posts:
    2,749
    Using textures would make far more sense, now i have to convert it back, thankyou for the example Farfarer your contributions to Unity shaders have been really useful for me in these forums
     
  8. Nose

    Nose

    Joined:
    Jun 21, 2011
    Posts:
    36
    I found the correct shader, and I was able to convert it to Unity3D,
    I was very happy with the result.
     
  9. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Isn't that what the one I wrote did?
     
  10. Nose

    Nose

    Joined:
    Jun 21, 2011
    Posts:
    36
    No) I use one texture, duffuse
     
  11. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Suit yourself.
     
  12. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Just in case anyone comes across this thread in the future, it's worth noting that there is nothing incorrect about Farfarer's implementation. It's just faster and more versatile than the version Nose wanted.
     
  13. Nose

    Nose

    Joined:
    Jun 21, 2011
    Posts:
    36
    I agree, the only difference is in the use of Beckman, I just bad speak English
     
  14. lazygunn

    lazygunn

    Joined:
    Jul 24, 2011
    Posts:
    2,749
    Indeed i've set out to replicate Nose's discovery because sometimes the good isnt bad enough
     
  15. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Can you explain?
     
  16. lazygunn

    lazygunn

    Joined:
    Jul 24, 2011
    Posts:
    2,749
    Haha sorry i was being a smartass and agreeing with you with a silly joke, dont mind me!

    Thankyou for supplying both Farfarer, im sure id have figured it but im at the point of learning shaders (rewriting other peoples and smooshing them together mainly) where being able to compare is very handy