The projection matrix Y-flip (projectionMatrix[1][1] *= -1) reverses
triangle winding from the GPU's perspective. With the default
VK_FRONT_FACE_COUNTER_CLOCKWISE, gl_FrontFacing was inverted, causing
all fragment shaders (M2, WMO, character) to flip normals on front
faces instead of back faces, putting specular highlights on the wrong
side of surfaces.
Player interaction ripples: vertex shader adds radial damped-sine displacement
centered on player position, fragment shader adds matching normal perturbation
for specular highlights. Player XY packed into shadowParams.zw, ripple strength
into fogParams.w. Separate 1x render pass for water when MSAA is active to
avoid MSAA-induced darkening — water renders after main pass resolves, using
the resolved swapchain image and depth resolve target. Water 1x framebuffers
rebuilt on swapchain recreate (window resize).