9eead719b0
Full project snapshot migrated to new Gitea remote without history: engine, editor, physics, script, examples, tests, docs, and assets. Relicensed from GPLv3 to MIT and updated repo URLs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
2.3 KiB
WebGPU Shading Language
70 lines
2.3 KiB
WebGPU Shading Language
// Stage 4 forward lit shader: a single directional light with Lambert diffuse,
|
|
// ambient, and a Blinn-Phong specular term scaled by material roughness/metallic
|
|
// (PBR-lite). Output is linear color; an sRGB surface format converts on write.
|
|
|
|
struct Globals {
|
|
view_proj: mat4x4<f32>,
|
|
camera_pos: vec4<f32>, // xyz world-space camera position
|
|
light_dir: vec4<f32>, // xyz unit vector pointing TOWARD the light
|
|
light_color: vec4<f32>, // rgb light color * intensity
|
|
ambient: vec4<f32>, // rgb ambient term
|
|
};
|
|
|
|
struct ObjectData {
|
|
model: mat4x4<f32>,
|
|
normal_mtx: mat4x4<f32>, // inverse-transpose of model (3x3 in a 4x4)
|
|
albedo: vec4<f32>,
|
|
mr: vec4<f32>, // x = metallic, y = roughness
|
|
};
|
|
|
|
@group(0) @binding(0) var<uniform> globals: Globals;
|
|
@group(1) @binding(0) var<uniform> obj: ObjectData;
|
|
|
|
struct VsOut {
|
|
@builtin(position) clip_pos: vec4<f32>,
|
|
@location(0) world_pos: vec3<f32>,
|
|
@location(1) world_normal: vec3<f32>,
|
|
@location(2) uv: vec2<f32>,
|
|
};
|
|
|
|
@vertex
|
|
fn vs_main(
|
|
@location(0) position: vec3<f32>,
|
|
@location(1) normal: vec3<f32>,
|
|
@location(2) uv: vec2<f32>,
|
|
) -> VsOut {
|
|
let world = obj.model * vec4<f32>(position, 1.0);
|
|
var out: VsOut;
|
|
out.world_pos = world.xyz;
|
|
out.world_normal = (obj.normal_mtx * vec4<f32>(normal, 0.0)).xyz;
|
|
out.uv = uv;
|
|
out.clip_pos = globals.view_proj * world;
|
|
return out;
|
|
}
|
|
|
|
@fragment
|
|
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
|
|
let n = normalize(in.world_normal);
|
|
let l = normalize(globals.light_dir.xyz);
|
|
let v = normalize(globals.camera_pos.xyz - in.world_pos);
|
|
let h = normalize(l + v);
|
|
|
|
let albedo = obj.albedo.rgb;
|
|
let metallic = obj.mr.x;
|
|
let roughness = clamp(obj.mr.y, 0.04, 1.0);
|
|
|
|
let ndl = max(dot(n, l), 0.0);
|
|
let ndh = max(dot(n, h), 0.0);
|
|
|
|
// Metals have no diffuse; dielectrics get a fixed 0.04 specular, metals
|
|
// tint their specular by the albedo.
|
|
let diffuse = albedo * (1.0 - metallic);
|
|
let spec_color = mix(vec3<f32>(0.04), albedo, metallic);
|
|
let spec_power = mix(8.0, 256.0, 1.0 - roughness);
|
|
let spec = spec_color * pow(ndh, spec_power) * select(0.0, 1.0, ndl > 0.0);
|
|
|
|
let direct = (diffuse * ndl + spec) * globals.light_color.rgb;
|
|
let ambient = albedo * globals.ambient.rgb;
|
|
return vec4<f32>(ambient + direct, obj.albedo.a);
|
|
}
|