kitgit

tirbofish/dropbear · diff

ac07aa8 · Thribhu K

feature: improved shaders, way nicer looking.

Unverified

diff --git a/crates/dropbear-engine/src/shaders/shader.wgsl b/crates/dropbear-engine/src/shaders/shader.wgsl
index af3e4f6..84f7b3f 100644
--- a/crates/dropbear-engine/src/shaders/shader.wgsl
+++ b/crates/dropbear-engine/src/shaders/shader.wgsl
@@ -1,100 +1,80 @@
 const PI: f32 = 3.14159265358979;
 
 struct Globals {
-    num_lights: u32,
+    num_lights:       u32,
     ambient_strength: f32,
 }
 
 struct CameraUniform {
-    view_pos: vec4<f32>,
-    view: mat4x4<f32>,
+    view_pos:  vec4<f32>,
+    view:      mat4x4<f32>,
     view_proj: mat4x4<f32>,
-    inv_proj: mat4x4<f32>,
-    inv_view: mat4x4<f32>,
+    inv_proj:  mat4x4<f32>,
+    inv_view:  mat4x4<f32>,
 }
 
 struct Light {
-    position: vec4<f32>,
+    position:  vec4<f32>,
     direction: vec4<f32>, // x, y, z, outer_cutoff_angle
-    color: vec4<f32>,     // r, g, b, light_type (0=directional, 1=point, 2=spot)
-    constant: f32,
-    lin: f32,
+    color:     vec4<f32>, // r, g, b, light_type (0=directional, 1=point, 2=spot)
+    constant:  f32,
+    lin:       f32,
     quadratic: f32,
-    cutoff: f32,
+    cutoff:    f32,
 }
 
 struct MaterialUniform {
-    base_colour: vec4<f32>,
-    emissive: vec3<f32>,
+    base_colour:       vec4<f32>,
+    emissive:          vec3<f32>,
     emissive_strength: f32,
-    metallic: f32,
-    roughness: f32,
-    normal_scale: f32,
+    metallic:          f32,
+    roughness:         f32,
+    normal_scale:      f32,
     occlusion_strength: f32,
-    alpha_cutoff: f32,
-    uv_tiling: vec2<f32>,
+    alpha_cutoff:      f32,
+    uv_tiling:         vec2<f32>,
 
-    has_normal_texture: u32,
+    has_normal_texture:   u32,
     has_emissive_texture: u32,
     has_metallic_texture: u32,
     has_occlusion_texture: u32,
 }
 
 struct MorphTargetInfo {
-    num_vertices: u32,
-    num_targets: u32,
-    base_offset: u32,
+    num_vertices:  u32,
+    num_targets:   u32,
+    base_offset:   u32,
     weight_offset: u32,
-    uses_morph: u32,
+    uses_morph:    u32,
 }
 
 // per-frame
-@group(0) @binding(0)
-var<uniform> u_globals: Globals;
-@group(0) @binding(1)
-var<uniform> u_camera: CameraUniform;
-@group(0) @binding(2)
-var<storage, read> s_light_array: array<Light>;
+@group(0) @binding(0) var<uniform>       u_globals:     Globals;
+@group(0) @binding(1) var<uniform>       u_camera:      CameraUniform;
+@group(0) @binding(2) var<storage, read> s_light_array: array<Light>;
 
 // per-material
