LIT_FORWARD_WGSL

Constant LIT_FORWARD_WGSL 

Source
pub const LIT_FORWARD_WGSL: &str = "// Lit Forward Shader\n// Multi-light forward rendering with Blinn-Phong lighting\n// Supports: 4 directional + 16 point + 8 spot lights\n\n// --- Vertex Shader ---\n\n// Camera uniform block containing view and projection matrices\nstruct CameraUniforms {\n    view_projection: mat4x4<f32>,  // Combined projection * view matrix\n    camera_position: vec4<f32>,     // Camera position in world space (w is padding)\n};\n\n@group(0) @binding(0)\nvar<uniform> camera: CameraUniforms;\n\n// Vertex input from the vertex buffer\nstruct VertexInput {\n    @location(0) position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n    @location(2) uv: vec2<f32>,\n    @location(3) color: vec3<f32>,\n};\n\n// Output from vertex shader to fragment shader\nstruct VertexOutput {\n    @builtin(position) clip_position: vec4<f32>,\n    @location(0) world_position: vec3<f32>,\n    @location(1) normal: vec3<f32>,\n    @location(2) uv: vec2<f32>,\n    @location(3) color: vec3<f32>,\n};\n\n// Model transform uniform\nstruct ModelUniforms {\n    model_matrix: mat4x4<f32>,\n    normal_matrix: mat4x4<f32>,  // For transforming normals\n};\n\n@group(1) @binding(0)\nvar<uniform> model: ModelUniforms;\n\n@vertex\nfn vs_main(input: VertexInput) -> VertexOutput {\n    var out: VertexOutput;\n    \n    // Transform vertex position to world space\n    let world_pos = model.model_matrix * vec4<f32>(input.position, 1.0);\n    out.world_position = world_pos.xyz;\n    \n    // Transform to clip space\n    out.clip_position = camera.view_projection * world_pos;\n    \n    // Transform normal to world space (using normal matrix to handle non-uniform scales)\n    out.normal = normalize((model.normal_matrix * vec4<f32>(input.normal, 0.0)).xyz);\n    \n    // Pass through UV and color\n    out.uv = input.uv;\n    out.color = input.color;\n    \n    return out;\n}\n\n\n// --- Fragment Shader ---\n\n// Material properties uniform\nstruct MaterialUniforms {\n    base_color: vec4<f32>,      // RGBA base color\n    emissive: vec3<f32>,        // RGB emissive color\n    specular_power: f32,        // Specular exponent (shininess)\n    ambient: vec3<f32>,         // Ambient color\n    _padding: f32,              // Alignment padding\n};\n\n@group(2) @binding(0)\nvar<uniform> material: MaterialUniforms;\n\n// --- Light Structures ---\n\nstruct DirectionalLight {\n    direction: vec3<f32>,\n    _padding1: f32,\n    color: vec3<f32>,\n    intensity: f32,\n};\n\nstruct PointLight {\n    position: vec3<f32>,\n    range: f32,\n    color: vec3<f32>,\n    intensity: f32,\n};\n\nstruct SpotLight {\n    position: vec3<f32>,\n    range: f32,\n    direction: vec3<f32>,\n    inner_cone_cos: f32,\n    color: vec3<f32>,\n    outer_cone_cos: f32,\n    intensity: f32,\n    _padding: vec3<f32>,\n};\n\n// Light arrays with fixed sizes matching LitForwardLane defaults\nconst MAX_DIRECTIONAL_LIGHTS: u32 = 4u;\nconst MAX_POINT_LIGHTS: u32 = 16u;\nconst MAX_SPOT_LIGHTS: u32 = 8u;\n\nstruct LightingUniforms {\n    directional_lights: array<DirectionalLight, 4>,\n    point_lights: array<PointLight, 16>,\n    spot_lights: array<SpotLight, 8>,\n    num_directional_lights: u32,\n    num_point_lights: u32,\n    num_spot_lights: u32,\n    _padding: u32,\n};\n\n@group(3) @binding(0)\nvar<uniform> lights: LightingUniforms;\n\n// --- Lighting Functions ---\n\n/// Calculates attenuation for point/spot lights based on distance and range\nfn calculate_attenuation(distance: f32, range: f32) -> f32 {\n    // Smooth attenuation that reaches zero at range\n    let normalized_distance = distance / range;\n    let attenuation = saturate(1.0 - normalized_distance * normalized_distance);\n    return attenuation * attenuation;\n}\n\n/// Calculates spotlight cone attenuation\nfn calculate_spot_attenuation(\n    light_dir: vec3<f32>,\n    spot_direction: vec3<f32>,\n    inner_cone_cos: f32,\n    outer_cone_cos: f32\n) -> f32 {\n    let cos_angle = dot(-light_dir, spot_direction);\n    return smoothstep(outer_cone_cos, inner_cone_cos, cos_angle);\n}\n\n/// Blinn-Phong BRDF calculation\nfn blinn_phong(\n    N: vec3<f32>,           // Surface normal (normalized)\n    V: vec3<f32>,           // View direction (normalized)\n    L: vec3<f32>,           // Light direction (normalized, pointing toward light)\n    light_color: vec3<f32>,\n    light_intensity: f32,\n    diffuse_color: vec3<f32>,\n    specular_power: f32\n) -> vec3<f32> {\n    // Diffuse component (Lambertian)\n    let NdotL = max(dot(N, L), 0.0);\n    let diffuse = diffuse_color * NdotL;\n    \n    // Specular component (Blinn-Phong)\n    let H = normalize(L + V);  // Half vector\n    let NdotH = max(dot(N, H), 0.0);\n    let specular_strength = pow(NdotH, specular_power);\n    let specular = vec3<f32>(specular_strength);\n    \n    return (diffuse + specular) * light_color * light_intensity;\n}\n\n/// Calculate contribution from all directional lights\nfn calculate_directional_lights(\n    N: vec3<f32>,\n    V: vec3<f32>,\n    diffuse_color: vec3<f32>,\n    specular_power: f32\n) -> vec3<f32> {\n    var result = vec3<f32>(0.0);\n    \n    for (var i = 0u; i < lights.num_directional_lights && i < MAX_DIRECTIONAL_LIGHTS; i++) {\n        let light = lights.directional_lights[i];\n        let L = -normalize(light.direction);  // Reverse direction (toward light)\n        \n        result += blinn_phong(\n            N, V, L,\n            light.color,\n            light.intensity,\n            diffuse_color,\n            specular_power\n        );\n    }\n    \n    return result;\n}\n\n/// Calculate contribution from all point lights\nfn calculate_point_lights(\n    world_position: vec3<f32>,\n    N: vec3<f32>,\n    V: vec3<f32>,\n    diffuse_color: vec3<f32>,\n    specular_power: f32\n) -> vec3<f32> {\n    var result = vec3<f32>(0.0);\n    \n    for (var i = 0u; i < lights.num_point_lights && i < MAX_POINT_LIGHTS; i++) {\n        let light = lights.point_lights[i];\n        let light_vec = light.position - world_position;\n        let distance = length(light_vec);\n        \n        // Skip if outside range\n        if (distance > light.range) {\n            continue;\n        }\n        \n        let L = normalize(light_vec);\n        let attenuation = calculate_attenuation(distance, light.range);\n        \n        result += blinn_phong(\n            N, V, L,\n            light.color,\n            light.intensity * attenuation,\n            diffuse_color,\n            specular_power\n        );\n    }\n    \n    return result;\n}\n\n/// Calculate contribution from all spot lights\nfn calculate_spot_lights(\n    world_position: vec3<f32>,\n    N: vec3<f32>,\n    V: vec3<f32>,\n    diffuse_color: vec3<f32>,\n    specular_power: f32\n) -> vec3<f32> {\n    var result = vec3<f32>(0.0);\n    \n    for (var i = 0u; i < lights.num_spot_lights && i < MAX_SPOT_LIGHTS; i++) {\n        let light = lights.spot_lights[i];\n        let light_vec = light.position - world_position;\n        let distance = length(light_vec);\n        \n        // Skip if outside range\n        if (distance > light.range) {\n            continue;\n        }\n        \n        let L = normalize(light_vec);\n        let distance_attenuation = calculate_attenuation(distance, light.range);\n        let spot_attenuation = calculate_spot_attenuation(\n            L,\n            normalize(light.direction),\n            light.inner_cone_cos,\n            light.outer_cone_cos\n        );\n        let total_attenuation = distance_attenuation * spot_attenuation;\n        \n        // Skip if outside cone\n        if (total_attenuation <= 0.0) {\n            continue;\n        }\n        \n        result += blinn_phong(\n            N, V, L,\n            light.color,\n            light.intensity * total_attenuation,\n            diffuse_color,\n            specular_power\n        );\n    }\n    \n    return result;\n}\n\n@fragment\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\n    // Prepare surface data\n    let N = normalize(input.normal);\n    let V = normalize(camera.camera_position.xyz - input.world_position);\n    \n    // Calculate base diffuse color (material * vertex color)\n    let diffuse_color = material.base_color.rgb * input.color;\n    \n    // Start with ambient lighting\n    var final_color = material.ambient * diffuse_color;\n    \n    // Add contributions from all light types\n    final_color += calculate_directional_lights(N, V, diffuse_color, material.specular_power);\n    final_color += calculate_point_lights(input.world_position, N, V, diffuse_color, material.specular_power);\n    final_color += calculate_spot_lights(input.world_position, N, V, diffuse_color, material.specular_power);\n    \n    // Add emissive\n    final_color += material.emissive;\n    \n    // Simple tone mapping (Reinhard)\n    final_color = final_color / (final_color + vec3<f32>(1.0));\n    \n    // Gamma correction\n    final_color = pow(final_color, vec3<f32>(1.0 / 2.2));\n    \n    return vec4<f32>(final_color, material.base_color.a);\n}\n";
Expand description

Lit forward rendering shader with multi-light Blinn-Phong lighting.

Supports:

  • Up to 4 directional lights
  • Up to 16 point lights
  • Up to 8 spot lights

Uses Blinn-Phong BRDF with Reinhard tone mapping.