STANDARD_PBR_WGSL

Constant STANDARD_PBR_WGSL 

Source
pub const STANDARD_PBR_WGSL: &str = "// Standard PBR Material Shader\n// Implements metallic-roughness workflow for physically-based rendering\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    metallic: f32,              // Metallic factor [0-1]\n    roughness: f32,             // Roughness factor [0-1]\n    alpha_cutoff: f32,          // For alpha masking\n    _padding: vec2<f32>,        // Alignment padding\n};\n\n@group(2) @binding(0)\nvar<uniform> material: MaterialUniforms;\n\n// Simple directional light for basic lighting\nstruct DirectionalLight {\n    direction: vec3<f32>,\n    _padding1: f32,\n    color: vec3<f32>,\n    intensity: f32,\n};\n\n@group(3) @binding(0)\nvar<uniform> light: DirectionalLight;\n\n// Constants\nconst PI: f32 = 3.14159265359;\n\n// Simplified PBR calculations\n// This is a basic implementation - will be enhanced with full PBR in issue #48\n\nfn fresnel_schlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {\n    return F0 + (vec3<f32>(1.0) - F0) * pow(1.0 - cosTheta, 5.0);\n}\n\nfn distribution_ggx(N: vec3<f32>, H: vec3<f32>, roughness: f32) -> f32 {\n    let a = roughness * roughness;\n    let a2 = a * a;\n    let NdotH = max(dot(N, H), 0.0);\n    let NdotH2 = NdotH * NdotH;\n    \n    let nom = a2;\n    var denom = (NdotH2 * (a2 - 1.0) + 1.0);\n    denom = PI * denom * denom;\n    \n    return nom / denom;\n}\n\nfn geometry_schlick_ggx(NdotV: f32, roughness: f32) -> f32 {\n    let r = (roughness + 1.0);\n    let k = (r * r) / 8.0;\n    \n    let nom = NdotV;\n    let denom = NdotV * (1.0 - k) + k;\n    \n    return nom / denom;\n}\n\nfn geometry_smith(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>, roughness: f32) -> f32 {\n    let NdotV = max(dot(N, V), 0.0);\n    let NdotL = max(dot(N, L), 0.0);\n    let ggx2 = geometry_schlick_ggx(NdotV, roughness);\n    let ggx1 = geometry_schlick_ggx(NdotL, roughness);\n    \n    return ggx1 * ggx2;\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n    // Base color from material and vertex color\n    let albedo = material.base_color.rgb * in.color;\n    \n    // Normal (already in world space from vertex shader)\n    let N = normalize(in.normal);\n    \n    // View direction\n    let V = normalize(camera.camera_position.xyz - in.world_position);\n    \n    // Light direction (negated because we typically define light direction as \"to light\")\n    let L = normalize(-light.direction);\n    \n    // Half vector\n    let H = normalize(V + L);\n    \n    // Calculate F0 (surface reflection at zero incidence)\n    // For dielectrics, F0 is around 0.04, for metals it\'s the albedo color\n    var F0 = vec3<f32>(0.04);\n    F0 = mix(F0, albedo, material.metallic);\n    \n    // Cook-Torrance BRDF\n    let NDF = distribution_ggx(N, H, material.roughness);\n    let G = geometry_smith(N, V, L, material.roughness);\n    let F = fresnel_schlick(max(dot(H, V), 0.0), F0);\n    \n    let NdotL = max(dot(N, L), 0.0);\n    \n    // Specular component\n    let numerator = NDF * G * F;\n    let denominator = 4.0 * max(dot(N, V), 0.0) * NdotL + 0.001; // Add epsilon to prevent division by zero\n    let specular = numerator / denominator;\n    \n    // Energy conservation - diffuse component\n    let kS = F;  // Specular reflection\n    var kD = vec3<f32>(1.0) - kS;  // Diffuse reflection\n    kD *= 1.0 - material.metallic;  // Metals have no diffuse reflection\n    \n    // Lambert diffuse\n    let diffuse = kD * albedo / PI;\n    \n    // Combine diffuse and specular\n    let radiance = light.color * light.intensity;\n    let Lo = (diffuse + specular) * radiance * NdotL;\n    \n    // Simple ambient (will be replaced with proper ambient lighting later)\n    let ambient = vec3<f32>(0.03) * albedo;\n    \n    // Final color\n    var color = ambient + Lo + material.emissive;\n    \n    // Simple tone mapping (Reinhard)\n    color = color / (color + vec3<f32>(1.0));\n    \n    // Gamma correction\n    color = pow(color, vec3<f32>(1.0 / 2.2));\n    \n    return vec4<f32>(color, material.base_color.a);\n}\n";
Expand description

Standard PBR (Physically-Based Rendering) shader.

Implements the metallic-roughness workflow with Cook-Torrance BRDF:

  • GGX/Trowbridge-Reitz normal distribution
  • Schlick-GGX geometry function
  • Fresnel-Schlick approximation