Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes/Lighting refractor #158

Merged
merged 2 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dotrix_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ pub fn extension(app: &mut Application) {
solid::extension(app);
skeletal::extension(app);
}

pub fn add_pbr_to_shader(source: &str, bind_group: usize, binding: usize) -> String {
let pbr_code = include_str!("shaders/pbr.inc.wgsl");

let pbr_lighted_code = Lights::add_to_shader(pbr_code, bind_group, binding);

source.replace("{{ include(light) }}", &pbr_lighted_code)
}
218 changes: 47 additions & 171 deletions dotrix_pbr/src/shaders/light.inc.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Most of this comes from https://learnopengl.com/PBR/Lighting
let MAX_LIGHTS_COUNT: u32 = {{ max_lights_count }};
let PI: f32 = 3.14159;

struct LightCalcOutput {
light_direction: vec3<f32>;
Expand Down Expand Up @@ -51,7 +49,6 @@ var<uniform> u_light: Light;

fn calculate_directional(
light: DirectionalLight,
normal: vec3<f32>,
) -> LightCalcOutput {
let light_direction: vec3<f32> = normalize(-light.direction.xyz);

Expand All @@ -65,7 +62,6 @@ fn calculate_directional(
fn calculate_point(
light: PointLight,
position: vec3<f32>,
normal: vec3<f32>,
) -> LightCalcOutput {
let light_direction: vec3<f32> = normalize(light.position.xyz - position);

Expand All @@ -85,7 +81,6 @@ fn calculate_point(
fn calculate_simple(
light: SimpleLight,
position: vec3<f32>,
normal: vec3<f32>,
) -> LightCalcOutput {
let light_direction: vec3<f32> = normalize(light.position.xyz - position.xyz);

Expand All @@ -99,7 +94,6 @@ fn calculate_simple(
fn calculate_spot(
light: SpotLight,
position: vec3<f32>,
normal: vec3<f32>,
) -> LightCalcOutput {
let light_direction: vec3<f32> = normalize(light.position.xyz - position.xyz);
let theta: f32 = dot(light_direction, normalize(-light.direction.xyz));
Expand All @@ -113,173 +107,55 @@ fn calculate_spot(
return out;
}

fn distribution_ggx(normal: vec3<f32>, halfway: vec3<f32>, roughness: f32) -> f32
{
let a: f32 = roughness*roughness;
let a2: f32 = a*a;
let n_dot_h: f32 = max(dot(normal, halfway), 0.0);
let n_dot_h_2: f32 = n_dot_h*n_dot_h;

let num: f32 = a2;
var denom: f32 = (n_dot_h_2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;

return num / denom;
}

fn geometry_schlick_ggx(n_dot_v: f32, roughness: f32) -> f32
{
let r: f32 = (roughness + 1.0);
let k: f32 = (r*r) / 8.0;

let num: f32 = n_dot_v;
let denom: f32 = n_dot_v * (1.0 - k) + k;

return num / denom;
}
fn geometry_smith(normal: vec3<f32>, camera_direction: vec3<f32>, light_direction: vec3<f32>, roughness: f32) -> f32
{
let n_dot_v: f32 = max(dot(normal, camera_direction), 0.0);
let n_dot_l: f32 = max(dot(normal, light_direction), 0.0);
let ggx2: f32 = geometry_schlick_ggx(n_dot_v, roughness);
let ggx1: f32 = geometry_schlick_ggx(n_dot_l, roughness);

return ggx1 * ggx2;
// This will get the direction direction and intensity of
// the nth light towards a position
// If used in conjectuion with `get_light_count`
// It allows for more consistent iter code by providing
// A standard single data `LightCalcOutput` for any light
// regardless of type
fn calculate_nth_light_ray(
in_camera_index: u32,
position: vec3<f32>,
) -> LightCalcOutput {
var camera_index: u32 = in_camera_index;
// directional
let dir_count = min(u32(u_light.count.x), MAX_LIGHTS_COUNT);
if (camera_index < dir_count) {
var light: DirectionalLight = u_light.directional[camera_index];
return calculate_directional(light);
}
camera_index = camera_index - dir_count;
// point
let point_count = min(u32(u_light.count.y), MAX_LIGHTS_COUNT);
if (camera_index < point_count) {
var light: PointLight = u_light.point[camera_index];
return calculate_point(light, position);
}
camera_index = camera_index - point_count;
// simple
let simple_count = min(u32(u_light.count.z), MAX_LIGHTS_COUNT);
if (camera_index < simple_count) {
var light: SimpleLight = u_light.simple[camera_index];
return calculate_simple(light, position);
}
camera_index = camera_index - simple_count;
// spot
let spot_count = min(u32(u_light.count.w), MAX_LIGHTS_COUNT);
if (camera_index < spot_count) {
var light: SpotLight = u_light.spot[camera_index];
return calculate_spot(light, position);
}
// Trying to access a non existant light
var oob: LightCalcOutput;
oob.light_direction = vec3<f32>(0.);
oob.radiance = vec3<f32>(0.);
return oob;
}

// Calulates the amount of light that refects (specular) and that which scatters (diffuse)
fn calculate_fresnel_schlick(cos_theta: f32, fresnel_schlick_0: vec3<f32>) -> vec3<f32>
{
return fresnel_schlick_0 + (vec3<f32>(1.0) - fresnel_schlick_0) * pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0);
fn get_light_count() -> u32 {
return u_light.count.x + u_light.count.y + u_light.count.z + u_light.count.w;
}

fn pbr(
light_out: LightCalcOutput,
camera_direction: vec3<f32>, // Camera direction
normal: vec3<f32>, // normal
fresnel_schlick_0: vec3<f32>, // surface reflection at zero incidence
albedo: vec3<f32>, // scatter color in linear space
metallic: f32, // Metallic (reflectance)
roughness: f32 // Roughness (random scatter)
) -> vec3<f32> {
let light_direction: vec3<f32> = light_out.light_direction;
let halfway: vec3<f32> = normalize(camera_direction + light_direction);

// cook-torrance brdf
let normal_distribution_function: f32 = distribution_ggx(normal, halfway, roughness);
let geometry_function: f32 = geometry_smith(normal, camera_direction, light_direction, roughness);
let fresnel_schlick: vec3<f32> = calculate_fresnel_schlick(max(dot(halfway, camera_direction), 0.0), fresnel_schlick_0);

let reflection_specular_fraction: vec3<f32> = fresnel_schlick;
var refraction_diffuse_fraction: vec3<f32> = vec3<f32>(1.0) - reflection_specular_fraction; // refraction/diffuse fraction
refraction_diffuse_fraction = refraction_diffuse_fraction * (1.0 - metallic);

let numerator: vec3<f32> = normal_distribution_function * geometry_function * fresnel_schlick;
let denominator: f32 = 4.0 * max(dot(normal, camera_direction), 0.0) * max(dot(normal, light_direction), 0.0) + 0.0001;
let specular: vec3<f32> = numerator / denominator;

// get the outgoing radiance
let n_dot_l: f32 = max(dot(normal, light_direction), 0.0);
return (refraction_diffuse_fraction * albedo / PI + specular) * light_out.radiance * n_dot_l;
}

fn calculate_lighting(
position: vec3<f32>,
normal_in: vec3<f32>,
albedo: vec3<f32>,
roughness: f32,
metallic: f32,
ao: f32,
) -> vec4<f32> {
let camera_position: vec3<f32> = u_light.camera_position.xyz;
var light_color: vec3<f32> = vec3<f32>(0.);

let normal: vec3<f32> = normalize(normal_in );
let camera_direction: vec3<f32> = normalize(camera_position - position);

var fresnel_schlick_0: vec3<f32> = vec3<f32>(0.04);
fresnel_schlick_0 = mix(fresnel_schlick_0, albedo, vec3<f32>(metallic));

// Directions light
var i: u32 = 0u;
var count: u32 = min(u32(u_light.count.x), MAX_LIGHTS_COUNT);
for (i = 0u; i< count; i = i + 1u) {
let light_result = calculate_directional(
u_light.directional[i],
normal
);
light_color = light_color + pbr(
light_result,
camera_direction,
normal,
fresnel_schlick_0,
albedo,
metallic,
roughness
);
}
// Point light
count = min(u32(u_light.count.y), MAX_LIGHTS_COUNT);
for (i = 0u; i< count; i = i + 1u) {
let light_result = calculate_point(
u_light.point[i],
position,
normal
);
light_color = light_color + pbr(
light_result,
camera_direction,
normal,
fresnel_schlick_0,
albedo,
metallic,
roughness
);
}
// Simple light
count = min(u32(u_light.count.z), MAX_LIGHTS_COUNT);
for (i = 0u; i< count; i = i + 1u) {
let light_result = calculate_simple(
u_light.simple[i],
position,
normal
);
light_color = light_color + pbr(
light_result,
camera_direction,
normal,
fresnel_schlick_0,
albedo,
metallic,
roughness
);
}
// Spot light
count = min(u32(u_light.count.w), MAX_LIGHTS_COUNT);
for (i = 0u; i< count; i = i + 1u) {
let light_result = calculate_spot(
u_light.spot[i],
position,
normal
);
light_color = light_color + pbr(
light_result,
camera_direction,
normal,
fresnel_schlick_0,
albedo,
metallic,
roughness
);
}

// Ambient
let ambient = u_light.ambient.xyz * albedo * ao;
light_color = light_color + ambient;

// Gamma correct
light_color = light_color / (light_color + vec3<f32>(1.0));
light_color = pow(light_color, vec3<f32>(1.0/2.2));

return vec4<f32>(light_color, 1.0);
fn get_ambient() -> vec3<f32> {
return u_light.ambient.xyz;
}
118 changes: 118 additions & 0 deletions dotrix_pbr/src/shaders/pbr.inc.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Most of this comes from https://learnopengl.com/PBR/Lighting
let PI: f32 = 3.14159;

{{ include(light) }}

fn distribution_ggx(normal: vec3<f32>, halfway: vec3<f32>, roughness: f32) -> f32
{
let a: f32 = roughness*roughness;
let a2: f32 = a*a;
let n_dot_h: f32 = max(dot(normal, halfway), 0.0);
let n_dot_h_2: f32 = n_dot_h*n_dot_h;

let num: f32 = a2;
var denom: f32 = (n_dot_h_2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;

return num / denom;
}

fn geometry_schlick_ggx(n_dot_v: f32, roughness: f32) -> f32
{
let r: f32 = (roughness + 1.0);
let k: f32 = (r*r) / 8.0;

let num: f32 = n_dot_v;
let denom: f32 = n_dot_v * (1.0 - k) + k;

return num / denom;
}
fn geometry_smith(normal: vec3<f32>, camera_direction: vec3<f32>, light_direction: vec3<f32>, roughness: f32) -> f32
{
let n_dot_v: f32 = max(dot(normal, camera_direction), 0.0);
let n_dot_l: f32 = max(dot(normal, light_direction), 0.0);
let ggx2: f32 = geometry_schlick_ggx(n_dot_v, roughness);
let ggx1: f32 = geometry_schlick_ggx(n_dot_l, roughness);

return ggx1 * ggx2;
}

// Calulates the amount of light that refects (specular) and that which scatters (diffuse)
fn calculate_fresnel_schlick(cos_theta: f32, fresnel_schlick_0: vec3<f32>) -> vec3<f32>
{
return fresnel_schlick_0 + (vec3<f32>(1.0) - fresnel_schlick_0) * pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0);
}

fn pbr(
light_out: LightCalcOutput,
camera_direction: vec3<f32>, // Camera direction
normal: vec3<f32>, // normal
fresnel_schlick_0: vec3<f32>, // surface reflection at zero incidence
albedo: vec3<f32>, // scatter color in linear space
metallic: f32, // Metallic (reflectance)
roughness: f32 // Roughness (random scatter)
) -> vec3<f32> {
let light_direction: vec3<f32> = light_out.light_direction;
let halfway: vec3<f32> = normalize(camera_direction + light_direction);

// cook-torrance brdf
let normal_distribution_function: f32 = distribution_ggx(normal, halfway, roughness);
let geometry_function: f32 = geometry_smith(normal, camera_direction, light_direction, roughness);
let fresnel_schlick: vec3<f32> = calculate_fresnel_schlick(max(dot(halfway, camera_direction), 0.0), fresnel_schlick_0);

let reflection_specular_fraction: vec3<f32> = fresnel_schlick;
var refraction_diffuse_fraction: vec3<f32> = vec3<f32>(1.0) - reflection_specular_fraction; // refraction/diffuse fraction
refraction_diffuse_fraction = refraction_diffuse_fraction * (1.0 - metallic);

let numerator: vec3<f32> = normal_distribution_function * geometry_function * fresnel_schlick;
let denominator: f32 = 4.0 * max(dot(normal, camera_direction), 0.0) * max(dot(normal, light_direction), 0.0) + 0.0001;
let specular: vec3<f32> = numerator / denominator;

// get the outgoing radiance
let n_dot_l: f32 = max(dot(normal, light_direction), 0.0);
return (refraction_diffuse_fraction * albedo / PI + specular) * light_out.radiance * n_dot_l;
}

fn calculate_lighting(
position: vec3<f32>,
normal_in: vec3<f32>,
albedo: vec3<f32>,
roughness: f32,
metallic: f32,
ao: f32,
) -> vec4<f32> {
let camera_position: vec3<f32> = u_light.camera_position.xyz;
var light_color: vec3<f32> = vec3<f32>(0.);

let normal: vec3<f32> = normalize(normal_in );
let camera_direction: vec3<f32> = normalize(camera_position - position);

var fresnel_schlick_0: vec3<f32> = vec3<f32>(0.04);
fresnel_schlick_0 = mix(fresnel_schlick_0, albedo, vec3<f32>(metallic));

// Directions light
var i: u32 = 0u;
var count: u32 = get_light_count();
for (i = 0u; i< count; i = i + 1u) {
let light_result = calculate_nth_light_ray(i, position);
light_color = light_color + pbr(
light_result,
camera_direction,
normal,
fresnel_schlick_0,
albedo,
metallic,
roughness
);
}

// Ambient
let ambient = get_ambient() * albedo * ao;
light_color = light_color + ambient;

// Gamma correct
light_color = light_color / (light_color + vec3<f32>(1.0));
light_color = pow(light_color, vec3<f32>(1.0/2.2));

return vec4<f32>(light_color, 1.0);
}
Loading