struct VertexOutput { @builtin(position) position: vec4f, @location(0) tex_coord: vec2f, } struct LayerUniforms { resolution: vec2f, center: vec2f, size: vec2f, rotation_radians: f32, opacity: f32, flip_x: f32, flip_y: f32, _padding: vec2f, } @group(0) @binding(0) var source_texture: texture_2d; @group(0) @binding(1) var source_sampler: sampler; @group(1) @binding(0) var uniforms: LayerUniforms; fn rotate_inverse(point: vec2f, angle: f32) -> vec2f { let c = cos(angle); let s = sin(angle); return vec2f( point.x * c + point.y * s, -point.x * s + point.y * c, ); } @fragment fn fragment_main(input: VertexOutput) -> @location(0) vec4f { let pixel = input.tex_coord * uniforms.resolution; let local = rotate_inverse(pixel - uniforms.center, uniforms.rotation_radians); let uv = vec2f( local.x / uniforms.size.x + 0.5, local.y / uniforms.size.y + 0.5, ); if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { return vec4f(0.0, 0.0, 0.0, 0.0); } let sample_uv = vec2f( select(uv.x, 1.0 - uv.x, uniforms.flip_x > 0.5), select(uv.y, 1.0 - uv.y, uniforms.flip_y > 0.5), ); let color = textureSampleLevel(source_texture, source_sampler, sample_uv, 0.0); return vec4f(color.rgb, color.a * uniforms.opacity); }