struct Uniforms {
view_proj: mat4x4<f32>,
wall_freq_and_threshold: vec4<f32>,
disp_and_alpha: vec4<f32>,
uv_and_pad: vec4<f32>,
sun_direction_and_intensity: vec4<f32>,
sun_color: vec4<f32>,
ambient_color: vec4<f32>,
}
struct InstanceData {
model: mat4x4<f32>,
camera_pos_object: vec4<f32>,
}
@group(0) @binding(0)
var<uniform> uniforms: Uniforms;
@group(1) @binding(0)
var ceiling_texture: texture_2d<f32>;
@group(1) @binding(1)
var ceiling_sampler: sampler;
@group(1) @binding(2)
var floor_texture: texture_2d<f32>;
@group(1) @binding(3)
var floor_sampler: sampler;
@group(1) @binding(4)
var wall_xy_texture: texture_2d<f32>;
@group(1) @binding(5)
var wall_xy_sampler: sampler;
@group(1) @binding(6)
var wall_zy_texture: texture_2d<f32>;
@group(1) @binding(7)
var wall_zy_sampler: sampler;
@group(1) @binding(8)
var noise_texture: texture_2d<f32>;
@group(1) @binding(9)
var noise_sampler: sampler;
@group(1) @binding(10)
var furniture_texture: texture_2d<f32>;
@group(1) @binding(11)
var furniture_sampler: sampler;
@group(1) @binding(12)
var exterior_texture: texture_2d<f32>;
@group(1) @binding(13)
var exterior_sampler: sampler;
@group(1) @binding(14)
var cubemap_texture: texture_cube<f32>;
@group(1) @binding(15)
var cubemap_sampler: sampler;
@group(2) @binding(0)
var<storage, read> instances: array<InstanceData>;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
@location(2) texcoord: vec2<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) object_position: vec3<f32>,
@location(1) texcoord: vec2<f32>,
@location(2) lighting: vec4<f32>,
@location(3) normal_object: vec3<f32>,
@location(4) reflection: vec3<f32>,
@location(5) camera_pos_object: vec3<f32>,
@location(6) @interpolate(flat) model_scale: vec3<f32>,
}
@vertex
fn vertex_main(input: VertexInput, @builtin(instance_index) instance_index: u32) -> VertexOutput {
let instance = instances[instance_index];
let model = instance.model;
let camera_pos_obj = instance.camera_pos_object.xyz;
let world_position = model * vec4<f32>(input.position, 1.0);
let clip_position = uniforms.view_proj * world_position;
let world_normal = normalize((model * vec4<f32>(input.normal, 0.0)).xyz);
let world_camera_dir = normalize(world_position.xyz - (model * vec4<f32>(camera_pos_obj, 1.0)).xyz);
let reflection = reflect(world_camera_dir, world_normal);
let light_dir = normalize(uniforms.sun_direction_and_intensity.xyz);
let sun_intensity = uniforms.sun_direction_and_intensity.w;
let light_strength = dot(world_normal, light_dir);
let lighting = clamp(light_strength, 0.0, 1.0) * sun_intensity * vec4<f32>(uniforms.sun_color.xyz, 1.0) + uniforms.ambient_color;
let scale = vec3<f32>(length(model[0].xyz), length(model[1].xyz), length(model[2].xyz));
let abs_normal = abs(input.normal);
var tex_scale: vec2<f32>;
if (abs_normal.x > 0.5) {
tex_scale = vec2<f32>(scale.z, scale.y);
} else if (abs_normal.y > 0.5) {
tex_scale = vec2<f32>(scale.x, scale.z);
} else {
tex_scale = vec2<f32>(scale.x, scale.y);
}
var output: VertexOutput;
output.clip_position = clip_position;
output.object_position = input.position;
output.texcoord = input.texcoord * tex_scale * uniforms.uv_and_pad.x;
output.lighting = lighting;
output.normal_object = input.normal;
output.reflection = reflection;
output.camera_pos_object = camera_pos_obj;
output.model_scale = scale;
return output;
}
fn calculate_noise(position: vec3<f32>) -> f32 {
let noise_x = textureSample(noise_texture, noise_sampler, position.xy / 32.0).r;
let noise_y = textureSample(noise_texture, noise_sampler, position.zx / 32.0).r;
let noise_z = textureSample(noise_texture, noise_sampler, position.yz / 32.0).r;
return (noise_x + noise_y + noise_z) / 3.0;
}
@fragment
fn fragment_main(input: VertexOutput) -> @location(0) vec4<f32> {
let position = input.object_position;
let camera_position = input.camera_pos_object;
let normal = input.normal_object;
let wall_freq = uniforms.wall_freq_and_threshold.xyz * input.model_scale;
let direction = position - camera_position;
let room_pos = position + vec3<f32>(0.5, 0.5, 0.5);
let room_cam = camera_position + vec3<f32>(0.5, 0.5, 0.5);
let walls_floor = floor(room_pos * wall_freq);
let light_variation_raw = step(calculate_noise(walls_floor), uniforms.wall_freq_and_threshold.w);
let light_variation = light_variation_raw * 0.7 + 0.3;
let dir_step = step(vec3<f32>(0.0), direction);
let walls = (walls_floor + dir_step) / wall_freq;
let ray_fractions = (walls - room_cam) / direction;
let interior_scale = 4.0 * input.model_scale;
let intersection_xy = interior_scale.xy * (room_cam + ray_fractions.z * direction).xy;
let intersection_xz = interior_scale.xz * (room_cam + ray_fractions.y * direction).xz;
let intersection_zy = interior_scale.zy * (room_cam + ray_fractions.x * direction).zy;
let ceiling_colour = textureSample(ceiling_texture, ceiling_sampler, intersection_xz);
let floor_colour = textureSample(floor_texture, floor_sampler, intersection_xz);
let vertical_colour = mix(floor_colour, ceiling_colour, step(0.0, direction.y));
let wall_xy_colour = textureSample(wall_xy_texture, wall_xy_sampler, intersection_xy);
let wall_zy_colour = textureSample(wall_zy_texture, wall_zy_sampler, intersection_zy);
let x_vs_z = step(ray_fractions.x, ray_fractions.z);
var walls_colour = mix(wall_xy_colour, wall_zy_colour, x_vs_z);
let ray_fraction_x_vs_z = mix(ray_fractions.z, ray_fractions.x, x_vs_z);
let xz_vs_y = step(ray_fraction_x_vs_z, ray_fractions.y);
walls_colour = mix(vertical_colour, walls_colour, xz_vs_y);
let ray_fraction_winner = mix(ray_fractions.y, ray_fraction_x_vs_z, xz_vs_y);
let d = dot(position, normal) - uniforms.disp_and_alpha.w;
let alpha_ray_fraction = (d - dot(camera_position, normal)) / dot(direction, normal);
let alpha_plane_point = alpha_ray_fraction * direction + camera_position;
let scaled_alpha = alpha_plane_point * input.model_scale;
let alpha_plane_uv = vec2<f32>(
scaled_alpha.x + scaled_alpha.z,
-scaled_alpha.y
) * 4.0;
let alpha_plane_colour = textureSample(furniture_texture, furniture_sampler, alpha_plane_uv);
let alpha_plane_weight = step(alpha_ray_fraction, ray_fraction_winner) * (1.0 - alpha_plane_colour.a);
var interior_colour = mix(walls_colour, alpha_plane_colour, alpha_plane_weight);
interior_colour = interior_colour * light_variation;
let diffuse_colour = textureSample(exterior_texture, exterior_sampler, input.texcoord);
let cube_colour = textureSample(cubemap_texture, cubemap_sampler, input.reflection);
let blended = mix(
diffuse_colour * input.lighting,
cube_colour + interior_colour,
diffuse_colour.a
);
return vec4<f32>(blended.rgb, 1.0);
}