MCPcopy Create free account
hub / github.com/Bloom-Engine/engine / oblique_proj

Function oblique_proj

native/shared/src/renderer/planar_reflection.rs:230–273  ·  view source on GitHub ↗

EN-011 V2 — modify a projection matrix so its near plane is clipped at the given eye-space plane. Used for planar reflection to prevent geometry below the water from polluting the reflection along the shoreline edge. Reference: Eric Lengyel, "Oblique View Frustum Depth Projection and Clipping" (Journal of Game Development, 2005). The technique shifts the projection's near plane to coincide with t

(
    proj: [[f32; 4]; 4],
    plane_eye_space: [f32; 4],
)

Source from the content-addressed store, hash-verified

228/// plane satisfy `N · p_eye + d = 0`. Use `world_plane_to_eye_space`
229/// to convert from a world-space plane.
230pub fn oblique_proj(
231 proj: [[f32; 4]; 4],
232 plane_eye_space: [f32; 4],
233) -> [[f32; 4]; 4] {
234 let c = plane_eye_space;
235
236 // Far-plane corner in clip space is in the direction of (sgn(c.x),
237 // sgn(c.y), 1, 1) — Lengyel §2. Pulled back into eye space by
238 // multiplying with `inv(proj)`.
239 let sx = if c[0] >= 0.0 { 1.0 } else { -1.0 };
240 let sy = if c[1] >= 0.0 { 1.0 } else { -1.0 };
241 let q_clip = [sx, sy, 1.0, 1.0];
242 let inv_p = mat4_invert(proj);
243 let q = mat4_mul_vec4(&inv_p, &q_clip);
244
245 // Scale `c` so the near-plane crosses through the supplied plane:
246 // M = (2 / dot(c, q)) · c
247 // Then the new third row of P (which controls the depth output) is
248 // P_row2 = M - P_row3
249 // (P_row3 is the standard perspective w-row, all stays the same.)
250 let denom = c[0] * q[0] + c[1] * q[1] + c[2] * q[2] + c[3] * q[3];
251 if denom.abs() < 1e-10 {
252 // Degenerate plane orientation w.r.t. the frustum — leave
253 // projection unchanged rather than divide-by-zero. The
254 // reflection still renders, just without near-plane clipping.
255 return proj;
256 }
257 let scale = 2.0 / denom;
258 let m = [c[0] * scale, c[1] * scale, c[2] * scale, c[3] * scale];
259
260 // proj is column-major: proj[col][row]. The "third row" we want
261 // to replace is at row index 2 across all four columns. The
262 // "fourth row" is at row index 3. New row-2 = M - row3 — but
263 // since wgpu's clip-space z range is [0, 1] (not [-1, 1] like
264 // OpenGL), the depth-rescaling pre-step is `M - P_row3` exactly
265 // as in Lengyel's original derivation; the [-1, 1] vs [0, 1]
266 // difference is absorbed by the scale.
267 let mut out = proj;
268 out[0][2] = m[0] - proj[0][3];
269 out[1][2] = m[1] - proj[1][3];
270 out[2][2] = m[2] - proj[2][3];
271 out[3][2] = m[3] - proj[3][3];
272 out
273}
274
275fn normalise(v: [f32; 3]) -> [f32; 3] {
276 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];

Callers 2

Calls 2

mat4_invertFunction · 0.85
mat4_mul_vec4Function · 0.70

Tested by 1