feat(render): Phase A8 — portal_stencil vert/frag shaders

Minimal pair for the indoor-cell stencil pipeline (#78). Vert transforms
world-space portal polygon vertices through uViewProjection; includes a
near-zero pos.w guard for coplanar-camera robustness (matches WB pattern).
Frag either passes through gl_FragCoord.z or writes gl_FragDepth=1.0
based on uWriteFarDepth; FragColor declared but suppressed via ColorMask
on the CPU side.

Matches WorldBuilder's PortalStencil.vert/.frag at
references/WorldBuilder/Chorizite.OpenGLSDLBackend/Shaders/.
Uses #version 430 core consistent with acdream's mesh_modern shaders.
Deployed to bin/ via existing Rendering\Shaders\*.* .csproj glob.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-26 07:52:55 +02:00
parent 6577c0a21c
commit 2d31d490d1
2 changed files with 59 additions and 0 deletions

View file

@ -0,0 +1,34 @@
#version 430 core
//
// Phase A8 — portal stencil mark + far-depth punch.
//
// uWriteFarDepth = 0 → pass through gl_FragCoord.z (used for the
// stencil mark pass; depth mask is off anyway).
// uWriteFarDepth != 0 → write gl_FragDepth = 1.0 (the far-depth punch
// pass; depth mask is on, color is off).
//
// Matches WorldBuilder's PortalStencil.frag at
// references/WorldBuilder/Chorizite.OpenGLSDLBackend/Shaders/PortalStencil.frag
out vec4 FragColor;
uniform int uWriteFarDepth;
void main()
{
if (uWriteFarDepth != 0)
{
// Write far depth to clear the depth buffer in the portal region.
// This punches through the building exterior's depth so interior
// geometry at any depth can be rendered through the stencil mask.
gl_FragDepth = 1.0;
}
else
{
gl_FragDepth = gl_FragCoord.z;
}
// Color writes are suppressed via ColorMask(false) on the CPU side.
// Output is required by GLSL but will not be written to the framebuffer.
FragColor = vec4(0.0);
}

View file

@ -0,0 +1,25 @@
#version 430 core
//
// Phase A8 — portal stencil mark + far-depth punch.
//
// Position is in WORLD space (pipeline transforms cell-local portal
// polygon vertices through cell.WorldTransform on the CPU before
// uploading to the VBO). Output is clip space via uViewProjection.
layout(location = 0) in vec3 aPosition;
uniform mat4 uViewProjection;
void main()
{
vec4 pos = uViewProjection * vec4(aPosition, 1.0);
// Prevent near-zero clipping issues when the camera is perfectly
// coplanar with the portal polygon.
if (abs(pos.w) < 0.001)
{
pos.w = pos.w < 0.0 ? -0.001 : 0.001;
}
gl_Position = pos;
}