Jump to content

  • Log In with Google      Sign In   
  • Create Account

#ActualChris_F

Posted 25 February 2013 - 04:23 PM

The features that I think I need so far are: Non-lambertian diffuse, IOR/F(0º)/spec-mask, anisotropic roughness, metal/non-metal, retro-reflectiveness and translucency.

I took Chris_F's BRDF containing Cook-Torrence/Schlick/GGX/Smith and Oren-Nayar, and re-implemented it with hacked support for anisotropy (based roughly on Ashikhmin-Shirley) and retroreflectivity.

If both the roughness factors are equal (or if the isotropic bool is true), then the distribution should be the same as GGX, otherwise it behaves a bit like Ashikhmin-Shirley. Also, the distribution isn't properly normalized any more though when using anisotropic roughness.

The retro-reflectivity is a complete hack and won't be energy conserving. When the retro-reflectivity factor is set to 0.5, you get two specular lobes -- a regular reflected one, and one reflected back at the light source -- without any attempt to split the energy between them. At 0 you just get the regular specular lobe, and at 1 you only get the retro-reflected one.
 
BRDF Explorer file for anyone interested: http://pastebin.com/6ZpQGgpP
 
Thanks again for sending me on a weekend BRDF exploration quest, Chris and Promit biggrin.png

 

Actually, it's a lot easier to convert it to anisotropic than that.

 

 

analytic

::begin parameters
color Diffuse 1 0 0
color Specular 1 1 1
float DiffuseScale 0 1 0.5
float SpecularScale 0 0.999 .028
float RoughnessX 0.005 2 0.2
float RoughnessY 0.005 2 0.2
bool isotropic 1
::end parameters

::begin shader

float saturate(float x) { return clamp(x,0,1); }

vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
    float PI = 3.1415926535897932;
    vec3 Kd = Diffuse * DiffuseScale;
    vec3 Ks = Specular * SpecularScale;

    float ax = RoughnessX;
    float ay = (isotropic) ? RoughnessX : RoughnessY;

    vec3 H = normalize(L + V);
    float NdotL = saturate(dot(N, L));
    float NdotV = dot(N, V);
    float NdotH = dot(N, H);
    float LdotH = dot(L, H);
    float HdotX = dot(H, X);
    float HdotY = dot(H, Y);
    
    float ax_2 = ax * ax;
    float ay_2 = ay * ay;
    float a_2 = (ax_2 + ay_2) / 2;
    float NdotL_2 = NdotL * NdotL;
    float NdotV_2 = NdotV * NdotV;
    float NdotH_2 = NdotH * NdotH;
    float HdotX_2 = HdotX * HdotX;
    float HdotY_2 = HdotY * HdotY;
    float OneMinusNdotL_2 = 1.0 - NdotL_2;
    float OneMinusNdotV_2 = 1.0 - NdotV_2;

    vec3 Fd = 1.0 - Ks;

    float gamma = saturate(dot(V - N * NdotV, L - N * NdotL));
    float A = 1.0 - 0.5 * (a_2 / (a_2 + 0.33));
    float B = 0.45 * (a_2 / (a_2 + 0.09));
    float C = sqrt(OneMinusNdotL_2 * OneMinusNdotV_2) / max(NdotL, NdotV);
    float OrenNayar = A + B * gamma * C;

    vec3 Rd = (Kd / PI) * Fd * OrenNayar;

    float D = 1.0 / (PI * ax * ay * pow(HdotX_2 / ax_2 + HdotY_2 / ay_2 + NdotH_2, 2.0));

    vec3 Fs = Ks + Fd * exp(-6 * LdotH);

    float G1_1 = 2.0 / (1.0 + sqrt(1.0 + a_2 * (OneMinusNdotL_2 / NdotL_2)));
    float G1_2 = 2.0 / (1.0 + sqrt(1.0 + a_2 * (OneMinusNdotV_2 / NdotV_2)));
    float G = G1_1 * G1_2;

    vec3 Rs = (D * Fs * G) / (4 * NdotV * NdotL);

    return Rd + Rs;
}

::end shader

 

I left out the retro-reflection hack because this BRDF actually already exhibits a lot of retro-reflection. If you go to Image Slice in BRDF Explorer and look at the bottom edge, that is the retro part. This is probably a lot more physically plausible as far as retro-reflections go.


