Skip to content

Commit

Permalink
merian-nodes: common: ggx: sampling
Browse files Browse the repository at this point in the history
  • Loading branch information
LDAP committed Sep 23, 2024
1 parent 68e1184 commit a5bc971
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 13 deletions.
2 changes: 1 addition & 1 deletion include/merian-shaders/bsdf_diffuse.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// n must be normalized
#define bsdf_diffuse_sample(n, random) (make_frame(n) * sample_cos(random))

// solid angle, not error checked against wdotn < 0.
// solid angle, not error checked against wodotn < 0.
#define bsdf_diffuse_pdf(wodotn) (INV_PI * wodotn)

// solid angle
Expand Down
111 changes: 99 additions & 12 deletions include/merian-shaders/bsdf_ggx.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#define _MERIAN_SHADERS_BSDF_GGX_H_

#include "merian-shaders/common.glsl"
#include "merian-shaders/fresnel.glsl"
#include "merian-shaders/bsdf_diffuse.glsl"
#include "merian-shaders/frames.glsl"

// Smiths shadow masking term
float smith_g1(const float roughness, const float wodotn) {
Expand All @@ -11,12 +14,73 @@ float smith_g1(const float roughness, const float wodotn) {

// minuswi = V
float smith_g_over_minuswidotn(const float roughness, const float minuswidotn, const float wodotn) {
float alpha = MERIAN_SQUARE(roughness);
float g1 = minuswidotn * sqrt(MERIAN_SQUARE(alpha) + (1.0 - MERIAN_SQUARE(alpha)) * MERIAN_SQUARE(wodotn));
float g2 = wodotn * sqrt(MERIAN_SQUARE(alpha) + (1.0 - MERIAN_SQUARE(alpha)) * MERIAN_SQUARE(minuswidotn));
const float alpha = MERIAN_SQUARE(roughness);
const float g1 = minuswidotn * sqrt(MERIAN_SQUARE(alpha) + (1.0 - MERIAN_SQUARE(alpha)) * MERIAN_SQUARE(wodotn));
const float g2 = wodotn * sqrt(MERIAN_SQUARE(alpha) + (1.0 - MERIAN_SQUARE(alpha)) * MERIAN_SQUARE(minuswidotn));
return 2.0 * wodotn / (g1 + g2);
}

// minuswi = V
vec3 bsdf_ggx_times_wodotn(const vec3 minuswi, const vec3 wo, const vec3 n, const float roughness, const vec3 F0) {
const vec3 H = normalize(wo + minuswi);

const float wodotn = clamp(dot(n, wo), 0, 1);
const float minuswi_dot_h = clamp(dot(minuswi, H), 0, 1);
const float ndotv = clamp(dot(n, minuswi), 0, 1);
const float ndoth = clamp(dot(n, H), 0, 1);

if (wodotn > 0) {
const float G = smith_g_over_minuswidotn(roughness, ndotv, wodotn);
const float alpha = MERIAN_SQUARE(roughness);
const float D = MERIAN_SQUARE(alpha) / (M_PI * MERIAN_SQUARE(MERIAN_SQUARE(ndoth) * MERIAN_SQUARE(alpha) + (1 - MERIAN_SQUARE(ndoth))));

const vec3 F = fresnel_schlick(minuswi_dot_h, F0);

return F * (D * G / 4);
}
return vec3(0);
}

float bsdf_ggx_times_wodotn(const vec3 minuswi, const vec3 wo, const vec3 n, const float roughness, const float F0) {
const vec3 H = normalize(wo + minuswi);

const float wodotn = clamp(dot(n, wo), 0, 1);
const float minuswi_dot_h = clamp(dot(minuswi, H), 0, 1);
const float ndotv = clamp(dot(n, minuswi), 0, 1);
const float ndoth = clamp(dot(n, H), 0, 1);

if (wodotn > 0) {
const float G = smith_g_over_minuswidotn(roughness, ndotv, wodotn);
const float alpha = MERIAN_SQUARE(roughness);
const float D = MERIAN_SQUARE(alpha) / (M_PI * MERIAN_SQUARE(MERIAN_SQUARE(ndoth) * MERIAN_SQUARE(alpha) + (1 - MERIAN_SQUARE(ndoth))));

const float F = fresnel_schlick(minuswi_dot_h, F0);

return F * (D * G / 4);
}
return 0;
}

float bsdf_ggx_diffuse_mix_times_wodotn(const vec3 minuswi, const vec3 wo, const vec3 n, const float roughness, const float F0) {
const vec3 H = normalize(wo + minuswi);

const float wodotn = clamp(dot(n, wo), 0, 1);
const float minuswi_dot_h = clamp(dot(minuswi, H), 0, 1);
const float ndotv = clamp(dot(n, minuswi), 0, 1);
const float ndoth = clamp(dot(n, H), 0, 1);

if (wodotn > 0) {
const float G = smith_g_over_minuswidotn(roughness, ndotv, wodotn);
const float alpha = MERIAN_SQUARE(roughness);
const float D = MERIAN_SQUARE(alpha) / (M_PI * MERIAN_SQUARE(MERIAN_SQUARE(ndoth) * MERIAN_SQUARE(alpha) + (1 - MERIAN_SQUARE(ndoth))));

const float F = fresnel_schlick(minuswi_dot_h, F0);

return mix(INV_PI * wodotn, (D * G / 4), F);
}
return 0;
}

// returns a half vector in tangent space
vec3 bsdf_ggx_sample_H(const vec2 random, const float roughness) {
const float phi = TWO_PI * random.x;
Expand All @@ -30,15 +94,15 @@ vec3 bsdf_ggx_sample_H(const vec2 random, const float roughness) {

// https://github.com/NVIDIAGameWorks/donut
// MIT Licensed
vec3 bsdf_ggx_sample_H_VNDF(const vec3 random, const float roughness, const vec3 Ve, const float ndf_trim) {
vec3 bsdf_ggx_VNDF_sample_H(const vec2 random, const float roughness, const vec3 Ve) {
const float alpha = MERIAN_SQUARE(roughness);
const vec3 Vh = normalize(vec3(alpha * Ve.x, alpha * Ve.y, Ve.z));

const float lensq = MERIAN_SQUARE(Vh.x) + MERIAN_SQUARE(Vh.y);
const vec3 T1 = lensq > 0.0 ? vec3(-Vh.y, Vh.x, 0.0) * (1 / sqrt(lensq)) : vec3(1.0, 0.0, 0.0);
const vec3 T2 = cross(Vh, T1);

const float r = sqrt(random.x * ndf_trim);
const float r = sqrt(random.x);// sqrt(random.x * ndf_trim);
const float phi = 2.0 * M_PI * random.y;
const float t1 = r * cos(phi);
float t2 = r * sin(phi);
Expand All @@ -53,14 +117,37 @@ vec3 bsdf_ggx_sample_H_VNDF(const vec3 random, const float roughness, const vec3
);
}

float bsdf_ggx_sample_VNDF_PDF(const float roughness, const vec3 N, const vec3 minus_wi, const vec3 wo) {
vec3 H = normalize(wo + minus_wi);
float ndoth = clamp(dot(N, H), 0, 1);
float minus_wi_dot_h = clamp(dot(minus_wi, H), 0, 1);
float bsdf_ggx_VNDF_pdf(const float roughness, const vec3 N, const vec3 minus_wi, const vec3 wo) {
const vec3 H = normalize(wo + minus_wi);
const float ndoth = clamp(dot(N, H), 0, 1);
const float minuswi_dot_h = clamp(dot(minus_wi, H), 0, 1);

const float alpha = MERIAN_SQUARE(roughness);
const float D = MERIAN_SQUARE(alpha) / (M_PI * MERIAN_SQUARE(MERIAN_SQUARE(ndoth) * MERIAN_SQUARE(alpha) + (1 - MERIAN_SQUARE(ndoth))));
return (minuswi_dot_h > 0.0) ? D / (4.0 * minuswi_dot_h) : 0.0;
}

float bsdf_ggx_diffuse_mix_pdf(const vec3 minuswi, const vec3 wo, const vec3 n, const float roughness) {
const float wodotn = dot(wo, n);
if (wodotn <= 0)
return 0;
const float diffuse_pdf = bsdf_diffuse_pdf(wodotn);
const float ggx_vndf_pdf = bsdf_ggx_VNDF_pdf(roughness, n, minuswi, wo);

const float fresnel = fresnel_schlick(dot(minuswi, n), 0.02);
return mix(diffuse_pdf, ggx_vndf_pdf, fresnel);
}

vec3 bsdf_ggx_diffuse_mix_sample(const vec3 minuswi, const vec3 n, const float roughness, const vec3 random) {
const float fresnel = fresnel_schlick(dot(minuswi, n), 0.02);

float alpha = MERIAN_SQUARE(roughness);
float D = MERIAN_SQUARE(alpha) / (M_PI * MERIAN_SQUARE(MERIAN_SQUARE(ndoth) * MERIAN_SQUARE(alpha) + (1 - MERIAN_SQUARE(ndoth))));
return (minus_wi_dot_h > 0.0) ? D / (4.0 * minus_wi_dot_h) : 0.0;
if (fresnel < random.x) {
return bsdf_diffuse_sample(n, random.yz);
} else {
const mat3x3 frame = make_frame(n);
const vec3 H = normalize(bsdf_ggx_VNDF_sample_H(random.yz, roughness, world_to_frame(frame, minuswi)));
return reflect(-minuswi, frame_to_world(frame, H));
}
}

#endif // _MERIAN_SHADERS_BSDF_GGX_H_
16 changes: 16 additions & 0 deletions include/merian-shaders/frames.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,27 @@ mat3x3 make_frame(const vec3 z) {
z);
}

void make_frame(const vec3 z, out vec3 x, out vec3 y) {
const float sign = (z.z >= 0) ? 1 : -1;
const float a = -1.0 / (sign + z.z);
const float b = z.x * z.y * a;
x = vec3(1.0 + sign * z.x * z.x * a, sign * b, -sign * z.x);
y = vec3(b, sign + z.y * z.y * a, -z.y);
}

// Returns a matrix of axis (x, y, z) which all being perpendicular to each other
mat3x3 make_frame_naive(const vec3 z) {
const vec3 up = (abs(z.x) > abs(z.y)) ? vec3(0,1,0) : vec3(1,0,0);
const vec3 du = normalize(cross(up, z));
return mat3(du, normalize(cross(du, z)), z);
}

vec3 world_to_frame(const mat3x3 frame, const vec3 v) {
return vec3(dot(frame[0], v), dot(frame[1], v), dot(frame[2], v));
}

vec3 frame_to_world(const mat3x3 frame, const vec3 v) {
return frame * v;
}

#endif

0 comments on commit a5bc971

Please sign in to comment.