Fixed uniform alignment & starfield flicker

master
Mark 2023-12-24 11:08:44 -08:00
parent cd6537f971
commit 67f10c940a
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
4 changed files with 60 additions and 37 deletions

View File

@ -11,23 +11,33 @@ pub struct GlobalData {
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] #[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 { pub struct GlobalDataContent {
/// Camera position, in game units /// Camera position, in game units
pub camera_position: [f32; 2], pub camera_position: [f32; 2],
/// Camera zoom value, in game units /// Camera zoom value, in game units.
pub camera_zoom: f32, /// Only first component has meaning.
pub camera_zoom: [f32; 2],
/// Aspect ratio of window (width / height) /// Size ratio of window, in physical pixels
pub window_aspect: f32, pub window_size: [f32; 2],
// Aspect ration of window
/// Only first component has meaning.
pub window_aspect: [f32; 2],
/// Texture index of starfield sprites /// 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 // Size of (square) starfield tiles, in game units
pub starfield_tile_size: f32, /// Only first component has meaning.
pub starfield_tile_size: [f32; 2],
pub padding: [f32; 3],
} }
impl GlobalDataContent { impl GlobalDataContent {

View File

@ -362,11 +362,14 @@ impl GPUState {
0, 0,
bytemuck::cast_slice(&[GlobalDataContent { bytemuck::cast_slice(&[GlobalDataContent {
camera_position: game.camera.pos.into(), camera_position: game.camera.pos.into(),
camera_zoom: game.camera.zoom, camera_zoom: [game.camera.zoom, 0.0],
window_aspect: self.window_aspect, window_size: [
starfield_texture: 1, self.window_size.width as f32,
starfield_tile_size: STARFIELD_SIZE as f32, self.window_size.height as f32,
padding: Default::default(), ],
window_aspect: [self.window_aspect, 0.0],
starfield_texture: [1, 0],
starfield_tile_size: [STARFIELD_SIZE as f32, 0.0],
}]), }]),
); );

View File

@ -24,10 +24,11 @@ struct VertexOutput {
var<uniform> global: GlobalUniform; var<uniform> global: GlobalUniform;
struct GlobalUniform { struct GlobalUniform {
camera_position: vec2<f32>, camera_position: vec2<f32>,
camera_zoom: f32, camera_zoom: vec2<f32>,
window_aspect: f32, window_size: vec2<f32>,
starfield_texture: u32, window_aspect: vec2<f32>,
starfield_tile_size: f32 starfield_texture: vec2<u32>,
starfield_tile_size: vec2<f32>,
}; };
@ -39,7 +40,7 @@ fn vertex_shader_main(
// Apply sprite aspect ratio & scale factor // Apply sprite aspect ratio & scale factor
// This must be done *before* rotation. // This must be done *before* rotation.
let scale = instance.height / global.camera_zoom; let scale = instance.height / global.camera_zoom.x;
var pos: vec2<f32> = vec2<f32>( var pos: vec2<f32> = vec2<f32>(
vertex.position.x * instance.aspect * scale, vertex.position.x * instance.aspect * scale,
vertex.position.y * scale vertex.position.y * scale
@ -53,13 +54,13 @@ fn vertex_shader_main(
// Apply screen aspect ratio, again preserving height. // Apply screen aspect ratio, again preserving height.
// This must be done AFTER rotation... think about it! // This must be done AFTER rotation... think about it!
pos = pos / vec2<f32>(global.window_aspect, 1.0); pos = pos / vec2<f32>(global.window_aspect.x, 1.0);
// Translate // Translate
pos = pos + ( pos = pos + (
// Don't forget to correct distance for screen aspect ratio too! // Don't forget to correct distance for screen aspect ratio too!
(instance.position / global.camera_zoom) (instance.position / global.camera_zoom.x)
/ vec2<f32>(global.window_aspect, 1.0) / vec2<f32>(global.window_aspect.x, 1.0)
); );
var out: VertexOutput; var out: VertexOutput;

View File

@ -19,10 +19,11 @@ struct VertexOutput {
var<uniform> global: GlobalUniform; var<uniform> global: GlobalUniform;
struct GlobalUniform { struct GlobalUniform {
camera_position: vec2<f32>, camera_position: vec2<f32>,
camera_zoom: f32, camera_zoom: vec2<f32>,
window_aspect: f32, window_size: vec2<f32>,
starfield_texture: u32, window_aspect: vec2<f32>,
starfield_tile_size: f32 starfield_texture: vec2<u32>,
starfield_tile_size: vec2<f32>,
}; };
@ -39,33 +40,41 @@ fn vertex_shader_main(
// Center of the tile the camera is currently in, in game coordinates. // Center of the tile the camera is currently in, in game coordinates.
// x div y = x - (x mod y) // x div y = x - (x mod y)
let tile_center = ( let tile_center = (
global.camera_position global.camera_position.xy
- ( - (
fmod( fmod(
global.camera_position + global.starfield_tile_size / 2.0, global.camera_position.xy + global.starfield_tile_size.x / 2.0,
global.starfield_tile_size global.starfield_tile_size.x
) - global.starfield_tile_size / 2.0 ) - global.starfield_tile_size.x / 2.0
) )
); );
// Apply sprite aspect ratio & scale factor // Apply sprite aspect ratio & scale factor
// also applies screen aspect ratio // 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<f32> = vec2<f32>( var pos: vec2<f32> = vec2<f32>(
vertex.position.x * scale / global.window_aspect, vertex.position.x * scale / global.window_aspect.x,
vertex.position.y * scale vertex.position.y * scale
); );
// World position relative to camera // World position relative to camera
// (Note that instance position is in a different // (Note that instance position is in a different
// coordinate system than usual) // 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 // Translate
pos = pos + ( pos = pos + (
// Don't forget to correct distance for screen aspect ratio too! // Don't forget to correct distance for screen aspect ratio too!
(camera_pos / (global.camera_zoom * (instance.parallax))) (camera_pos / (global.camera_zoom.x * (instance.parallax)))
/ vec2<f32>(global.window_aspect, 1.0) / vec2<f32>(global.window_aspect.x, 1.0)
); );
var out: VertexOutput; var out: VertexOutput;
@ -84,8 +93,8 @@ var sampler_array: binding_array<sampler>;
@fragment @fragment
fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSampleLevel( return textureSampleLevel(
texture_array[1], texture_array[global.starfield_texture.x],
sampler_array[1], sampler_array[global.starfield_texture.x],
in.texture_coords, in.texture_coords,
0.0 0.0
).rgba * vec4<f32>(0.5, 0.5, 0.5, 1.0); ).rgba * vec4<f32>(0.5, 0.5, 0.5, 1.0);