feat: 添加 app/theme/ui/matrix/render 模块,重构 shader

This commit is contained in:
lennlouisgeek
2026-05-18 02:17:46 +08:00
parent e0fbbd46a6
commit aff9c2a75c
7 changed files with 947 additions and 576 deletions

121
src/matrix.rs Normal file
View File

@@ -0,0 +1,121 @@
pub const MATRIX_ROWS: u32 = 12;
pub const MATRIX_COLS: u32 = 7;
const BASE_MATRIX_SPAN: f32 = 24.0;
const MATRIX_SPAN_GROWTH: f32 = 0.6;
const MIN_MATRIX_SPAN: f32 = 24.0;
const MAX_MATRIX_SPAN: f32 = 58.0;
const MIN_CELL_SPACING: f32 = 0.52;
const MAX_CELL_SPACING: f32 = 3.8;
const MIN_BOARD_PADDING: f32 = 2.6;
const MAX_BOARD_PADDING: f32 = 6.8;
const MATRIX_OFFSET_Y: f32 = -2.4;
const MATRIX_OFFSET_Z: f32 = 12.0;
const HEIGHT_SCALE: f32 = 10.6;
const BASE_HEIGHT: f32 = 0.12;
const CAMERA_FOV: f32 = 36.0;
const CAMERA_DISTANCE_MIN: f32 = 30.0;
const CAMERA_DISTANCE_MAX: f32 = 122.0;
const CAMERA_FIT_PADDING: f32 = 1.04;
const CAMERA_ELEVATION_DEG: f32 = 64.0;
const CAMERA_TARGET_X: f32 = 0.0;
const CAMERA_TARGET_Y: f32 = MATRIX_OFFSET_Y + 0.2;
const CAMERA_TARGET_Z: f32 = MATRIX_OFFSET_Z - 0.4;
pub struct MatrixLayout {
pub cell_spacing: f32,
pub board_width: f32,
pub board_depth: f32,
pub board_padding: f32,
pub label_float_offset: f32,
}
impl MatrixLayout {
pub fn new(rows: u32, cols: u32) -> Self {
let longest_edge = rows.max(cols).max(1) as f32;
let edge_span = (longest_edge - 1.0).max(1.0);
let target_span = (BASE_MATRIX_SPAN + edge_span * MATRIX_SPAN_GROWTH)
.clamp(MIN_MATRIX_SPAN, MAX_MATRIX_SPAN);
let cell_spacing = (target_span / edge_span).clamp(MIN_CELL_SPACING, MAX_CELL_SPACING);
let board_width = cols.max(1) as f32 * cell_spacing;
let board_depth = rows.max(1) as f32 * cell_spacing;
let board_padding = (cell_spacing * 1.62).clamp(MIN_BOARD_PADDING, MAX_BOARD_PADDING);
let label_float_offset = (cell_spacing * 0.42).clamp(0.36, 1.12);
Self {
cell_spacing,
board_width,
board_depth,
board_padding,
label_float_offset,
}
}
}
pub fn build_view_projection(aspect: f32, layout: &MatrixLayout) -> [[f32; 4]; 4] {
let camera_distance = fit_camera_distance(
layout.board_width,
layout.board_depth,
layout.board_padding,
aspect,
);
let elevation = CAMERA_ELEVATION_DEG.to_radians();
let target = glam::Vec3::new(CAMERA_TARGET_X, CAMERA_TARGET_Y, CAMERA_TARGET_Z);
let eye = glam::Vec3::new(
CAMERA_TARGET_X,
CAMERA_TARGET_Y + elevation.sin() * camera_distance,
CAMERA_TARGET_Z + elevation.cos() * camera_distance,
);
let view = glam::Mat4::look_at_rh(eye, target, glam::Vec3::Y);
let projection = glam::Mat4::perspective_rh(CAMERA_FOV.to_radians(), aspect, 0.1, 500.0);
let open_gl_to_wgpu = glam::Mat4::from_cols_array(&[
1.0, 0.0, 0.0, 0.0, //
0.0, 1.0, 0.0, 0.0, //
0.0, 0.0, 0.5, 0.0, //
0.0, 0.0, 0.5, 1.0,
]);
(open_gl_to_wgpu * projection * view).to_cols_array_2d()
}
pub fn glyph_world_position(
row: u32,
col: u32,
rows: u32,
cols: u32,
layout: &MatrixLayout,
time: f32,
) -> ([f32; 4], f32) {
let normalized = demo_pressure(row, col, time);
let height = BASE_HEIGHT + normalized.powf(0.9) * HEIGHT_SCALE;
let x = (col as f32 - cols as f32 / 2.0 + 0.5) * layout.cell_spacing;
let z = (row as f32 - rows as f32 / 2.0 + 0.5) * layout.cell_spacing;
(
[
x,
MATRIX_OFFSET_Y + height + layout.label_float_offset,
MATRIX_OFFSET_Z + z,
1.0,
],
normalized,
)
}
fn demo_pressure(row: u32, col: u32, time: f32) -> f32 {
let seed = ((row * 17 + col * 31) as f32).sin() * 43_758.547;
let phase = seed.fract() * std::f32::consts::TAU;
let slow = 0.5 + 0.5 * (time * 1.35 + phase).sin();
let row_wave = 0.5 + 0.5 * (time * 0.82 + row as f32 * 0.54 + col as f32 * 0.19).sin();
(slow * row_wave).powf(0.72).clamp(0.0, 1.0)
}
fn fit_camera_distance(board_width: f32, board_depth: f32, board_padding: f32, aspect: f32) -> f32 {
let padded_width = board_width + board_padding * 2.0;
let padded_depth = board_depth + board_padding * 2.0;
let safe_aspect = aspect.max(0.5);
let effective_half_span = (padded_depth * 0.5).max((padded_width * 0.5) / safe_aspect);
let fov_radians = (CAMERA_FOV * 0.5).to_radians();
let fit_distance = (effective_half_span / fov_radians.tan()) * CAMERA_FIT_PADDING;
fit_distance.clamp(CAMERA_DISTANCE_MIN, CAMERA_DISTANCE_MAX)
}