mirror of https://github.com/AxioDL/metaforce.git
350 lines
9.6 KiB
WebGPU Shading Language
350 lines
9.6 KiB
WebGPU Shading Language
struct PassTrait {
|
|
constant_color: vec4<f32>;
|
|
tex_idx: i32;
|
|
uv_source: i32;
|
|
tcg_mtx_idx: i32;
|
|
normalize: u32;
|
|
sample_alpha: u32;
|
|
has_constant_color: u32;
|
|
};
|
|
struct TCGUniform {
|
|
mode: i32;
|
|
f0: f32;
|
|
f1: f32;
|
|
f2: f32;
|
|
f3: f32;
|
|
f4: f32;
|
|
f5: f32;
|
|
f6: f32;
|
|
f7: f32;
|
|
f8: f32;
|
|
pad1: u32;
|
|
pad2: u32;
|
|
};
|
|
struct ConstShaderTraits {
|
|
shader_type: u32;
|
|
world_shadow: u32;
|
|
short_uvs: u32;
|
|
alpha_discard: u32;
|
|
samus_reflection: u32;
|
|
@align(16) tcgs: @stride(48) array<TCGUniform, 4>;
|
|
};
|
|
struct MutShaderTraits {
|
|
post_type: u32;
|
|
game_blend_mode: u32;
|
|
model_flags: u32;
|
|
};
|
|
struct ConstTraits {
|
|
shader: ConstShaderTraits;
|
|
lightmap: PassTrait;
|
|
diffuse: PassTrait;
|
|
diffuse_mod: PassTrait;
|
|
emissive: PassTrait;
|
|
specular: PassTrait;
|
|
extended_specular: PassTrait;
|
|
reflection: PassTrait;
|
|
alpha: PassTrait;
|
|
alpha_mod: PassTrait;
|
|
};
|
|
@group(2) @binding(10)
|
|
var<uniform> c_traits: ConstTraits;
|
|
|
|
struct Light {
|
|
pos: vec3<f32>;
|
|
dir: vec3<f32>;
|
|
color: vec4<f32>;
|
|
lin_att: vec3<f32>;
|
|
ang_att: vec3<f32>;
|
|
};
|
|
struct FogState {
|
|
color: vec4<f32>;
|
|
a: f32;
|
|
b: f32;
|
|
c: f32;
|
|
mode: u32;
|
|
};
|
|
struct ModelUniform {
|
|
position: vec3<f32>;
|
|
orientation: vec3<f32>;
|
|
scale: vec3<f32>;
|
|
ambient_color: vec4<f32>;
|
|
lights: array<Light, 8>;
|
|
};
|
|
struct GlobalUniform {
|
|
view: mat4x4<f32>;
|
|
proj: mat4x4<f32>;
|
|
ambient: vec4<f32>;
|
|
lightmap_mul: vec4<f32>;
|
|
fog: FogState;
|
|
seconds: f32;
|
|
};
|
|
@group(1) @binding(0)
|
|
var<uniform> u_model: ModelUniform;
|
|
@group(1) @binding(1)
|
|
var<uniform> u_global: GlobalUniform;
|
|
@group(1) @binding(2)
|
|
var<uniform> u_traits: MutShaderTraits;
|
|
|
|
@group(2) @binding(0)
|
|
var t_lightmap: texture_2d<f32>;
|
|
@group(2) @binding(1)
|
|
var t_diffuse: texture_2d<f32>;
|
|
@group(2) @binding(2)
|
|
var t_diffuse_mod: texture_2d<f32>;
|
|
@group(2) @binding(3)
|
|
var t_emissive: texture_2d<f32>;
|
|
@group(2) @binding(4)
|
|
var t_specular: texture_2d<f32>;
|
|
@group(2) @binding(5)
|
|
var t_extended_specular: texture_2d<f32>;
|
|
@group(2) @binding(6)
|
|
var t_reflection: texture_2d<f32>;
|
|
@group(2) @binding(7)
|
|
var t_alpha: texture_2d<f32>;
|
|
@group(2) @binding(8)
|
|
var t_alpha_mod: texture_2d<f32>;
|
|
@group(2) @binding(9)
|
|
var t_material: texture_2d<f32>;
|
|
|
|
@group(3) @binding(0)
|
|
var s_repeat: sampler;
|
|
@group(3) @binding(1)
|
|
var s_clamp: sampler;
|
|
@group(3) @binding(2)
|
|
var s_reflect: sampler;
|
|
@group(3) @binding(3)
|
|
var s_clamp_edge: sampler;
|
|
|
|
struct VertexOutput {
|
|
@builtin(position) position: vec4<f32>;
|
|
@location(0) lighting: vec3<f32>;
|
|
@location(1) uv_lightmap: vec2<f32>;
|
|
@location(2) uv_diffuse: vec2<f32>;
|
|
@location(3) uv_diffuse_mod: vec2<f32>;
|
|
@location(4) uv_emissive: vec2<f32>;
|
|
@location(5) uv_specular: vec2<f32>;
|
|
@location(6) uv_extended_specular: vec2<f32>;
|
|
@location(7) uv_reflection: vec2<f32>;
|
|
@location(8) uv_alpha: vec2<f32>;
|
|
@location(9) uv_alpha_mod: vec2<f32>;
|
|
};
|
|
|
|
fn make_rotate_x(r: f32) -> mat4x4<f32> {
|
|
var s = sin(r);
|
|
var c = cos(r);
|
|
return mat4x4<f32>(
|
|
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, c, s, 0.0),
|
|
vec4<f32>(0.0, -s, c, 0.0),
|
|
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
fn make_rotate_y(r: f32) -> mat4x4<f32> {
|
|
var s = sin(r);
|
|
var c = cos(r);
|
|
return mat4x4<f32>(
|
|
vec4<f32>(c, 0.0, -s, 0.0),
|
|
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
vec4<f32>(s, 0.0, c, 0.0),
|
|
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
fn make_rotate_z(r: f32) -> mat4x4<f32> {
|
|
var s = sin(r);
|
|
var c = cos(r);
|
|
return mat4x4<f32>(
|
|
vec4<f32>(c, s, 0.0, 0.0),
|
|
vec4<f32>(-s, c, 0.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
fn make_rotate(r: vec3<f32>) -> mat4x4<f32> {
|
|
return make_rotate_z(r.z)
|
|
* make_rotate_y(r.y)
|
|
* make_rotate_x(r.x);
|
|
}
|
|
|
|
fn make_scale(s: vec3<f32>) -> mat4x4<f32> {
|
|
return mat4x4<f32>(
|
|
vec4<f32>(s.x, 0.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, s.y, 0.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, s.z, 0.0),
|
|
vec4<f32>(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
fn make_translate(translate: vec3<f32>) -> mat4x4<f32> {
|
|
return mat4x4<f32>(
|
|
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
vec4<f32>(translate, 1.0)
|
|
);
|
|
}
|
|
|
|
struct TCGMatrix {
|
|
mtx: mat4x4<f32>;
|
|
post_mtx: mat4x4<f32>;
|
|
};
|
|
|
|
let identity_mtx: mat4x4<f32> = mat4x4<f32>(
|
|
vec4<f32>(1.0, 0.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, 1.0, 0.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
);
|
|
|
|
fn process_uv_anim(
|
|
model_matrix: ptr<function, mat4x4<f32>>,
|
|
mv_inv: ptr<function, mat4x4<f32>>,
|
|
traits: PassTrait,
|
|
) -> TCGMatrix {
|
|
var out = TCGMatrix(identity_mtx, identity_mtx);
|
|
var tu = c_traits.shader.tcgs[traits.tcg_mtx_idx];
|
|
switch (tu.mode) {
|
|
case 0: {
|
|
out.mtx = *mv_inv;
|
|
out.mtx[3][3] = 1.0;
|
|
out.post_mtx[0][0] = 0.5;
|
|
out.post_mtx[1][1] = 0.5;
|
|
out.post_mtx[3][0] = 0.5;
|
|
out.post_mtx[3][1] = 0.5;
|
|
}
|
|
case 1: {
|
|
out.mtx = *mv_inv;
|
|
out.mtx[3] = u_global.view * vec4<f32>(u_model.position, 1.0);
|
|
out.mtx[3][3] = 1.0;
|
|
out.post_mtx[0][0] = 0.5;
|
|
out.post_mtx[1][1] = 0.5;
|
|
out.post_mtx[3][0] = 0.5;
|
|
out.post_mtx[3][1] = 0.5;
|
|
}
|
|
case 2: {
|
|
out.mtx[3][0] = u_global.seconds * tu.f2 + tu.f0;
|
|
out.mtx[3][1] = u_global.seconds * tu.f3 + tu.f1;
|
|
}
|
|
case 3: {
|
|
var angle = u_global.seconds * tu.f1 + tu.f0;
|
|
var acos = cos(angle);
|
|
var asin = sin(angle);
|
|
out.mtx[0][0] = acos;
|
|
out.mtx[0][1] = asin;
|
|
out.mtx[1][0] = -asin;
|
|
out.mtx[1][1] = acos;
|
|
out.mtx[3][0] = (1.0 - (acos - asin)) * 0.5;
|
|
out.mtx[3][1] = (1.0 - (asin + acos)) * 0.5;
|
|
}
|
|
case 4: {
|
|
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
|
|
out.mtx[3][0] = trunc(tu.f1 * fract(n)) * tu.f2;
|
|
}
|
|
case 5: {
|
|
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
|
|
out.mtx[3][1] = trunc(tu.f1 * fract(n)) * tu.f2;
|
|
}
|
|
case 6: {
|
|
let pos = u_model.position;
|
|
out.mtx = *model_matrix;
|
|
out.mtx[3] = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
out.post_mtx[0][0] = 0.5;
|
|
out.post_mtx[1][1] = 0.0;
|
|
out.post_mtx[2][1] = 0.5;
|
|
out.post_mtx[3][0] = pos.x * 0.05;
|
|
out.post_mtx[3][1] = pos.y * 0.05;
|
|
}
|
|
default: {}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
fn t_sample(
|
|
traits: PassTrait,
|
|
tex: texture_2d<f32>,
|
|
uv: vec2<f32>,
|
|
) -> vec3<f32> {
|
|
// For control flow uniformity, we always have to sample
|
|
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
|
|
if (traits.has_constant_color == 1u) {
|
|
color = traits.constant_color;
|
|
}
|
|
if (traits.sample_alpha == 1u) {
|
|
return vec3<f32>(color.w);
|
|
}
|
|
return color.xyz;
|
|
}
|
|
|
|
let kRGBToYPrime: vec3<f32> = vec3<f32>(0.299, 0.587, 0.114);
|
|
|
|
fn t_sample_alpha(
|
|
traits: PassTrait,
|
|
tex: texture_2d<f32>,
|
|
uv: vec2<f32>,
|
|
) -> f32 {
|
|
// For control flow uniformity, we always have to sample
|
|
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
|
|
if (traits.has_constant_color == 1u) {
|
|
color = traits.constant_color;
|
|
}
|
|
if (traits.sample_alpha == 1u) {
|
|
return color.w;
|
|
}
|
|
return dot(color.xyz, kRGBToYPrime);
|
|
}
|
|
|
|
@stage(fragment)
|
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
var color: vec4<f32>;
|
|
|
|
let mat_sample = textureSample(t_material, s_repeat, in.uv_diffuse);
|
|
|
|
if (c_traits.shader.shader_type == 0u) {
|
|
// Invalid
|
|
color = vec4<f32>(
|
|
t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse),
|
|
t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
);
|
|
} else if (c_traits.shader.shader_type == 1u) {
|
|
// RetroShader
|
|
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
|
|
* u_global.lightmap_mul.xyz + in.lighting;
|
|
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
|
|
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
|
|
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
|
|
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
|
|
* in.lighting;
|
|
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
|
|
// TODO DynReflectionSample
|
|
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
|
|
} else if (c_traits.shader.shader_type == 2u) {
|
|
// RetroDynamicShader
|
|
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
|
|
* u_global.lightmap_mul.xyz + in.lighting;
|
|
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
|
|
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
|
|
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
|
|
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
|
|
* in.lighting;
|
|
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
|
|
// TODO DynReflectionSample
|
|
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
|
|
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
|
|
} else {
|
|
// TODO RetroDynamicAlphaShader + RetroDynamicCharacterShader
|
|
}
|
|
|
|
// TODO fog shader
|
|
|
|
// TODO post type
|
|
|
|
if (c_traits.shader.alpha_discard == 1u && color.w < 0.25) {
|
|
discard;
|
|
}
|
|
|
|
return color;
|
|
}
|