WebGPU Voxel Game
1// Vertex shader
2
3// struct InstanceInput {
4// @location(5) model_matrix_0: vec4<f32>,
5// @location(6) model_matrix_1: vec4<f32>,
6// @location(7) model_matrix_2: vec4<f32>,
7// @location(8) model_matrix_3: vec4<f32>,
8
9// @location(9) normal_matrix_0: vec3<f32>,
10// @location(10) normal_matrix_1: vec3<f32>,
11// @location(11) normal_matrix_2: vec3<f32>,
12// };
13
14struct Camera {
15 view_pos: vec4<f32>,
16 view_proj: mat4x4<f32>,
17}
18@group(1) @binding(0)
19var<uniform> camera: Camera;
20
21@group(1) @binding(1)
22var<uniform> light: Camera;
23
24
25struct VertexInput {
26 @location(0) position: vec3<f32>,
27 @location(1) tex_coords: vec2<f32>,
28 @location(2) normal: vec3<f32>,
29}
30
31struct VertexOutput {
32 @builtin(position) clip_position: vec4<f32>,
33 @location(0) tex_coords: vec2<f32>,
34 @location(1) world_normal: vec3<f32>,
35 @location(2) world_position: vec3<f32>,
36};
37
38@vertex
39fn vs_main(
40 model: VertexInput,
41) -> VertexOutput {
42
43
44 var out: VertexOutput;
45 out.tex_coords = model.tex_coords;
46 out.world_normal = model.normal;
47
48 var world_position: vec4<f32> = vec4<f32>(model.position, 1.0);
49 out.world_position = world_position.xyz;
50 out.clip_position = camera.view_proj * world_position;
51 return out;
52}
53
54
55// Fragment shader
56
57@group(0) @binding(0)
58var t_diffuse: texture_2d<f32>;
59@group(0) @binding(1)
60var s_diffuse: sampler;
61
62@group(1) @binding(2)
63var t_shadow: texture_depth_2d;
64@group(1) @binding(3)
65var s_shadow: sampler_comparison;
66
67// TODO CITE: wgpu/examples/shadow
68fn fetch_shadow(homogeneous_coords: vec4<f32>) -> f32 {
69 if (homogeneous_coords.w <= 0.0) {
70 return 1.0;
71 }
72 // compensate for the Y-flip difference between the NDC and texture coordinates
73 let flip_correction = vec2<f32>(0.5, -0.5);
74 // compute texture coordinates for shadow lookup
75 let proj_correction = 1.0 / homogeneous_coords.w;
76 let light_local = homogeneous_coords.xy * flip_correction * proj_correction + vec2<f32>(0.5, 0.5);
77 // do the lookup, using HW PCF and comparison
78 return textureSampleCompareLevel(t_shadow, s_shadow, light_local, homogeneous_coords.z * proj_correction);
79}
80
81@fragment
82fn fs_main(in: VertexOutput, @builtin(front_facing) face: bool) -> @location(0) vec4<f32> {
83 let light_color = vec3<f32>(1.0, 1.0, 1.0);
84
85 let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
86
87 let ambient_strength = 1.0;
88 let ambient_color = light_color * ambient_strength;
89
90 let light_dir = normalize(light.view_pos.xyz - in.world_position.xyz);
91 let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0);
92 let diffuse_color = light_color * diffuse_strength;
93
94 let view_dir = normalize(camera.view_pos.xyz - in.world_position.xyz);
95 let half_dir = normalize(view_dir + light_dir);
96
97 // https://github.com/mcclure/webgpu-tutorial-rs/blob/webgpu-tutorial/src/shader.wgsl
98 // This one-dimensional separable blur filter samples five points and averages them by different amounts.
99 // If we do it on two separate axes, we get a 2d blur.
100 // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
101
102 // The weights for the center, one-point-out, and two-point-out samples
103 const WEIGHT0 = 0.2270270270;
104 const WEIGHT1 = 0.3162162162;
105 const WEIGHT2 = 0.0702702703;
106
107 // The distances-from-center for the samples
108 const OFFSET1 = 1.3846153846;
109 const OFFSET2 = 3.2307692308;
110
111 let blur_resolution = vec2<f32>(60.0, 60.0);
112
113 var shadow_guassian = 0.0;
114 shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position, 1.0)) * WEIGHT0;
115 shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy + blur_resolution * OFFSET1, in.world_position.z, 1.0)) * WEIGHT1;
116 shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy - blur_resolution * OFFSET1, in.world_position.z, 1.0)) * WEIGHT1;
117 shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy + blur_resolution * OFFSET2, in.world_position.z, 1.0)) * WEIGHT2;
118 shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy - blur_resolution * OFFSET2, in.world_position.z, 1.0)) * WEIGHT2;
119
120 let shadow_strength = 0.7;
121 let shadow = fetch_shadow(light.view_proj * vec4<f32>(in.world_position, 1.0));
122
123
124 let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0);
125
126 // Disable specular effects in shadow
127 let specular_color = specular_strength * light_color * shadow;
128
129 let result = (ambient_color + diffuse_color + specular_color) * (shadow_guassian * shadow_strength + (1.0 - shadow_strength)) * object_color.xyz;
130 // let result = (ambient_color + diffuse_color + specular_color) * (shadow_guassian * shadow_strength + (1.0 - shadow_strength));
131
132 return vec4<f32>(result, object_color.a);
133}