-@group(1) @binding(0)
-var<uniform> u_material: MaterialUniform;
-@group(1) @binding(1)
-var t_diffuse: texture_2d<f32>;
-@group(1) @binding(2)
-var s_diffuse: sampler;
-@group(1) @binding(3)
-var t_normal: texture_2d<f32>;
-@group(1) @binding(4)
-var s_normal: sampler;
-@group(1) @binding(5)
-var t_emissive: texture_2d<f32>;
-@group(1) @binding(6)
-var s_emissive: sampler;
-@group(1) @binding(7)
-var t_metallic: texture_2d<f32>;
-@group(1) @binding(8)
-var s_metallic: sampler;
-@group(1) @binding(9)
-var t_occlusion: texture_2d<f32>;
-@group(1) @binding(10)
-var s_occlusion: sampler;
+@group(1) @binding(0)  var<uniform>  u_material:  MaterialUniform;
+@group(1) @binding(1)  var           t_diffuse:   texture_2d<f32>;
+@group(1) @binding(2)  var           s_diffuse:   sampler;
+@group(1) @binding(3)  var           t_normal:    texture_2d<f32>;
+@group(1) @binding(4)  var           s_normal:    sampler;
+@group(1) @binding(5)  var           t_emissive:  texture_2d<f32>;
+@group(1) @binding(6)  var           s_emissive:  sampler;
+@group(1) @binding(7)  var           t_metallic:  texture_2d<f32>;
+@group(1) @binding(8)  var           s_metallic:  sampler;
+@group(1) @binding(9)  var           t_occlusion: texture_2d<f32>;
+@group(1) @binding(10) var           s_occlusion: sampler;
 
 // animation
-@group(2) @binding(0)
-var<storage, read> s_skinning: array<mat4x4<f32>>;
-@group(2) @binding(1)
-var<storage, read> s_morph_deltas: array<f32>;
-@group(2) @binding(2)
-var<storage, read> s_morph_weights: array<f32>;
-@group(2) @binding(3)
-var<uniform> u_morph_info: MorphTargetInfo;
+@group(2) @binding(0) var<storage, read> s_skinning:     array<mat4x4<f32>>;
+@group(2) @binding(1) var<storage, read> s_morph_deltas: array<f32>;
+@group(2) @binding(2) var<storage, read> s_morph_weights: array<f32>;
+@group(2) @binding(3) var<uniform>       u_morph_info:   MorphTargetInfo;
 
 // environment
