For Trials of Asphodel we are aiming to build with Unity Indie, which has a few restrictions, and probably the biggest restriction for us is the lack of render to textures.
(If you just want the code skip the next 5 of paragraphs of backstory)
In the temple level Martin wanted shallow reflective water, to do this a few of options spring to mind, one would be realtime render to texture reflection using something like the Mirror Reflection Script on the Unity wiki. This re-renders the scene from a second camera flipped on the Y axis and looks great, BUT requires Unity Pro so that was out.
Another option would be to mask off the water and render a second camera from the flipped Y axis reflection position, maybe Cull None on all the shaders to fix reversed triangle winding, but this wouldn’t have allowed any texture distortion so wouldn’t have looked very watery.
That leaves cubemap reflection. Generating cubemaps can be done in Indie using this handy script. Here’s the cubemap I generated.
The standard cubemap shaders work great on curvy objects but on planar surfaces the illusion is broken, the view direction doesn’t change so reflections seem magnified and appear not to move correctly. An increasingly common solution is Box Projected Cubemaps, where the reflection vector is corrected to appear to mapped to a cube. Box Projection works great for cuboid rooms, and is used on every object in VRoom, but the arenas in Trials are circular and not a good fit for Box Projection.
So final solution, Spherical Projection Mapping, correcting the refection vector to map onto the inside of a sphere. I couldn’t find any shader snippets online to do it, but there were a couple of resources on the theory and many resources on the maths principles. Theory wise this blog post by Sébastien Lagarde is a great resource. And for the actual maths this page on lighthouse3d.com is useful. I jotted it all down at 2am when the idea popped into my head.
The result looks like this;
(CODE STARTS HERE)
Anyway, the Shader, this is for a surface shader but could be easily translated to vert/frag I think. I’ve only included the bit relevant to the reflection, skipping out normal mapping and transparancy etc.
_Center is the centre of the sphere
_Radius is the radius of the sphere
IN. and o. properties are built in surface shader values
//get the vector direction from the center of the sphere to the origin of the ray
float3 centerToPosition = IN.worldPos - _Center.xyz;
float3 worldRefl = WorldReflectionVector (IN, o.Normal);
//normalize the reflection vector
float3 reflection = normalize(worldRefl);
//get directional vector to the closest point on the ray to the center of the circle
float3 proj = (dot(centerToPosition, reflection)/length(reflection))* reflection;
//get the position of the closest point
float3 projCoord = IN.worldPos - proj;
//get vector direction between the center of the sphere and the closest point
projCoord -= _Center.xyz;
//cache the magnitude (saves on an instruction)
float projCoordMag = length(projCoord);
//use pythagoras to length of the remaining side
float distanceToP = sqrt((_Radius * _Radius) - (projCoordMag * projCoordMag));
//get the position of the intersection
float3 P = projCoord + (reflection * distanceToP);
//the final
float3 correctedReflection = normalize(P - _Center.xyz);
fixed3 reflcol = texCUBE (_Cube, correctedReflection);
Resolution artifacts due to the arena being 80m wide can be disguised with a normal map.
I think the result is passable and especially with the normal map distortion is a worthwhile Indie alternative to render to texture reflections.
For the Trials of Asphodel indiedb page go to http://www.indiedb.com/games/trials-of-asphodel