Use hand texture as render background
This commit is contained in:
@@ -3,21 +3,74 @@ struct MatrixUniform {
|
||||
viewport: vec4f,
|
||||
glyph: vec4f,
|
||||
color: vec4f,
|
||||
render_options: vec4f,
|
||||
image: vec4f,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> u: MatrixUniform;
|
||||
|
||||
@group(1) @binding(0)
|
||||
var model_texture: texture_2d<f32>;
|
||||
var base_color_texture: texture_2d<f32>;
|
||||
|
||||
@group(1) @binding(1)
|
||||
var model_sampler: sampler;
|
||||
var base_color_sampler: sampler;
|
||||
|
||||
struct MaterialParams {
|
||||
base_color: vec4f,
|
||||
metallic_roughness: vec4f,
|
||||
emissive_alpha: vec4f,
|
||||
flags: vec4f,
|
||||
}
|
||||
|
||||
@group(1) @binding(2)
|
||||
var<uniform> material: MaterialParams;
|
||||
|
||||
@group(1) @binding(3)
|
||||
var metallic_roughness_texture: texture_2d<f32>;
|
||||
|
||||
@group(1) @binding(4)
|
||||
var metallic_roughness_sampler: sampler;
|
||||
|
||||
@group(1) @binding(5)
|
||||
var normal_texture: texture_2d<f32>;
|
||||
|
||||
@group(1) @binding(6)
|
||||
var normal_sampler: sampler;
|
||||
|
||||
@group(1) @binding(7)
|
||||
var occlusion_texture: texture_2d<f32>;
|
||||
|
||||
@group(1) @binding(8)
|
||||
var occlusion_sampler: sampler;
|
||||
|
||||
@group(1) @binding(9)
|
||||
var emissive_texture: texture_2d<f32>;
|
||||
|
||||
@group(1) @binding(10)
|
||||
var emissive_sampler: sampler;
|
||||
|
||||
fn saturate(value: f32) -> f32 {
|
||||
return clamp(value, 0.0, 1.0);
|
||||
}
|
||||
|
||||
fn linear_to_srgb(linear: vec3f) -> vec3f {
|
||||
let x = max(linear, vec3f(0.0));
|
||||
let low = x * 12.92;
|
||||
let high = 1.055 * pow(x, vec3f(1.0 / 2.4)) - vec3f(0.055);
|
||||
return select(high, low, x <= vec3f(0.0031308));
|
||||
}
|
||||
|
||||
fn output_color(linear_rgb: vec3f, alpha: f32) -> vec4f {
|
||||
let clamped = clamp(linear_rgb, vec3f(0.0), vec3f(1.0));
|
||||
if (u.color.w > 0.5) {
|
||||
return vec4f(clamped, alpha);
|
||||
}
|
||||
|
||||
let encoded = linear_to_srgb(clamped);
|
||||
return vec4f(encoded, alpha);
|
||||
}
|
||||
|
||||
fn range_stop_color(index: u32) -> vec3f {
|
||||
switch index {
|
||||
case 0u: {
|
||||
@@ -76,38 +129,66 @@ 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);
|
||||
var color = mix(vec3f(0.018, 0.019, 0.022), vec3f(0.038, 0.040, 0.046), 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)));
|
||||
color *= 1.0 - vignette * 0.22;
|
||||
color += vec3f(0.010, 0.010, 0.012) * (1.0 - smoothstep(0.0, 0.85, abs(uv.y - 0.50)));
|
||||
|
||||
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);
|
||||
return output_color(color, 1.0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// hand image background
|
||||
struct HandImageVertexOutput {
|
||||
@builtin(position) clip_position: vec4f,
|
||||
@location(0) screen_uv: vec2f,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_hand_image(@builtin(vertex_index) vertex_index: u32) -> HandImageVertexOutput {
|
||||
let positions = array<vec2f, 6>(
|
||||
vec2f(-1.0, -1.0),
|
||||
vec2f(1.0, -1.0),
|
||||
vec2f(-1.0, 1.0),
|
||||
vec2f(-1.0, 1.0),
|
||||
vec2f(1.0, -1.0),
|
||||
vec2f(1.0, 1.0),
|
||||
);
|
||||
let uvs = array<vec2f, 6>(
|
||||
vec2f(0.0, 1.0),
|
||||
vec2f(1.0, 1.0),
|
||||
vec2f(0.0, 0.0),
|
||||
vec2f(0.0, 0.0),
|
||||
vec2f(1.0, 1.0),
|
||||
vec2f(1.0, 0.0),
|
||||
);
|
||||
|
||||
var out: HandImageVertexOutput;
|
||||
out.clip_position = vec4f(positions[vertex_index], 0.0, 1.0);
|
||||
out.screen_uv = uvs[vertex_index];
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_hand_image(in: HandImageVertexOutput) -> @location(0) vec4f {
|
||||
let viewport_aspect = u.viewport.x / max(u.viewport.y, 1.0);
|
||||
let image_aspect = u.image.x / max(u.image.y, 1.0);
|
||||
|
||||
var uv = in.screen_uv;
|
||||
if (viewport_aspect > image_aspect) {
|
||||
let image_width = image_aspect / viewport_aspect;
|
||||
uv.x = (uv.x - (1.0 - image_width) * 0.5) / image_width;
|
||||
} else {
|
||||
let image_height = viewport_aspect / image_aspect;
|
||||
uv.y = (uv.y - (1.0 - image_height) * 0.5) / image_height;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
return vec4f(color, 1.0);
|
||||
let color = textureSample(base_color_texture, base_color_sampler, uv);
|
||||
return output_color(color.rgb, color.a);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +333,7 @@ fn vs_glyph(vertex: GlyphVertexInput, instance: GlyphInstanceInput) -> GlyphVert
|
||||
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);
|
||||
return output_color(color, alpha);
|
||||
}
|
||||
|
||||
|
||||
@@ -310,7 +391,7 @@ fn fs_dot(in: DotVertexOutput) -> @location(0) vec4f {
|
||||
|
||||
let alpha = max(core, glow);
|
||||
|
||||
return vec4f(color, alpha);
|
||||
return output_color(color, alpha);
|
||||
}
|
||||
|
||||
|
||||
@@ -319,6 +400,8 @@ struct ModelVertexInput {
|
||||
@location(0) position: vec3f,
|
||||
@location(1) tex_coords: vec2f,
|
||||
@location(2) normal: vec3f,
|
||||
@location(3) color: vec4f,
|
||||
@location(4) tangent: vec4f,
|
||||
@location(5) model_0: vec4f,
|
||||
@location(6) model_1: vec4f,
|
||||
@location(7) model_2: vec4f,
|
||||
@@ -330,29 +413,217 @@ struct ModelVertexOutput {
|
||||
@location(0) world_normal: vec3f,
|
||||
@location(1) world_position: vec3f,
|
||||
@location(2) tex_coords: vec2f,
|
||||
@location(3) color: vec4f,
|
||||
@location(4) world_tangent: vec4f,
|
||||
}
|
||||
|
||||
fn inverse_transpose3x3(m: mat3x3f) -> mat3x3f {
|
||||
let c0 = m[0];
|
||||
let c1 = m[1];
|
||||
let c2 = m[2];
|
||||
let r0 = cross(c1, c2);
|
||||
let r1 = cross(c2, c0);
|
||||
let r2 = cross(c0, c1);
|
||||
let det = dot(c0, r0);
|
||||
let safe_det = select(det, select(-0.000001, 0.000001, det >= 0.0), abs(det) < 0.000001);
|
||||
let inv_det = 1.0 / safe_det;
|
||||
return mat3x3f(r0 * inv_det, r1 * inv_det, r2 * inv_det);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_model(vertex: ModelVertexInput) -> ModelVertexOutput {
|
||||
let model = mat4x4f(vertex.model_0, vertex.model_1, vertex.model_2, vertex.model_3);
|
||||
let world_position = model * vec4f(vertex.position, 1.0);
|
||||
let normal_matrix = mat3x3f(model[0].xyz, model[1].xyz, model[2].xyz);
|
||||
let model3 = mat3x3f(model[0].xyz, model[1].xyz, model[2].xyz);
|
||||
let normal_matrix = inverse_transpose3x3(model3);
|
||||
|
||||
var out: ModelVertexOutput;
|
||||
out.clip_position = u.view_proj * world_position;
|
||||
out.world_position = world_position.xyz;
|
||||
out.world_normal = normalize(normal_matrix * vertex.normal);
|
||||
out.tex_coords = vec2f(vertex.tex_coords.x, 1.0 - vertex.tex_coords.y);
|
||||
out.tex_coords = vertex.tex_coords;
|
||||
out.color = vertex.color;
|
||||
out.world_tangent = vec4f(normalize(normal_matrix * vertex.tangent.xyz), vertex.tangent.w);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_model(in: ModelVertexOutput) -> @location(0) vec4f {
|
||||
let light_dir = normalize(vec3f(-0.35, 0.85, 0.45));
|
||||
let normal = normalize(in.world_normal);
|
||||
let diffuse = max(dot(normal, light_dir), 0.0);
|
||||
let rim = pow(1.0 - saturate(abs(normal.y)), 2.0) * 0.16;
|
||||
let base = textureSample(model_texture, model_sampler, in.tex_coords).rgb;
|
||||
let color = base * (0.28 + diffuse * 0.72) + vec3f(0.14, 0.24, 0.32) * rim;
|
||||
return vec4f(color, 1.0);
|
||||
fn distribution_ggx(normal: vec3f, half_dir: vec3f, roughness: f32) -> f32 {
|
||||
let a = roughness * roughness;
|
||||
let a2 = a * a;
|
||||
let n_dot_h = max(dot(normal, half_dir), 0.0);
|
||||
let n_dot_h2 = n_dot_h * n_dot_h;
|
||||
let denom = (n_dot_h2 * (a2 - 1.0) + 1.0);
|
||||
return a2 / max(3.14159265 * denom * denom, 0.000001);
|
||||
}
|
||||
|
||||
fn geometry_schlick_ggx(n_dot_v: f32, roughness: f32) -> f32 {
|
||||
let r = roughness + 1.0;
|
||||
let k = (r * r) / 8.0;
|
||||
return n_dot_v / max(n_dot_v * (1.0 - k) + k, 0.000001);
|
||||
}
|
||||
|
||||
fn geometry_smith(normal: vec3f, view_dir: vec3f, light_dir: vec3f, roughness: f32) -> f32 {
|
||||
let n_dot_v = max(dot(normal, view_dir), 0.0);
|
||||
let n_dot_l = max(dot(normal, light_dir), 0.0);
|
||||
let ggx_v = geometry_schlick_ggx(n_dot_v, roughness);
|
||||
let ggx_l = geometry_schlick_ggx(n_dot_l, roughness);
|
||||
return ggx_v * ggx_l;
|
||||
}
|
||||
|
||||
fn fresnel_schlick(cos_theta: f32, f0: vec3f) -> vec3f {
|
||||
let factor = pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0);
|
||||
return f0 + (vec3f(1.0) - f0) * factor;
|
||||
}
|
||||
|
||||
fn pbr_directional_light(
|
||||
albedo: vec3f,
|
||||
normal: vec3f,
|
||||
view_dir: vec3f,
|
||||
light_dir: vec3f,
|
||||
radiance: vec3f,
|
||||
metallic: f32,
|
||||
roughness: f32,
|
||||
) -> vec3f {
|
||||
let half_dir = normalize(view_dir + light_dir);
|
||||
let n_dot_l = max(dot(normal, light_dir), 0.0);
|
||||
let n_dot_v = max(dot(normal, view_dir), 0.0);
|
||||
let h_dot_v = max(dot(half_dir, view_dir), 0.0);
|
||||
|
||||
let f0 = mix(vec3f(0.04), albedo, metallic);
|
||||
let f = fresnel_schlick(h_dot_v, f0);
|
||||
let d = distribution_ggx(normal, half_dir, roughness);
|
||||
let g = geometry_smith(normal, view_dir, light_dir, roughness);
|
||||
let specular = (d * g * f) / max(4.0 * n_dot_v * n_dot_l, 0.000001);
|
||||
|
||||
let k_s = f;
|
||||
let k_d = (vec3f(1.0) - k_s) * (1.0 - metallic);
|
||||
let diffuse = k_d * albedo / 3.14159265;
|
||||
|
||||
return (diffuse + specular) * radiance * n_dot_l;
|
||||
}
|
||||
|
||||
fn aces_tonemap(x: vec3f) -> vec3f {
|
||||
let a = 2.51;
|
||||
let b = 0.03;
|
||||
let c = 2.43;
|
||||
let d = 0.59;
|
||||
let e = 0.14;
|
||||
return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));
|
||||
}
|
||||
|
||||
fn material_normal(in: ModelVertexOutput, front_facing: bool) -> vec3f {
|
||||
var n = normalize(in.world_normal);
|
||||
if (!front_facing && material.flags.w > 0.5) {
|
||||
n = -n;
|
||||
}
|
||||
|
||||
if (material.flags.z < 0.5) {
|
||||
return n;
|
||||
}
|
||||
|
||||
let t = normalize(in.world_tangent.xyz);
|
||||
let b = normalize(cross(n, t) * in.world_tangent.w);
|
||||
let tangent_normal_sample = textureSample(normal_texture, normal_sampler, in.tex_coords).xyz;
|
||||
var tangent_normal = tangent_normal_sample * 2.0 - vec3f(1.0);
|
||||
tangent_normal.x = tangent_normal.x * material.metallic_roughness.z;
|
||||
tangent_normal.y = tangent_normal.y * material.metallic_roughness.z;
|
||||
|
||||
return normalize(mat3x3f(t, b, n) * tangent_normal);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_model(in: ModelVertexOutput, @builtin(front_facing) front_facing: bool) -> @location(0) vec4f {
|
||||
let base_sample = textureSample(base_color_texture, base_color_sampler, in.tex_coords);
|
||||
let base_color = base_sample * material.base_color * in.color;
|
||||
let alpha_mode = material.emissive_alpha.w;
|
||||
var alpha = base_color.a;
|
||||
if (alpha_mode < 0.5) {
|
||||
alpha = 1.0;
|
||||
} else if (alpha_mode < 1.5) {
|
||||
if (alpha < material.flags.x) {
|
||||
discard;
|
||||
}
|
||||
alpha = 1.0;
|
||||
}
|
||||
|
||||
let debug_mode = u32(u.render_options.x + 0.5);
|
||||
if (debug_mode == 1u) {
|
||||
return output_color(base_color.rgb, alpha);
|
||||
}
|
||||
|
||||
let mr_sample = textureSample(
|
||||
metallic_roughness_texture,
|
||||
metallic_roughness_sampler,
|
||||
in.tex_coords,
|
||||
);
|
||||
let roughness = clamp(mr_sample.g * material.metallic_roughness.y, 0.04, 1.0);
|
||||
let metallic = clamp(mr_sample.b * material.metallic_roughness.x, 0.0, 1.0);
|
||||
let ao_sample = textureSample(occlusion_texture, occlusion_sampler, in.tex_coords).r;
|
||||
let ao = mix(1.0, ao_sample, clamp(material.metallic_roughness.w, 0.0, 1.0));
|
||||
let emissive = textureSample(emissive_texture, emissive_sampler, in.tex_coords).rgb
|
||||
* material.emissive_alpha.rgb;
|
||||
let albedo = base_color.rgb;
|
||||
let normal = material_normal(in, front_facing);
|
||||
|
||||
switch debug_mode {
|
||||
case 2u: {
|
||||
return output_color(normal * 0.5 + vec3f(0.5), 1.0);
|
||||
}
|
||||
case 3u: {
|
||||
return output_color(vec3f(roughness), 1.0);
|
||||
}
|
||||
case 4u: {
|
||||
return output_color(vec3f(metallic), 1.0);
|
||||
}
|
||||
case 5u: {
|
||||
return output_color(vec3f(ao), 1.0);
|
||||
}
|
||||
case 6u: {
|
||||
return output_color(emissive, 1.0);
|
||||
}
|
||||
case 7u: {
|
||||
return output_color(vec3f(fract(in.tex_coords), 0.0), 1.0);
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
|
||||
let view_dir = normalize(vec3f(0.0, 0.72, 0.70));
|
||||
let key = pbr_directional_light(
|
||||
albedo,
|
||||
normal,
|
||||
view_dir,
|
||||
normalize(vec3f(-0.35, 0.82, 0.44)),
|
||||
vec3f(2.15, 2.08, 1.92),
|
||||
metallic,
|
||||
roughness,
|
||||
);
|
||||
let fill = pbr_directional_light(
|
||||
albedo,
|
||||
normal,
|
||||
view_dir,
|
||||
normalize(vec3f(0.62, 0.44, -0.36)),
|
||||
vec3f(0.50, 0.50, 0.50),
|
||||
metallic,
|
||||
roughness,
|
||||
);
|
||||
|
||||
let f0 = mix(vec3f(0.04), albedo, metallic);
|
||||
let f_ambient = fresnel_schlick(max(dot(normal, view_dir), 0.0), f0);
|
||||
let ambient_color = vec3f(1.0);
|
||||
let ambient_strength = 0.08;
|
||||
// Temporary neutral linear ambient term until a real IBL/HDR environment is added.
|
||||
var ambient = vec3f(0.0);
|
||||
if (u.render_options.y > 0.5) {
|
||||
let ambient_diffuse = albedo * (1.0 - metallic) * ambient_color * ambient_strength;
|
||||
let ambient_specular = f_ambient * ambient_color * ambient_strength * (1.0 - roughness * 0.55);
|
||||
ambient = (ambient_diffuse + ambient_specular) * ao;
|
||||
}
|
||||
|
||||
let exposed_color = (ambient + key + fill + emissive) * max(u.render_options.w, 0.0);
|
||||
var color = exposed_color;
|
||||
if (u.render_options.z > 0.5) {
|
||||
color = aces_tonemap(exposed_color);
|
||||
}
|
||||
|
||||
return output_color(color, alpha);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user