render-zig

A 3D rendering engine written in Zig
git clone git://git.christianermann.dev/render-zig
Log | Files | Refs

mesh_pbr.wgsl (5263B)


      1 struct VertexInput {
      2     @location(0) position: vec4<f32>,
      3     @location(1) normal: vec3<f32>,
      4     @location(2) tex_coord: vec3<f32>,
      5     @location(3) tangent: vec3<f32>,
      6     @location(4) bitangent: vec3<f32>,
      7 };
      8 
      9 struct VertexOutput {
     10     @builtin(position) clip_position: vec4<f32>,
     11     @location(0) tex_coord: vec3<f32>,
     12     @location(1) pt_fragment: vec3<f32>,
     13     @location(2) pt_light: vec3<f32>,
     14     @location(3) pt_camera: vec3<f32>,
     15     @location(4) @interpolate(flat) instance_id: u32,
     16 };
     17 
     18 @group(0) @binding(0)
     19 var<storage, read> instances: array<Instance>;
     20 struct Instance {
     21     model_matrix: mat4x4<f32>,
     22     material: Material,
     23 };
     24 struct Material {
     25     albedo: u32,
     26     normal: u32,
     27     roughness: u32,
     28     metalness: u32,
     29 };
     30 
     31 @group(0) @binding(1)
     32 var<uniform> uniform_data: UniformData;
     33 struct UniformData {
     34     pw_camera: vec3<f32>,
     35     view_matrix: mat4x4<f32>,
     36     proj_matrix: mat4x4<f32>,
     37 };
     38 
     39 @vertex fn vertex(
     40     in: VertexInput,
     41     @builtin(instance_index) idx: u32,
     42 ) -> VertexOutput {
     43     let model_matrix = instances[idx].model_matrix;
     44     let camera_matrix = uniform_data.proj_matrix * uniform_data.view_matrix;
     45 
     46     let t = normalize((model_matrix * vec4(in.tangent, 0.0)).xyz);
     47     let b = normalize((model_matrix * vec4(in.bitangent, 0.0)).xyz);
     48     let n = normalize((model_matrix * vec4(in.normal, 0.0)).xyz);
     49     let tangent_matrix = transpose(mat3x3<f32>(t, b, n));
     50 
     51     let world_position = model_matrix * in.position;
     52     let tangent_position = tangent_matrix * world_position.xyz;
     53 
     54     let light_position = vec3<f32>(20, 80, 20);
     55     let tangent_light_position = tangent_matrix * light_position;
     56 
     57     let pt_camera = tangent_matrix * uniform_data.pw_camera;
     58 
     59     return VertexOutput(
     60         camera_matrix * world_position,
     61         in.tex_coord,
     62         tangent_position,
     63         tangent_light_position,
     64         pt_camera,
     65         idx,
     66     );
     67 }
     68 
     69 
     70 @group(0) @binding(2) var material_sampler: sampler;
     71 @group(0) @binding(3) var material_texture: texture_2d_array<f32>;
     72 
     73 fn diffuse_lambert(albedo: vec3<f32>) -> vec3<f32> {
     74     return albedo * 0.31830988618;
     75 }
     76 
     77 fn distribution_trowbridge_reitz_ggx(
     78     n_dot_h: f32,
     79     alpha: f32,
     80 ) -> f32 {
     81     let alpha_squared = alpha * alpha;
     82     let n_dot_h_squared = n_dot_h * n_dot_h;
     83     let denom = n_dot_h_squared * (alpha_squared - 1) + 1;
     84     let denom_squared = denom * denom;
     85     return alpha_squared / (3.14159265359 * denom_squared);
     86 }
     87 
     88 fn geometry_schlick_ggx(
     89     n_dot_x: f32,
     90     alpha: f32,
     91 ) -> f32 {
     92     let k = 0.5 * alpha;
     93     let denom = n_dot_x * (1.0 - k) + k;
     94     return n_dot_x / denom;
     95 }
     96 
     97 fn geometry_smith(
     98     n_dot_l: f32,
     99     n_dot_v: f32,
    100     alpha: f32,
    101 ) -> f32 {
    102     let g1 = geometry_schlick_ggx(n_dot_l, alpha);
    103     let g2 = geometry_schlick_ggx(n_dot_v, alpha);
    104     return g1 * g2;
    105 }
    106 
    107 fn fresnel_schlick(
    108     f0: vec3<f32>,
    109     v_dot_h: f32,
    110 ) -> vec3<f32> {
    111     return f0 + (1.0 - f0) * pow(1.0 - v_dot_h, 5.0);
    112 }
    113 
    114 fn cook_torrance(
    115     n_dot_v: f32,
    116     n_dot_l: f32,
    117     n_dot_h: f32,
    118     roughness: f32,
    119 ) -> f32 {
    120     let alpha = roughness * roughness;
    121     let distribution = distribution_trowbridge_reitz_ggx(
    122         n_dot_h,
    123         alpha,
    124     );
    125     let geometry = geometry_smith(
    126         n_dot_l,
    127         n_dot_v,
    128         alpha,
    129     );
    130     let denom = 4.0 * n_dot_v * n_dot_l + 0.000001;
    131     return (distribution * geometry) / denom;
    132 }
    133 
    134 fn lighting(
    135     normal: vec3<f32>,
    136     p_fragment: vec3<f32>,
    137     p_camera: vec3<f32>,
    138     p_light: vec3<f32>,
    139     c_light: vec3<f32>,
    140     albedo: vec3<f32>,
    141     roughness: f32,
    142     metalness: f32,
    143 ) -> vec3<f32> {
    144     let v = normalize(p_camera - p_fragment);
    145     let l = normalize(p_light - p_fragment);
    146     let h = normalize(v + l);
    147 
    148     let n_dot_v = max(dot(normal, v), 0.0);
    149     let n_dot_l = max(dot(normal, l), 0.0);
    150     let n_dot_h = max(dot(normal, h), 0.0);
    151     let v_dot_h = max(dot(v, h), 0.0);
    152 
    153     let f0 = mix(vec3<f32>(0.04), albedo, metalness);
    154     let k_s = fresnel_schlick(f0, v_dot_h);
    155     let k_d = 1.0 - k_s;
    156 
    157     let diffuse = k_d * diffuse_lambert(albedo);
    158     let specular = k_s * cook_torrance(n_dot_v, n_dot_l, n_dot_h, roughness);
    159     let brdf = (diffuse + specular) * n_dot_l;
    160     return brdf * c_light;
    161 }
    162 
    163 @fragment fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
    164     let material = instances[in.instance_id].material;
    165     let uv = vec2<f32>(in.tex_coord.x, 1 - in.tex_coord.y);
    166     var albedo = textureSample(material_texture, material_sampler, uv, material.albedo).rgb;
    167     albedo = pow(albedo, vec3<f32>(2.2));
    168     var normal = textureSample(material_texture, material_sampler, uv, material.normal).rgb;
    169     normal = normalize(normal * 2.0 - 1.0);
    170     var roughness = textureSample(material_texture, material_sampler, uv, material.roughness).r;
    171     var metalness = textureSample(material_texture, material_sampler, uv, material.metalness).r;
    172 
    173     let light_color = normalize(vec3<f32>(23.47, 21.31, 20.79));
    174     var c = lighting(
    175         normal,
    176         in.pt_fragment,
    177         in.pt_camera,
    178         in.pt_light,
    179         light_color,
    180         albedo.rgb,
    181         roughness,
    182         metalness,
    183     );
    184 
    185     let ambient = vec3<f32>(0.03) * albedo;
    186     c += ambient;
    187 
    188     c = c / (c + 1.0);
    189     c = pow(c, vec3<f32>(1.0/2.2));
    190 
    191     return vec4<f32>(c, 1.0);
    192 }