Following up on the Reflections demo.
Reflection and refraction using environment maps are a simple way to make cool looking materials. However, environment mapping is limited to far away, static scenes. I was looking to render reflections in a dynamic scene.
I recall that certain games compute reflections or indirect lighting by sampling the environment at regular position intervals. Reflected rays would use / interpolate nearby samples to compute the reflected color. Although this is fast, efficient and would support an arbitrary number of reflective surfaces, I was also interested in something more accurate.
I started with a plane mirror, but I would like to explore with concave, convex or transparent panes in the future. A plane mirror is straightforward because the virtual image can besimulated by flipping the camera position and orientation around the plane.
In OpenGL, we can compute this reflected camera position and orientation, render the scene, and then use that render as the environment map with the actual camera. However, the reflected camera is behind the mirror, which means the mirror would obscure the entire scene. To solve this, we need to clip everything before the mirror.
Simply setting the near plane would not work because objects close to the mirror would then also be clipped.
A second approach was to scale the coordinates in the vertex shader such that the z-coordinate falls under the near plane. Depending on the orientation of the plane in camera space, we can compute a scaling factor based on x and y, and multiply the entire vertex by that scaling factor. However, this caused major artifacts as the camera approached the mirror plane. The scaling factor up close approached infinity, causing issues for vertices near the mirror plane.
The third approach was to pass the clipping plane to the fragment shader and use the discard statement on fragments behind the mirror. We can pass world-space coordinates through the shaders and compute which side of the plane it is on. To note, one down side of this approach is that almost all objects must implement these checks on the fragment shader.
Finally, to perform reflections on multiple mirrors that face each other, we require multiple renders (kn^2 renders for n mirrors and k levels of depth). For each mirror, we compute k reflections of the camera, alternating between each camera (thus n^2 renders). The renders to texture must be done backwards, starting with the deepest reflection.