power_play/src/pp/pp_vis/pp_vis_gpu.g

715 lines
23 KiB
Plaintext

////////////////////////////////////////////////////////////
//~ Helpers
f32 V_RandFromPos(Vec3 pos)
{
Texture3D<u32> noise3d = G_Dereference<u32>(V_ShaderConst_NoiseTex);
// TODO: Compile-time noise dims
u32 noise = noise3d[(Vec3U32)pos % countof(noise3d)];
f32 rand = (f32)noise / 0xFFFF;
return rand;
}
Vec4 V_DryColor(Vec4 color, f32 dryness)
{
Vec4 result = color;
result.rgb *= 1.0 - (dryness * 0.75);
return result;
}
////////////////////////////////////////////////////////////
//~ Utility shaders
//- Prepare shade
ComputeShader2D(V_PrepareShadeCS, 8, 8)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWTexture2D<Vec4> shade = G_Dereference<Vec4>(frame.shade_rw);
Vec2 shade_pos = SV_DispatchThreadID;
if (all(shade_pos < countof(shade)))
{
// Clear shade
shade[shade_pos] = 0;
}
}
//- Prepare cells
ComputeShader2D(V_PrepareCellsCS, 8, 8)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(frame.cells);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
Vec2 cells_pos = SV_DispatchThreadID;
if (all(cells_pos < countof(cells)))
{
// Clear cell
cells[cells_pos] = 0;
// Increase dryness
// TODO: Use simulation dt
f32 dry_rate = frame.dt * 0.1;
{
f32 old_dryness = drynesses[cells_pos];
f32 new_dryness = lerp(old_dryness, 1, dry_rate);
drynesses[cells_pos] = new_dryness;
}
// Clear stain
if (frame.should_clear_particles)
{
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
stains[cells_pos] = 0;
drynesses[cells_pos] = 0;
}
}
}
//- Clear particles
ComputeShader(V_ClearParticlesCS, 64)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles);
u32 particle_idx = SV_DispatchThreadID;
if (particle_idx < V_ParticlesCap)
{
particles[particle_idx].exists = 0;
}
}
////////////////////////////////////////////////////////////
//~ Quads
//////////////////////////////
//- Vertex shader
VertexShader(V_QuadVS, V_QuadPSInput)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
StructuredBuffer<V_Quad> quads = G_Dereference<V_Quad>(frame.quads);
V_Quad quad = quads[SV_InstanceID];
Vec2 rect_uv = RectUvFromIdx(SV_VertexID);
Vec2 screen_pos = mul(quad.quad_uv_to_screen_af, Vec3(rect_uv, 1));
Vec2 samp_uv = lerp(quad.tex_slice_uv.p0, quad.tex_slice_uv.p1, rect_uv);
V_QuadPSInput result;
result.sv_position = Vec4(NdcFromPos(screen_pos, frame.screen_dims).xy, 0, 1);
result.quad_idx = SV_InstanceID;
result.samp_uv = samp_uv;
return result;
}
//////////////////////////////
//- Pixel shader
PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
StructuredBuffer<V_Quad> quads = G_Dereference<V_Quad>(frame.quads);
SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler);
V_Quad quad = quads[input.quad_idx];
Texture2D<Vec4> tex = G_Dereference<Vec4>(quad.tex);
Vec4 albedo = tex.Sample(clamp_sampler, input.samp_uv);
V_QuadPSOutput output;
output.sv_target0 = albedo;
return output;
}
////////////////////////////////////////////////////////////
//~ Particle simulation
//////////////////////////////
//- Particle emitter shader
// TODO: Initialize particles in per-particle-sim-thread rather than sequentially in emitter thread
ComputeShader(V_EmitParticlesCS, 64)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(frame.emitters);
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles);
u32 emitter_idx = SV_DispatchThreadID;
if (emitter_idx < frame.emitters_count)
{
V_Emitter emitter = emitters[emitter_idx];
for (u32 i = 0; i < emitter.count; ++i)
{
u32 particle_seq = emitter.first_particle_seq + i;
u32 particle_idx = particle_seq % (u32)V_ParticlesCap;
particles[particle_idx].exists = 1;
particles[particle_idx].emitter_init_num = emitter_idx + 1;
particles[particle_idx].seq = particle_seq;
}
}
}
//////////////////////////////
//- Particle sim shader
ComputeShader(V_SimParticlesCS, 64)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles);
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(frame.cells);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
u32 particle_idx = SV_DispatchThreadID;
if (particle_idx < V_ParticlesCap)
{
V_Particle particle = particles[particle_idx];
if (particle.exists > 0)
{
//////////////////////////////
//- Initialize particle
if (particle.emitter_init_num != 0)
{
V_Emitter emitter = G_Dereference<V_Emitter>(frame.emitters)[particle.emitter_init_num - 1];
u64 seed0 = MixU64(particle.seq);
u64 seed1 = MixU64(seed0);
f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF;
f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF;
f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF;
f32 rand_falloff = (f32)((seed0 >> 48) & 0xFFFF) / (f32)0xFFFF;
f32 rand_r = (f32)((seed1 >> 0) & 0xFF) / (f32)0xFF;
f32 rand_g = (f32)((seed1 >> 8) & 0xFF) / (f32)0xFF;
f32 rand_b = (f32)((seed1 >> 16) & 0xFF) / (f32)0xFF;
f32 rand_a = (f32)((seed1 >> 24) & 0xFF) / (f32)0xFF;
f32 rand_lifetime = (f32)((seed1 >> 32) & 0xFFFF) / (f32)0xFFFF;
f32 speed = emitter.speed + (rand_speed - 0.5) * emitter.speed_spread;
f32 angle = emitter.angle + (rand_angle - 0.5) * emitter.angle_spread;
f32 velocity_falloff = emitter.velocity_falloff + (rand_falloff - 0.5) * emitter.velocity_falloff_spread;
f32 lifetime = emitter.lifetime + (rand_lifetime - 0.5) * emitter.lifetime_spread;
Vec4 color = emitter.color_lin + (Vec4(rand_r, rand_g, rand_b, rand_a) - 0.5) * emitter.color_spread;
Vec2 offset = (emitter.end - emitter.start) * rand_offset;
particle.flags = emitter.flags;
particle.pos = emitter.start + offset;
particle.velocity = Vec2(cos(angle), sin(angle)) * speed;
particle.velocity_falloff = velocity_falloff;
particle.color = color;
particle.lifetime = lifetime;
if (emitter.lifetime == 0)
{
particle.lifetime = Inf;
}
particle.emitter_init_num = 0;
}
//////////////////////////////
//- Simulate particle
f32 prev_exists = particle.exists;
{
Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1));
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells));
// Simulate
{
// TODO: Use simulation dt
particle.pos += particle.velocity * frame.dt;
particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * frame.dt);
particle.exists -= frame.dt / particle.lifetime;
if ((particle.flags & V_ParticleFlag_PruneWhenStill) && (dot(particle.velocity, particle.velocity) < (0.1 * 0.1)))
{
particle.exists = 0;
}
if (particle.exists < 0.000001)
{
particle.exists = 0;
}
}
}
//////////////////////////////
//- Commit particle
// FIXME: Atomic writes
{
Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1));
Vec2 screen_pos = mul(frame.af.world_to_screen, Vec3(particle.pos, 1));
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells));
b32 is_in_screen = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims);
Vec4 color = particle.color;
color.a *= prev_exists;
// Stain
if (is_in_world)
{
b32 should_stain = (
AnyBit(particle.flags, V_ParticleFlag_StainTrail) ||
(AnyBit(particle.flags, V_ParticleFlag_StainOnPrune) && particle.exists == 0)
);
// Stain
if (should_stain)
{
f32 old_dryness = drynesses[cell_pos];
Vec4 old_stain = stains[cell_pos];
// old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5);
// old_stain = V_DryColor(old_stain, old_dryness);
Vec4 new_stain = 0;
new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a));
new_stain.a = color.a + (old_stain.a * (1.0 - color.a));
// new_stain = V_DryColor(new_stain, old_dryness * 0.1);
// new_stain = V_DryColor(new_stain, old_dryness * 0.5);
stains[cell_pos] = new_stain;
drynesses[cell_pos] = 0;
}
}
// Draw
{
b32 should_draw = is_in_world;
if (should_draw)
{
cells[cell_pos] = color;
}
}
}
particles[particle_idx] = particle;
}
}
}
////////////////////////////////////////////////////////////
//~ Shade
ComputeShader2D(V_ShadeCS, 8, 8)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWTexture2D<Vec4> shade_tex = G_Dereference<Vec4>(frame.shade_rw);
Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(frame.albedo_ro);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler);
Vec2 shade_pos = SV_DispatchThreadID + Vec2(0.5, 0.5);
Vec2 world_pos = mul(frame.af.shade_to_world, Vec3(shade_pos, 1));
Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(world_pos, 1));
Vec2 tile_pos = mul(frame.af.world_to_tile, Vec3(world_pos, 1));
P_TileKind tile = tiles.Load(Vec3(tile_pos, 0));
Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5;
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(stains));
//////////////////////////////
//- Compute albedo
Vec4 albedo = 0;
//////////////////////////////
//- Compute result
Vec4 result = albedo;
//////////////////////////////
//- Write result
if (all(shade_pos < countof(shade_tex)))
{
shade_tex[shade_pos] = result;
}
}
////////////////////////////////////////////////////////////
//~ Composite
//////////////////////////////
//- Vertex shader
VertexShader(V_CompositeVS, V_CompositePSInput)
{
Vec2 uv = RectUvFromIdx(SV_VertexID);
V_CompositePSInput result;
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
return result;
}
//////////////////////////////
//- Pixel shader
PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
// Texture2D<Vec4> shade_tex = G_Dereference<Vec4>(frame.shade_ro);
Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(frame.albedo_ro);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(frame.cells);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles);
SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler);
Vec2 screen_pos = input.sv_position.xy;
Vec2 world_pos = mul(frame.af.screen_to_world, Vec3(screen_pos, 1));
Vec2 tile_pos = mul(frame.af.world_to_tile, Vec3(world_pos, 1));
Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(world_pos, 1));
Vec2 shade_pos = mul(frame.af.screen_to_shade, Vec3(screen_pos.xy, 1));
Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5;
Vec2 world_bounds_screen_p0 = mul(frame.af.world_to_screen, Vec3(-half_world_dims.xy, 1));
Vec2 world_bounds_screen_p1 = mul(frame.af.world_to_screen, Vec3(half_world_dims.xy, 1));
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells));
P_TileKind tile = tiles.Load(Vec3(tile_pos, 0));
P_TileKind equipped_tile = frame.equipped_tile;
//////////////////////////////
//- World color
Vec4 world_color = Vec4(0.025, 0.025, 0.025, 1);
if (is_in_world)
{
//////////////////////////////
//- Shade color
Vec4 shade_color = 0;
// if (all(shade_pos >= Vec2(0, 0)) && all(shade_pos < countof(shade_tex)))
// {
// Vec2 shade_uv = shade_pos / countof(shade_tex);
// shade_color = shade_tex.SampleLevel(clamp_sampler, shade_uv, 0);
// }
//////////////////////////////
//- Albedo
Vec4 albedo_color = 0;
{
//////////////////////////////
//- Tile
// TODO: Remove this
b32 tile_is_wall = 0;
Vec4 tile_color = 0;
{
P_TileKind tile_tl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y - 1, 0));
P_TileKind tile_tr = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y - 1, 0));
P_TileKind tile_br = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y + 1, 0));
P_TileKind tile_bl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y + 1, 0));
P_TileKind tile_t = tiles.Load(Vec3(tile_pos.x, tile_pos.y - 1, 0));
P_TileKind tile_r = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y, 0));
P_TileKind tile_b = tiles.Load(Vec3(tile_pos.x, tile_pos.y + 1, 0));
P_TileKind tile_l = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y, 0));
f32 tile_edge_dist = Inf;
P_TileKind edge_tile = tile;
if (tile_tl != tile) { edge_tile = tile_tl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), floor(tile_pos.y)))); }
if (tile_tr != tile) { edge_tile = tile_tr; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), floor(tile_pos.y)))); }
if (tile_br != tile) { edge_tile = tile_br; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), ceil(tile_pos.y)))); }
if (tile_bl != tile) { edge_tile = tile_bl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), ceil(tile_pos.y)))); }
if (tile_l != tile) { edge_tile = tile_l; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.x)); }
if (tile_r != tile) { edge_tile = tile_r; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.x)); }
if (tile_t != tile) { edge_tile = tile_t; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.y)); }
if (tile_b != tile) { edge_tile = tile_b; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.y)); }
if (tile == P_TileKind_Wall)
{
Vec4 outer = LinearFromSrgb(Vec4(0.05, 0.05, 0.05, 1));
Vec4 inner = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1));
tile_color = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.375));
tile_is_wall = 1;
}
else if (tile != P_TileKind_Empty)
{
V_TileDesc tile_desc = frame.tile_descs[tile];
Texture2D<Vec4> tile_tex = G_Dereference<Vec4>(tile_desc.tex);
Vec2 tile_samp_uv = lerp(tile_desc.tex_slice_uv.p0, tile_desc.tex_slice_uv.p1, frac(world_pos));
tile_color = tile_tex.SampleLevel(clamp_sampler, tile_samp_uv, 0);
}
// Checkered grid
else if (tile == P_TileKind_Empty)
{
i32 color_idx = 0;
Vec4 colors[2] = {
LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)),
LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1))
};
const f32 checker_size = 0.5;
Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2));
if (world_pos_modded.x < checker_size)
{
color_idx = !color_idx;
}
if (world_pos_modded.y < checker_size)
{
color_idx = !color_idx;
}
if (world_pos.x < 0)
{
color_idx = !color_idx;
}
if (world_pos.y < 0)
{
color_idx = !color_idx;
}
tile_color = colors[color_idx];
}
}
//////////////////////////////
//- Stain
Vec4 stain_color = 0;
{
f32 dryness = drynesses.Load(cell_pos);
Vec4 stain = stains.Load(cell_pos);
stain_color = V_DryColor(stain, dryness);
stain_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains
}
//////////////////////////////
//- Albedo tex
Vec4 albedo_tex_color = albedo_tex.Load(Vec3(screen_pos, 0));
//////////////////////////////
//- Compose albedo
if (!tile_is_wall)
{
albedo_color = BlendPremul(tile_color, albedo_color); // Blend floor tile
albedo_color = BlendPremul(stain_color, albedo_color); // Blend floor stain
}
albedo_color = BlendPremul(albedo_tex_color, albedo_color);
if (tile_is_wall)
{
albedo_color = BlendPremul(tile_color, albedo_color); // Blend wall tile
albedo_color = BlendPremul(stain_color, albedo_color); // Blend wall stain
}
}
//////////////////////////////
//- Particle
// TODO: Remove this
Vec4 particle_color = cells.Load(cell_pos);
//////////////////////////////
//- Compose world
// world_color = BlendPremul(shade_color, world_color);
world_color = BlendPremul(albedo_color, world_color);
world_color = BlendPremul(particle_color, world_color);
}
//////////////////////////////
//- Overlay color
Vec4 overlay_color = 0;
{
f32 half_thickness = 1;
//////////////////////////////
//- Tile selection overlay
Vec4 selection_color = 0;
if (frame.has_mouse_focus && frame.selection_mode == V_SelectionMode_Tile)
{
Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1));
// Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25));
Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6));
Rng2 screen_selection = frame.screen_selection;
Rng2 world_selection = frame.world_selection;
Rng2 tile_selection;
tile_selection.p0 = floor(mul(frame.af.world_to_tile, Vec3(world_selection.p0, 1)));
tile_selection.p1 = ceil(mul(frame.af.world_to_tile, Vec3(world_selection.p1, 1)));
f32 dist = 100000000;
dist = min(dist, screen_pos.x - screen_selection.p0.x);
dist = min(dist, screen_pos.y - screen_selection.p0.y);
dist = min(dist, screen_selection.p1.x - screen_pos.x);
dist = min(dist, screen_selection.p1.y - screen_pos.y);
dist = -dist;
// if (dist >= -half_thickness && dist <= half_thickness)
// {
// selection_color = border_color;
// }
// else
{
if (
world_pos.x > -(P_WorldPitch / 2) &&
world_pos.y > -(P_WorldPitch / 2) &&
world_pos.x < (P_WorldPitch / 2) &&
world_pos.y < (P_WorldPitch / 2) &&
tile_pos.x >= tile_selection.p0.x &&
tile_pos.x <= tile_selection.p1.x &&
tile_pos.y >= tile_selection.p0.y &&
tile_pos.y <= tile_selection.p1.y
)
{
selection_color = inner_color;
}
}
// Premultiply
selection_color.rgb *= selection_color.a;
}
//////////////////////////////
//- Grid
Vec4 grid_color = 0;
if (is_in_world)
{
b32 debug_draw = !!frame.show_console;
// Grid outline
if (frame.show_console)
{
const Vec4 line_color = LinearFromSrgb(Vec4(1, 1, 1, 0.1));
Vec2 line_screen_p0 = mul(frame.af.world_to_screen, Vec3(floor(world_pos), 1));
Vec2 line_screen_p1 = mul(frame.af.world_to_screen, Vec3(ceil(world_pos), 1));
f32 line_dist = 100000;
line_dist = min(line_dist, abs(screen_pos.x - line_screen_p0.x));
line_dist = min(line_dist, abs(screen_pos.x - line_screen_p1.x));
line_dist = min(line_dist, abs(screen_pos.y - line_screen_p0.y));
line_dist = min(line_dist, abs(screen_pos.y - line_screen_p1.y));
if (line_dist <= half_thickness * 0.5)
{
grid_color = line_color;
}
}
// Axis
if (frame.show_console)
{
const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1));
const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1));
Vec2 zero_screen = mul(frame.af.world_to_screen, Vec3(0, 0, 1));
f32 x_dist = abs(screen_pos.x - zero_screen.x);
f32 y_dist = abs(screen_pos.y - zero_screen.y);
if (y_dist <= half_thickness)
{
grid_color = x_axis_color;
}
else if (x_dist <= half_thickness)
{
grid_color = y_axis_color;
}
}
// World bounds
{
const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1));
f32 bounds_dist = 100000;
bounds_dist = min(bounds_dist, abs(screen_pos.x - world_bounds_screen_p0.x));
bounds_dist = min(bounds_dist, abs(screen_pos.x - world_bounds_screen_p1.x));
bounds_dist = min(bounds_dist, abs(screen_pos.y - world_bounds_screen_p0.y));
bounds_dist = min(bounds_dist, abs(screen_pos.y - world_bounds_screen_p1.y));
if (bounds_dist <= half_thickness)
{
grid_color = bounds_color;
}
}
// Premultiply
grid_color.rgb *= grid_color.a;
}
//////////////////////////////
//- Crosshair
// TODO: Remove this
Vec4 crosshair_color = 0;
if (frame.is_looking)
{
f32 dist = length(frame.screen_crosshair - screen_pos);
if (dist < 4)
{
// Adaptive crosshair color based on underlying luminance
f32 world_luminance = LuminanceFromColor(world_color);
f32 adaptive_threshold = 0.5;
Vec4 adapted_crosshair_color = crosshair_color;
if (world_luminance <= adaptive_threshold)
{
crosshair_color = Color_White;
}
else
{
crosshair_color = InvertColor(Color_White);
}
crosshair_color = Premul(crosshair_color);
}
}
//////////////////////////////
//- Compose overlay
overlay_color = BlendPremul(selection_color, overlay_color);
overlay_color = BlendPremul(grid_color, overlay_color);
overlay_color = BlendPremul(crosshair_color, overlay_color);
}
//////////////////////////////
//- Compose result
Vec4 result = Vec4(0, 0, 0, 1);
result = BlendPremul(world_color, result);
result = BlendPremul(overlay_color, result);
result = Unpremul(result);
V_CompositePSOutput output;
output.sv_target0 = result;
return output;
}
////////////////////////////////////////////////////////////
//~ Debug shapes
//////////////////////////////
//- Vertex shader
VertexShader(V_DVertVS, V_DVertPSInput)
{
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(frame.dverts);
V_DVert vert = verts[SV_VertexID];
Vec2 screen_pos = vert.pos;
V_DVertPSInput result;
result.sv_position = Vec4(NdcFromPos(screen_pos, frame.screen_dims).xy, 0, 1);
result.color_lin = vert.color_lin;
return result;
}
//////////////////////////////
//- Pixel shader
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
{
V_DVertPSOutput output;
output.sv_target0 = input.color_lin;
return output;
}