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)]
#[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 {

View File

@ -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],
}]),
);

View File

@ -24,10 +24,11 @@ struct VertexOutput {
var<uniform> global: GlobalUniform;
struct GlobalUniform {
camera_position: vec2<f32>,
camera_zoom: f32,
window_aspect: f32,
starfield_texture: u32,
starfield_tile_size: f32
camera_zoom: vec2<f32>,
window_size: vec2<f32>,
window_aspect: vec2<f32>,
starfield_texture: vec2<u32>,
starfield_tile_size: vec2<f32>,
};
@ -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<f32> = vec2<f32>(
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<f32>(global.window_aspect, 1.0);
pos = pos / vec2<f32>(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<f32>(global.window_aspect, 1.0)
(instance.position / global.camera_zoom.x)
/ vec2<f32>(global.window_aspect.x, 1.0)
);
var out: VertexOutput;

View File

@ -19,10 +19,11 @@ struct VertexOutput {
var<uniform> global: GlobalUniform;
struct GlobalUniform {
camera_position: vec2<f32>,
camera_zoom: f32,
window_aspect: f32,
starfield_texture: u32,
starfield_tile_size: f32
camera_zoom: vec2<f32>,
window_size: vec2<f32>,
window_aspect: vec2<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.
// 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<f32> = vec2<f32>(
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<f32>(global.window_aspect, 1.0)
(camera_pos / (global.camera_zoom.x * (instance.parallax)))
/ vec2<f32>(global.window_aspect.x, 1.0)
);
var out: VertexOutput;
@ -84,8 +93,8 @@ var sampler_array: binding_array<sampler>;
@fragment
fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4<f32> {
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<f32>(0.5, 0.5, 0.5, 1.0);