From 67f10c940aa71863debff7c4b8be22cfdd7119b8 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 24 Dec 2023 11:08:44 -0800 Subject: [PATCH] Fixed uniform alignment & starfield flicker --- src/render/globaldata.rs | 28 +++++++++++++++------- src/render/gpustate.rs | 13 +++++++---- src/render/shaders/sprite.wgsl | 17 +++++++------- src/render/shaders/starfield.wgsl | 39 +++++++++++++++++++------------ 4 files changed, 60 insertions(+), 37 deletions(-) diff --git a/src/render/globaldata.rs b/src/render/globaldata.rs index bc2ad06..8bbc4bf 100644 --- a/src/render/globaldata.rs +++ b/src/render/globaldata.rs @@ -11,23 +11,33 @@ pub struct GlobalData { #[repr(C)] #[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] +// Uniforms require uniform alignment. +// Since the largest value in this array is a [f32; 2], +// all smaller values must be padded. +// also, [f32; 3] are aligned as [f32; 4] +// (since alignments must be powers of two) pub struct GlobalDataContent { /// Camera position, in game units pub camera_position: [f32; 2], - /// Camera zoom value, in game units - pub camera_zoom: f32, + /// Camera zoom value, in game units. + /// Only first component has meaning. + pub camera_zoom: [f32; 2], - /// Aspect ratio of window (width / height) - pub window_aspect: f32, + /// Size ratio of window, in physical pixels + pub window_size: [f32; 2], + + // Aspect ration of window + /// Only first component has meaning. + pub window_aspect: [f32; 2], /// Texture index of starfield sprites - pub starfield_texture: u32, + /// Only first component has meaning. + pub starfield_texture: [u32; 2], - // Size of (square) starfield tile, in game units - pub starfield_tile_size: f32, - - pub padding: [f32; 3], + // Size of (square) starfield tiles, in game units + /// Only first component has meaning. + pub starfield_tile_size: [f32; 2], } impl GlobalDataContent { diff --git a/src/render/gpustate.rs b/src/render/gpustate.rs index c3089cb..ea31b82 100644 --- a/src/render/gpustate.rs +++ b/src/render/gpustate.rs @@ -362,11 +362,14 @@ impl GPUState { 0, bytemuck::cast_slice(&[GlobalDataContent { camera_position: game.camera.pos.into(), - camera_zoom: game.camera.zoom, - window_aspect: self.window_aspect, - starfield_texture: 1, - starfield_tile_size: STARFIELD_SIZE as f32, - padding: Default::default(), + camera_zoom: [game.camera.zoom, 0.0], + window_size: [ + self.window_size.width as f32, + self.window_size.height as f32, + ], + window_aspect: [self.window_aspect, 0.0], + starfield_texture: [1, 0], + starfield_tile_size: [STARFIELD_SIZE as f32, 0.0], }]), ); diff --git a/src/render/shaders/sprite.wgsl b/src/render/shaders/sprite.wgsl index 446064d..201c131 100644 --- a/src/render/shaders/sprite.wgsl +++ b/src/render/shaders/sprite.wgsl @@ -24,10 +24,11 @@ struct VertexOutput { var global: GlobalUniform; struct GlobalUniform { camera_position: vec2, - camera_zoom: f32, - window_aspect: f32, - starfield_texture: u32, - starfield_tile_size: f32 + camera_zoom: vec2, + window_size: vec2, + window_aspect: vec2, + starfield_texture: vec2, + starfield_tile_size: vec2, }; @@ -39,7 +40,7 @@ fn vertex_shader_main( // Apply sprite aspect ratio & scale factor // This must be done *before* rotation. - let scale = instance.height / global.camera_zoom; + let scale = instance.height / global.camera_zoom.x; var pos: vec2 = vec2( vertex.position.x * instance.aspect * scale, vertex.position.y * scale @@ -53,13 +54,13 @@ fn vertex_shader_main( // Apply screen aspect ratio, again preserving height. // This must be done AFTER rotation... think about it! - pos = pos / vec2(global.window_aspect, 1.0); + pos = pos / vec2(global.window_aspect.x, 1.0); // Translate pos = pos + ( // Don't forget to correct distance for screen aspect ratio too! - (instance.position / global.camera_zoom) - / vec2(global.window_aspect, 1.0) + (instance.position / global.camera_zoom.x) + / vec2(global.window_aspect.x, 1.0) ); var out: VertexOutput; diff --git a/src/render/shaders/starfield.wgsl b/src/render/shaders/starfield.wgsl index bcabc40..75f9797 100644 --- a/src/render/shaders/starfield.wgsl +++ b/src/render/shaders/starfield.wgsl @@ -19,10 +19,11 @@ struct VertexOutput { var global: GlobalUniform; struct GlobalUniform { camera_position: vec2, - camera_zoom: f32, - window_aspect: f32, - starfield_texture: u32, - starfield_tile_size: f32 + camera_zoom: vec2, + window_size: vec2, + window_aspect: vec2, + starfield_texture: vec2, + starfield_tile_size: vec2, }; @@ -39,33 +40,41 @@ fn vertex_shader_main( // Center of the tile the camera is currently in, in game coordinates. // x div y = x - (x mod y) let tile_center = ( - global.camera_position + global.camera_position.xy - ( fmod( - global.camera_position + global.starfield_tile_size / 2.0, - global.starfield_tile_size - ) - global.starfield_tile_size / 2.0 + global.camera_position.xy + global.starfield_tile_size.x / 2.0, + global.starfield_tile_size.x + ) - global.starfield_tile_size.x / 2.0 ) ); // Apply sprite aspect ratio & scale factor // also applies screen aspect ratio - let scale = instance.height / global.camera_zoom; + var scale: f32 = instance.height / global.camera_zoom.x; + + // Minimum scale to prevent flicker at large zoom levels + var real_size = scale * global.window_size.xy; + if (real_size.x < 2.0 || real_size.y < 2.0) { + scale = 2.0 / min(global.window_size.x, global.window_size.y); + } + + var pos: vec2 = vec2( - vertex.position.x * scale / global.window_aspect, + vertex.position.x * scale / global.window_aspect.x, vertex.position.y * scale ); // World position relative to camera // (Note that instance position is in a different // coordinate system than usual) - let camera_pos = (instance.position + tile_center) - global.camera_position; + let camera_pos = (instance.position + tile_center) - global.camera_position.xy; // Translate pos = pos + ( // Don't forget to correct distance for screen aspect ratio too! - (camera_pos / (global.camera_zoom * (instance.parallax))) - / vec2(global.window_aspect, 1.0) + (camera_pos / (global.camera_zoom.x * (instance.parallax))) + / vec2(global.window_aspect.x, 1.0) ); var out: VertexOutput; @@ -84,8 +93,8 @@ var sampler_array: binding_array; @fragment fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4 { return textureSampleLevel( - texture_array[1], - sampler_array[1], + texture_array[global.starfield_texture.x], + sampler_array[global.starfield_texture.x], in.texture_coords, 0.0 ).rgba * vec4(0.5, 0.5, 0.5, 1.0);