#1Chris_F

Posted 25 February 2013 - 12:29 PM

The features that I think I need so far are: Non-lambertian diffuse, IOR/F(0º)/spec-mask, anisotropic roughness, metal/non-metal, retro-reflectiveness and translucency.

I took Chris_F's BRDF containing Cook-Torrence/Schlick/GGX/Smith and Oren-Nayar, and re-implemented it with hacked support for anisotropy (based roughly on Ashikhmin-Shirley) and retroreflectivity.

If both the roughness factors are equal (or if the isotropic bool is true), then the distribution should be the same as GGX, otherwise it behaves a bit like Ashikhmin-Shirley. Also, the distribution isn't properly normalized any more though when using anisotropic roughness.

The retro-reflectivity is a complete hack and won't be energy conserving. When the retro-reflectivity factor is set to 0.5, you get two specular lobes -- a regular reflected one, and one reflected back at the light source -- without any attempt to split the energy between them. At 0 you just get the regular specular lobe, and at 1 you only get the retro-reflected one.
 
BRDF Explorer file for anyone interested: http://pastebin.com/6ZpQGgpP
 
Thanks again for sending me on a weekend BRDF exploration quest, Chris and Promit biggrin.png

 

Actually, it's a lot easier to convert it to anisotropic than that.

 

analytic

::begin parameters
color Diffuse 1 0 0
color Specular 1 1 1
float DiffuseScale 0 1 0.5
float SpecularScale 0 0.999 .028
float RoughnessX 0.005 2 0.2
float RoughnessY 0.005 2 0.2
bool isotropic 1
::end parameters

::begin shader

float saturate(float x) { return clamp(x,0,1); }

vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
    float PI = 3.1415926535897932;
    vec3 Kd = Diffuse * DiffuseScale;
    vec3 Ks = Specular * SpecularScale;

    float ax = RoughnessX;
    float ay = (isotropic) ? RoughnessX : RoughnessY;

    vec3 H = normalize(L + V);
    float NdotL = saturate(dot(N, L));
    float NdotV = dot(N, V);
    float NdotH = dot(N, H);
    float LdotH = dot(L, H);
    float HdotX = dot(H, X);
    float HdotY = dot(H, Y);
    
    float ax_2 = ax * ax;
    float ay_2 = ay * ay;
    float a_2 = (ax_2 + ay_2) / 2;
    float NdotL_2 = NdotL * NdotL;
    float NdotV_2 = NdotV * NdotV;
    float NdotH_2 = NdotH * NdotH;
    float HdotX_2 = HdotX * HdotX;
    float HdotY_2 = HdotY * HdotY;
    float OneMinusNdotL_2 = 1.0 - NdotL_2;
    float OneMinusNdotV_2 = 1.0 - NdotV_2;

    vec3 Fd = 1.0 - Ks;

    float gamma = saturate(dot(V - N * NdotV, L - N * NdotL));
    float A = 1.0 - 0.5 * (a_2 / (a_2 + 0.33));
    float B = 0.45 * (a_2 / (a_2 + 0.09));
    float C = sqrt(OneMinusNdotL_2 * OneMinusNdotV_2) / max(NdotL, NdotV);
    float OrenNayar = A + B * gamma * C;

    vec3 Rd = (Kd / PI) * Fd * OrenNayer;

    float D = 1.0 / (PI * ax * ay * pow(HdotX_2 / ax_2 + HdotY_2 / ay_2 + NdotH_2, 2.0));

    vec3 Fs = Ks + Fd * exp(-6 * LdotH);

    float G1_1 = 2.0 / (1.0 + sqrt(1.0 + a_2 * (OneMinusNdotL_2 / NdotL_2)));
    float G1_2 = 2.0 / (1.0 + sqrt(1.0 + a_2 * (OneMinusNdotV_2 / NdotV_2)));
    float G = G1_1 * G1_2;

    vec3 Rs = (D * Fs * G) / (4 * NdotV * NdotL);

    return Rd + Rs;
}

::end shader

 

I left out the retro-reflection hack because this BRDF actually already exhibits a lot of retro-reflection. If you go to Image Slice in BRDF Explorer and look at the bottom edge, that is the retro part. This is probably a lot more physically plausible as far as retro-reflections go.


PARTNERS