-@group(3) @binding(0)
-var env_map: texture_cube<f32>;
-@group(3) @binding(1)
-var env_sampler: sampler;
+@group(3) @binding(0) var env_map:     texture_cube<f32>;
+@group(3) @binding(1) var env_sampler: sampler;
 
 struct InstanceInput {
     @location(8)  model_matrix_0: vec4<f32>,
@@ -108,33 +88,33 @@ struct InstanceInput {
 };
 
 struct VertexInput {
-    @builtin(vertex_index) vertex_id: u32,
-    @location(0) position: vec3<f32>,
-    @location(1) normal: vec3<f32>,
-    @location(2) tangent: vec4<f32>,
+    @builtin(vertex_index) vertex_id:  u32,
+    @location(0) position:   vec3<f32>,
+    @location(1) normal:     vec3<f32>,
+    @location(2) tangent:    vec4<f32>,
     @location(3) tex_coords0: vec2<f32>,
     @location(4) tex_coords1: vec2<f32>,
-    @location(5) colour0: vec4<f32>,
-    @location(6) joints: vec4<u32>,
-    @location(7) weights: vec4<f32>,
+    @location(5) colour0:    vec4<f32>,
+    @location(6) joints:     vec4<u32>,
+    @location(7) weights:    vec4<f32>,
 };
 
 struct VertexOutput {
-    @builtin(position) clip_position: vec4<f32>,
-    @location(0) tex_coords: vec2<f32>,
-    @location(1) world_normal: vec3<f32>,
-    @location(2) world_position: vec3<f32>,
-    @location(3) world_tangent: vec3<f32>,
-    @location(4) world_bitangent: vec3<f32>,
-    @location(5) world_view_position: vec3<f32>,
+    @builtin(position) clip_position:     vec4<f32>,
+    @location(0) tex_coords:              vec2<f32>,
+    @location(1) world_normal:            vec3<f32>,
+    @location(2) world_position:          vec3<f32>,
+    @location(3) world_tangent:           vec3<f32>,
+    @location(4) world_bitangent:         vec3<f32>,
+    @location(5) world_view_position:     vec3<f32>,
 };
 
 fn apply_morph(base_pos: vec3<f32>, vertex_id: u32) -> vec3<f32> {
     var result = base_pos;
     for (var t = 0u; t < u_morph_info.num_targets; t++) {
         let weight = s_morph_weights[u_morph_info.weight_offset + t];
-        let idx = u_morph_info.base_offset + (t * u_morph_info.num_vertices + vertex_id) * 3u;
-        let delta = vec3<f32>(
+        let idx    = u_morph_info.base_offset + (t * u_morph_info.num_vertices + vertex_id) * 3u;
+        let delta  = vec3<f32>(
             s_morph_deltas[idx],
             s_morph_deltas[idx + 1u],
             s_morph_deltas[idx + 2u],
@@ -174,27 +154,27 @@ fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
             s_skinning[j.w] * w.w;
     }
 
-    let morphed_pos = select(
+    let morphed_pos  = select(
         model.position,
         apply_morph(model.position, model.vertex_id),
         u_morph_info.uses_morph != 0u,
     );
     let world_position = model_matrix * skin_matrix * vec4<f32>(morphed_pos, 1.0);
 
-    let skin_normal  = (skin_matrix * vec4<f32>(model.normal,       0.0)).xyz;
-    let skin_tangent = (skin_matrix * vec4<f32>(model.tangent.xyz,  0.0)).xyz;
+    let skin_normal  = (skin_matrix * vec4<f32>(model.normal,      0.0)).xyz;
+    let skin_tangent = (skin_matrix * vec4<f32>(model.tangent.xyz, 0.0)).xyz;
 
     let world_normal    = normalize(normal_matrix * skin_normal);
     let world_tangent   = normalize(normal_matrix * skin_tangent);
     let world_bitangent = normalize(cross(world_normal, world_tangent) * model.tangent.w);
 
     var out: VertexOutput;
-    out.clip_position       = u_camera.view_proj * world_position;
-    out.tex_coords          = model.tex_coords0;
-    out.world_normal        = world_normal;
-    out.world_position      = world_position.xyz;
-    out.world_tangent       = world_tangent;
-    out.world_bitangent     = world_bitangent;
+    out.clip_position      = u_camera.view_proj * world_position;
+    out.tex_coords         = model.tex_coords0;
+    out.world_normal       = world_normal;
+    out.world_position     = world_position.xyz;
+    out.world_tangent      = world_tangent;
+    out.world_bitangent    = world_bitangent;
     out.world_view_position = u_camera.view_pos.xyz;
     return out;
 }
@@ -265,7 +245,7 @@ fn directional_light_pbr(
     roughness: f32,
     metallic:  f32,
 ) -> vec3<f32> {
-    let l = normalize(light.direction.xyz);
+    let l = normalize(-light.direction.xyz);
     return pbr_direct(n, v, l, albedo, f0, roughness, metallic) * light.color.rgb;
 }
 
@@ -296,49 +276,48 @@ fn spot_light_pbr(
     metallic:  f32,
     world_pos: vec3<f32>,
 ) -> vec3<f32> {
-    let to_light = light.position.xyz - world_pos;
-    let dist     = length(to_light);
-    let l        = to_light / dist;
-    let atten    = 1.0 / (light.constant + light.lin * dist + light.quadratic * dist * dist);
+    let to_light     = light.position.xyz - world_pos;
+    let dist         = length(to_light);
+    let l            = to_light / dist;
+    let atten        = 1.0 / (light.constant + light.lin * dist + light.quadratic * dist * dist);
 
-    let spot_dir   = normalize(light.direction.xyz);
-    let theta      = dot(-l, spot_dir);
-    let cone_factor = smoothstep(light.direction.w, light.cutoff, theta);
+    let spot_dir     = normalize(light.direction.xyz);
+    let theta        = dot(-l, spot_dir);
+    let outer_cutoff = light.direction.w;
+    let epsilon      = light.cutoff - outer_cutoff;
+    let cone_factor  = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0);
 
     return pbr_direct(n, v, l, albedo, f0, roughness, metallic)
          * light.color.rgb * atten * cone_factor;
 }
 
 fn ibl(
-    n:            vec3<f32>,
-    v:            vec3<f32>,
-    albedo:       vec3<f32>,
-    f0:           vec3<f32>,
-    roughness:    f32,
-    metallic:     f32,
+    n:         vec3<f32>,
+    v:         vec3<f32>,
+    albedo:    vec3<f32>,
+    f0:        vec3<f32>,
+    roughness: f32,
+    metallic:  f32,
 ) -> vec3<f32> {
-    let n_dot_v = max(dot(n, v), 0.0001);
+    let n_dot_v  = max(dot(n, v), 0.0001);
     let num_mips = f32(textureNumLevels(env_map));
 
-    // fresnel for smooth roughness fade
-    let F = fresnel_schlick_roughness(n_dot_v, f0, roughness);
-
+    let F   = fresnel_schlick_roughness(n_dot_v, f0, roughness);
     let k_s = F;
     let k_d = (1.0 - k_s) * (1.0 - metallic);
 
-    // diffuse irradiance: sample the most-blurred mip to approximate
-    // hemisphere-integrated radiance
+    // diffuse irradiance: most-blurred mip approximates hemisphere integral
     let irradiance  = textureSampleLevel(env_map, env_sampler, n, num_mips - 1.0).rgb;
     let diffuse_ibl = k_d * albedo * irradiance;
 
-    // specular radiance: rougher materials sample higher (blurrier) mip levels
-    let r              = reflect(-v, n);
-    let specular_mip   = roughness * roughness * (num_mips - 1.0);
-    let prefiltered    = textureSampleLevel(env_map, env_sampler, r, specular_mip).rgb;
+    // specular: rougher surfaces sample blurrier mips
+    let r            = reflect(-v, n);
+    let specular_mip = roughness * roughness * (num_mips - 1.0);
+    let prefiltered  = textureSampleLevel(env_map, env_sampler, r, specular_mip).rgb;
 
-    // Analytic BRDF integration approximation (no LUT needed)
-    let env_brdf_x = exp(-6.9 * roughness * roughness * n_dot_v);
-    let env_brdf   = F * (1.0 - env_brdf_x) + f0 * env_brdf_x;
+    // analytic BRDF integration approximation (no LUT required)
+    let env_brdf_x   = exp(-6.9 * roughness * roughness * n_dot_v);
+    let env_brdf     = F * (1.0 - env_brdf_x) + f0 * env_brdf_x;
     let specular_ibl = prefiltered * env_brdf;
 
     return diffuse_ibl + specular_ibl;
@@ -349,20 +328,20 @@ fn get_normal(in: VertexOutput, uv: vec2<f32>) -> vec3<f32> {
         return normalize(in.world_normal);
     }
 
-    let raw = textureSample(t_normal, s_normal, uv).rgb;
-    var tangent_normal = raw * 2.0 - 1.0;
+    let raw     = textureSample(t_normal, s_normal, uv).rgb;
+    let unpacked = normalize((raw * 2.0 - 1.0) * vec3<f32>(u_material.normal_scale, u_material.normal_scale, 1.0));
 
-    tangent_normal = vec3<f32>(
-        tangent_normal.xy * u_material.normal_scale,
-        tangent_normal.z,
-    );
+    let n = normalize(in.world_normal);
+    // Gram-Schmidt re-orthogonalise tangent against normal
+    var t = normalize(in.world_tangent);
+    t = normalize(t - n * dot(n, t));
 
-    let t   = normalize(in.world_tangent);
-    let b   = normalize(in.world_bitangent);
-    let nm  = normalize(in.world_normal);
-    let tbn = mat3x3<f32>(t, b, nm);
+    let b_in       = normalize(in.world_bitangent);
+    let handedness = select(-1.0, 1.0, dot(cross(n, t), b_in) >= 0.0);
+    let b          = cross(n, t) * handedness;
 
-    return normalize(tbn * tangent_normal);
+    let tbn = mat3x3<f32>(t, b, n);
+    return normalize(tbn * unpacked);
 }
 
 @fragment
@@ -371,64 +350,60 @@ fn s_fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
 
     // albedo + alpha
     let albedo_sample = textureSample(t_diffuse, s_diffuse, uv);
-    if albedo_sample.a < u_material.alpha_cutoff {
+    let base_colour   = albedo_sample * u_material.base_colour;
+    if base_colour.a < u_material.alpha_cutoff {
         discard;
     }
+    let albedo = base_colour.rgb;
 
-    let albedo = albedo_sample.rgb * u_material.base_colour.rgb;
-
-    // metallic / roughness
-    // glTF convention: B = metallic, G = roughness (R = occlusion when packed).
+    // metallic / roughness — glTF: B = metallic, G = roughness
     var metallic  = u_material.metallic;
     var roughness = u_material.roughness;
     if u_material.has_metallic_texture != 0u {
-        let mr_sample = textureSample(t_metallic, s_metallic, uv);
-        metallic  *= mr_sample.b;
-        roughness *= mr_sample.g;
+        let mr    = textureSample(t_metallic, s_metallic, uv);
+        metallic  *= mr.b;
+        roughness *= mr.g;
     }
+    metallic  = clamp(metallic,  0.0,  1.0);
     roughness = clamp(roughness, 0.04, 1.0);
 
     // occlusion
     var occlusion = 1.0;
     if u_material.has_occlusion_texture != 0u {
-        let occ_sample = textureSample(t_occlusion, s_occlusion, uv);
-        occlusion = mix(1.0, occ_sample.r, u_material.occlusion_strength);
+        let occ   = textureSample(t_occlusion, s_occlusion, uv).r;
+        occlusion = 1.0 + u_material.occlusion_strength * (occ - 1.0);
+    }
+
+    // emissive
+    var emissive = u_material.emissive * u_material.emissive_strength;
+    if u_material.has_emissive_texture != 0u {
+        emissive *= textureSample(t_emissive, s_emissive, uv).rgb;
     }
 
     let n   = get_normal(in, uv);
     let v   = normalize(u_camera.view_pos.xyz - in.world_position);
+    let f0  = mix(vec3<f32>(0.04), albedo, metallic);
 
-    // F0: dielectrics use 0.04, metals use their albedo colour
-    let f0 = mix(vec3<f32>(0.04), albedo, metallic);
-
-    // cook-torence
+    // direct lighting (Cook-Torrance GGX)
     var lo = vec3<f32>(0.0);
     for (var i = 0u; i < u_globals.num_lights; i++) {
         let light      = s_light_array[i];
-        let light_type = u32(light.color.w);
+        let light_type = i32(light.color.w + 0.1);
 
-        if light_type == 0u {
+        if light_type == 0 {
             lo += directional_light_pbr(light, n, v, albedo, f0, roughness, metallic);
-        } else if light_type == 1u {
+        } else if light_type == 1 {
             lo += point_light_pbr(light, n, v, albedo, f0, roughness, metallic, in.world_position);
-        } else if light_type == 2u {
+        } else if light_type == 2 {
             lo += spot_light_pbr(light, n, v, albedo, f0, roughness, metallic, in.world_position);
         }
     }
 
-    // image-based lighting (env map)
+    // image-based lighting
     let ambient_ibl = ibl(n, v, albedo, f0, roughness, metallic)
                     * occlusion
                     * u_globals.ambient_strength;
 
-    // emissive
-    var emissive = u_material.emissive * u_material.emissive_strength;
-    if u_material.has_emissive_texture != 0u {
-        emissive *= textureSample(t_emissive, s_emissive, uv).rgb;
-    }
-
-    // combine
     let colour = lo + ambient_ibl + emissive;
-    
-    return vec4<f32>(colour, albedo_sample.a);
+    return vec4<f32>(colour, base_colour.a);
 }
\ No newline at end of file