Your preferred or desired BRDF?

Started by
50 comments, last by Hodgman 11 years, 2 months ago

Shaders do need NdotL. Division by PI should be precalculated and sent to the shader—IE the light values the shader receives should already have been divided by PI.

I don't really agree with that as general-case advice...it only makes sense for BRDF's that have a 1/pi term in them which isn't always the case.

Advertisement

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

If there were absolutely no limits I would like to evaluate spatially-varying and measured bidirectional texture functions. They show up a lot in my inverse rendering research and the results can look very realistic. Storing them in wavelet format makes them somewhat tractable and convenient to work with, but the system requirements add up rather quickly.

If you are interested in a good overview of the semi-standard lighting models, take a look in the Lighting section of Programming Vertex...

Sorry for intrusion on this thread. I have a question related to "cook_torrance" shader shown on that link.

float NdotH = saturate( dot( normal, half_vector ) );
 
...
if( ROUGHNESS_LOOK_UP == roughness_mode )
{
// texture coordinate is:
float2 tc = { NdotH, roughness_value };
 
// Remap the NdotH value to be 0.0-1.0
// instead of -1.0..+1.0
tc.x += 1.0f;
tc.x /= 2.0f;
 
// look up the coefficient from the texture:
roughness = texRoughness.Sample( sampRoughness, tc );
}

See author comments in code. Is this a bug? Saturate already clamps value to 0.0 - 1.0 range?

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.

If you are interested in a good overview of the semi-standard lighting models, take a look in the Lighting section of Programming Vertex...

Sorry for intrusion on this thread. I have a question related to "cook_torrance" shader shown on that link.


float NdotH = saturate( dot( normal, half_vector ) );
 
...
if( ROUGHNESS_LOOK_UP == roughness_mode )
{
// texture coordinate is:
float2 tc = { NdotH, roughness_value };
 
// Remap the NdotH value to be 0.0-1.0
// instead of -1.0..+1.0
tc.x += 1.0f;
tc.x /= 2.0f;
 
// look up the coefficient from the texture:
roughness = texRoughness.Sample( sampRoughness, tc );
}

See author comments in code. Is this a bug? Saturate already clamps value to 0.0 - 1.0 range?

This is indeed unnecessary, and it wouldn't be the first time I saw a mistake or oversight on gpwiki. In any case, I think you can probably do a lot better than a Beckmann lookup texture. The Beckmann distribution is not expensive to calculate and modern GPUs are limited by memory bandwidth, not instruction throughput. Lookup textures only make sense if you can use them to kill a lot of expensive instructions.

I didn't know about this article/book before (btw. thank you Jason Z), so these few days i started to experiment a little.

Long story short, i don't use Beckmann, i have modified lookup texture generation for gaussian model (since it looked better for me) and also moved fresnel term into it and set roughness_value to be constant for current slice of 3d/volume tex...

He was talking about dividing by NdotL.
BRDF explorer will multiply by NdotL outside of the BRDF, so if you've included NdotL inside your BRDF (as we usually do in games), then you need to divide by NdotL at the end to cancel it out.

Oops. Misread.


Anyone have a database of more BRDF’s we can use with BRDF Explorer?

Also, Chris_F, be careful of this:



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


Should be:


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



L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Also, Chris_F, be careful of this:

Actually, I fixed that in my version already, forgot to edit my post. I misspell Oren-Nayar about 50% of the time.

If you are interested in a good overview of the semi-standard lighting models, take a look in the Lighting section of Programming Vertex...

Sorry for intrusion on this thread. I have a question related to "cook_torrance" shader shown on that link.


float NdotH = saturate( dot( normal, half_vector ) );
 
...
if( ROUGHNESS_LOOK_UP == roughness_mode )
{
// texture coordinate is:
float2 tc = { NdotH, roughness_value };
 
// Remap the NdotH value to be 0.0-1.0
// instead of -1.0..+1.0
tc.x += 1.0f;
tc.x /= 2.0f;
 
// look up the coefficient from the texture:
roughness = texRoughness.Sample( sampRoughness, tc );
}

See author comments in code. Is this a bug? Saturate already clamps value to 0.0 - 1.0 range?

Good catch. I think it is safe to say that the shader code is more for instructional use rather than highly optimized, plus it is getting a bit old (Jack wrote those articles quite some time ago now)... Even so, I still find myself loading up the page every now and then to brush up on a concept that I don't use too often.

This topic is closed to new replies.

Advertisement