refactor: 迁移 shader.wgsl 至 static/wgsl/,新增 model 模块
This commit is contained in:
308
static/wgsl/shader.wgsl
Normal file
308
static/wgsl/shader.wgsl
Normal file
@@ -0,0 +1,308 @@
|
||||
struct MatrixUniform {
|
||||
view_proj: mat4x4f,
|
||||
viewport: vec4f,
|
||||
glyph: vec4f,
|
||||
color: vec4f,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> u: MatrixUniform;
|
||||
|
||||
fn saturate(value: f32) -> f32 {
|
||||
return clamp(value, 0.0, 1.0);
|
||||
}
|
||||
|
||||
fn range_stop_color(index: u32) -> vec3f {
|
||||
switch index {
|
||||
case 0u: {
|
||||
return vec3f(0.140, 0.690, 0.890);
|
||||
}
|
||||
case 1u: {
|
||||
return vec3f(0.250, 0.760, 0.380);
|
||||
}
|
||||
case 2u: {
|
||||
return vec3f(1.000, 0.670, 0.180);
|
||||
}
|
||||
default: {
|
||||
return vec3f(1.000, 0.255, 0.190);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_range_color(value: f32) -> vec3f {
|
||||
let t = saturate(value);
|
||||
|
||||
if (t <= 0.33) {
|
||||
let local = smoothstep(0.0, 0.33, t);
|
||||
return mix(range_stop_color(0u), range_stop_color(1u), local);
|
||||
}
|
||||
|
||||
if (t <= 0.66) {
|
||||
let local = smoothstep(0.33, 0.66, t);
|
||||
return mix(range_stop_color(1u), range_stop_color(2u), local);
|
||||
}
|
||||
|
||||
let local = smoothstep(0.66, 1.0, t);
|
||||
return mix(range_stop_color(2u), range_stop_color(3u), local);
|
||||
}
|
||||
|
||||
|
||||
// background
|
||||
struct BackgroundVertexOutput {
|
||||
@builtin(position) clip_position: vec4f,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_background(@builtin(vertex_index) vertex_index: u32) -> BackgroundVertexOutput {
|
||||
let positions = array<vec2f, 3>(
|
||||
vec2f(-1.0, -3.0),
|
||||
vec2f(3.0, 1.0),
|
||||
vec2f(-1.0, 1.0),
|
||||
);
|
||||
|
||||
var out: BackgroundVertexOutput;
|
||||
out.clip_position = vec4f(positions[vertex_index], 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_background(@builtin(position) frag_coord: vec4f) -> @location(0) vec4f {
|
||||
let pixel = frag_coord.xy;
|
||||
let viewport = u.viewport.xy;
|
||||
let uv = pixel / max(viewport, vec2f(1.0, 1.0));
|
||||
var color = mix(vec3f(0.112, 0.126, 0.160), vec3f(0.145, 0.160, 0.198), 1.0 - uv.y);
|
||||
let vignette = smoothstep(0.18, 0.92, length((uv - vec2f(0.52, 0.48)) * vec2f(viewport.x / viewport.y, 1.0)));
|
||||
color *= 1.0 - vignette * 0.30;
|
||||
color += vec3f(0.035, 0.070, 0.090) * (1.0 - smoothstep(0.0, 0.9, abs(uv.y - 0.52)));
|
||||
|
||||
let track_width = clamp(viewport.x * 0.42, 260.0, 560.0);
|
||||
let track_height = 12.0;
|
||||
let track_center = vec2f(viewport.x * 0.5, viewport.y - 38.0);
|
||||
let local = pixel - track_center;
|
||||
let half_size = vec2f(track_width * 0.5, track_height * 0.5);
|
||||
let inside_track = step(abs(local.x), half_size.x) * step(abs(local.y), half_size.y);
|
||||
let border = step(abs(local.x), half_size.x + 1.0) * step(abs(local.y), half_size.y + 1.0) - inside_track;
|
||||
let t = saturate((local.x + half_size.x) / track_width);
|
||||
|
||||
if (inside_track > 0.5) {
|
||||
let shade = mix(0.72, 1.0, 1.0 - saturate((local.y + half_size.y) / track_height));
|
||||
color = mix(color, sample_range_color(t) * shade, 0.96);
|
||||
}
|
||||
|
||||
color = max(color, vec3f(0.34, 0.41, 0.46) * border);
|
||||
|
||||
let tick_area = step(abs(local.x), half_size.x) * step(abs(local.y), half_size.y + 8.0);
|
||||
if (tick_area > 0.5) {
|
||||
let ticks = array<f32, 4>(0.0, 0.33, 0.66, 1.0);
|
||||
for (var index = 0u; index < 4u; index = index + 1u) {
|
||||
let tick_x = (ticks[index] - 0.5) * track_width;
|
||||
let tick = step(abs(local.x - tick_x), 1.0) * step(abs(local.y), half_size.y + 7.0);
|
||||
color = max(color, vec3f(0.78, 0.84, 0.90) * tick);
|
||||
}
|
||||
}
|
||||
|
||||
return vec4f(color, 1.0);
|
||||
}
|
||||
|
||||
|
||||
// glyph
|
||||
struct GlyphVertexInput {
|
||||
@location(0) local: vec2f,
|
||||
}
|
||||
|
||||
struct GlyphInstanceInput {
|
||||
@location(1) world_position: vec4f,
|
||||
@location(2) style: vec4f,
|
||||
}
|
||||
|
||||
struct GlyphVertexOutput {
|
||||
@builtin(position) clip_position: vec4f,
|
||||
@location(0) local: vec2f,
|
||||
@location(1) intensity: f32,
|
||||
@location(2) display_value: f32,
|
||||
}
|
||||
|
||||
fn rect_alpha(point: vec2f, center: vec2f, half_size: vec2f) -> f32 {
|
||||
let delta = abs(point - center) - half_size;
|
||||
let outside = length(max(delta, vec2f(0.0, 0.0)));
|
||||
let inside = min(max(delta.x, delta.y), 0.0);
|
||||
let dist = outside + inside;
|
||||
return 1.0 - smoothstep(0.015, 0.045, dist);
|
||||
}
|
||||
|
||||
fn digit_segment_on(digit: u32, segment: u32) -> bool {
|
||||
switch digit {
|
||||
case 0u: { return segment != 6u; }
|
||||
case 1u: { return segment == 1u || segment == 2u; }
|
||||
case 2u: { return segment == 0u || segment == 1u || segment == 6u || segment == 4u || segment == 3u; }
|
||||
case 3u: { return segment == 0u || segment == 1u || segment == 6u || segment == 2u || segment == 3u; }
|
||||
case 4u: { return segment == 5u || segment == 6u || segment == 1u || segment == 2u; }
|
||||
case 5u: { return segment == 0u || segment == 5u || segment == 6u || segment == 2u || segment == 3u; }
|
||||
case 6u: { return segment == 0u || segment == 5u || segment == 4u || segment == 3u || segment == 2u || segment == 6u; }
|
||||
case 7u: { return segment == 0u || segment == 1u || segment == 2u; }
|
||||
case 8u: { return true; }
|
||||
default: { return segment == 0u || segment == 1u || segment == 2u || segment == 3u || segment == 5u || segment == 6u; }
|
||||
}
|
||||
}
|
||||
|
||||
fn seven_segment_digit_alpha(local: vec2f, digit: u32) -> f32 {
|
||||
var alpha = 0.0;
|
||||
if (digit_segment_on(digit, 0u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(0.0, 0.70), vec2f(0.38, 0.078)));
|
||||
}
|
||||
if (digit_segment_on(digit, 1u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(0.39, 0.36), vec2f(0.078, 0.335)));
|
||||
}
|
||||
if (digit_segment_on(digit, 2u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(0.39, -0.36), vec2f(0.078, 0.335)));
|
||||
}
|
||||
if (digit_segment_on(digit, 3u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(0.0, -0.70), vec2f(0.38, 0.078)));
|
||||
}
|
||||
if (digit_segment_on(digit, 4u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(-0.39, -0.36), vec2f(0.078, 0.335)));
|
||||
}
|
||||
if (digit_segment_on(digit, 5u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(-0.39, 0.36), vec2f(0.078, 0.335)));
|
||||
}
|
||||
if (digit_segment_on(digit, 6u)) {
|
||||
alpha = max(alpha, rect_alpha(local, vec2f(0.0, 0.0), vec2f(0.35, 0.075)));
|
||||
}
|
||||
return alpha;
|
||||
}
|
||||
|
||||
fn digit_count(value: u32) -> u32 {
|
||||
if (value >= 1000u) {
|
||||
return 4u;
|
||||
}
|
||||
if (value >= 100u) {
|
||||
return 3u;
|
||||
}
|
||||
if (value >= 10u) {
|
||||
return 2u;
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
fn digit_at(value: u32, slot: u32, count: u32) -> u32 {
|
||||
if (count == 4u) {
|
||||
switch slot {
|
||||
case 0u: { return (value / 1000u) % 10u; }
|
||||
case 1u: { return (value / 100u) % 10u; }
|
||||
case 2u: { return (value / 10u) % 10u; }
|
||||
default: { return value % 10u; }
|
||||
}
|
||||
}
|
||||
if (count == 3u) {
|
||||
switch slot {
|
||||
case 0u: { return (value / 100u) % 10u; }
|
||||
case 1u: { return (value / 10u) % 10u; }
|
||||
default: { return value % 10u; }
|
||||
}
|
||||
}
|
||||
if (count == 2u) {
|
||||
return select(value % 10u, (value / 10u) % 10u, slot == 0u);
|
||||
}
|
||||
return value % 10u;
|
||||
}
|
||||
|
||||
fn number_alpha(local: vec2f, display_value: f32) -> f32 {
|
||||
let value = min(u32(max(display_value + 0.5, 0.0)), 9999u);
|
||||
let count = digit_count(value);
|
||||
let count_f = f32(count);
|
||||
let slot_width = 1.74 / count_f;
|
||||
let start_x = -slot_width * (count_f - 1.0) * 0.5;
|
||||
var alpha = 0.0;
|
||||
|
||||
for (var slot = 0u; slot < 4u; slot = slot + 1u) {
|
||||
if (slot < count) {
|
||||
let center_x = start_x + f32(slot) * slot_width;
|
||||
let digit_local = vec2f((local.x - center_x) / (slot_width * 0.78), local.y / 0.92);
|
||||
let digit = digit_at(value, slot, count);
|
||||
let in_slot = step(abs(local.x - center_x), slot_width * 0.48);
|
||||
alpha = max(alpha, seven_segment_digit_alpha(digit_local, digit) * in_slot);
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_glyph(vertex: GlyphVertexInput, instance: GlyphInstanceInput) -> GlyphVertexOutput {
|
||||
let center = u.view_proj * vec4f(instance.world_position.xyz, 1.0);
|
||||
let shaped = pow(saturate(instance.style.x), 0.9);
|
||||
let pixel_size = u.glyph.x * mix(1.08, 2.20, shaped);
|
||||
let ndc_offset = vertex.local * vec2f(pixel_size / u.viewport.x, pixel_size / u.viewport.y) * 2.0;
|
||||
|
||||
var out: GlyphVertexOutput;
|
||||
out.clip_position = vec4f(center.xy + ndc_offset * center.w, center.z, center.w);
|
||||
out.local = vertex.local;
|
||||
out.intensity = instance.style.x;
|
||||
out.display_value = instance.style.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_glyph(in: GlyphVertexOutput) -> @location(0) vec4f {
|
||||
let alpha = number_alpha(in.local, in.display_value);
|
||||
let color = sample_range_color(in.intensity) * mix(0.82, 1.16, saturate(in.intensity));
|
||||
return vec4f(color, alpha);
|
||||
}
|
||||
|
||||
|
||||
// dot
|
||||
struct DotVertexInput {
|
||||
@location(0) local: vec2f,
|
||||
}
|
||||
|
||||
struct DotInstanceInput {
|
||||
@location(1) world_position: vec4f,
|
||||
@location(2) style: vec4f
|
||||
}
|
||||
|
||||
struct DotVertexOutput {
|
||||
@builtin(position) clip_position: vec4f,
|
||||
@location(0) local: vec2f,
|
||||
@location(1) intensity: f32,
|
||||
}
|
||||
|
||||
fn circle_alpha(local: vec2f, radius: f32, softness: f32) -> f32 {
|
||||
let dist = length(local);
|
||||
return 1.0 - smoothstep(radius, radius + softness, dist);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_dot(vertex: DotVertexInput, instance: DotInstanceInput) -> DotVertexOutput {
|
||||
let center = u.view_proj * vec4f(instance.world_position.xyz, 1.0);
|
||||
let intensity = saturate(instance.style.x);
|
||||
let shaped = pow(intensity, 0.9);
|
||||
let pixel_size = u.glyph.x * mix(0.72, 1.85, shaped);
|
||||
let ndc_offset = vertex.local * vec2f(pixel_size / u.viewport.x, pixel_size / u.viewport.y) * 2.0;
|
||||
var out: DotVertexOutput;
|
||||
out.clip_position = vec4f(center.xy + ndc_offset * center.w, center.z, center.w);
|
||||
out.local = vertex.local;
|
||||
out.intensity = intensity;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_dot(in: DotVertexOutput) -> @location(0) vec4f {
|
||||
let intensity = saturate(in.intensity);
|
||||
let base_color = sample_range_color(intensity);
|
||||
|
||||
let core = circle_alpha(in.local, 0.42, 0.055);
|
||||
|
||||
let glow = circle_alpha(in.local, 0.78, 0.18) * intensity * 0.45;
|
||||
|
||||
let highlight_pos = in.local - vec2f(-0.18, 0.20);
|
||||
|
||||
let highlight = circle_alpha(highlight_pos, 0.16, 0.08) * 0.45;
|
||||
|
||||
let brightness = mix(0.82, 1.22, intensity);
|
||||
|
||||
let color = base_color * brightness + vec3f(1.0, 1.0, 1.0) * highlight;
|
||||
|
||||
let alpha = max(core, glow);
|
||||
|
||||
return vec4f(color, alpha);
|
||||
}
|
||||
Reference in New Issue
Block a user