We in the DS community used to think that UE2 has “bugged mapping”. Come to think of it, this phrase makes very little sense (which I can safely say now that I have some hands-on experience writing RSL code, environment lights in particular), but, well, there was and is that fact that matching a directional light to follow shadows cast by UE2 IBL is nigh impossible (i.e. mostly guesswork).
I actually have no idea what is going on inside any of the Omnifreaker’s shaders: they are closed-source. This was one of the reasons I started writing my own – because there comes a time when you grow damn tired of guesswork and all that experimenting with black boxes.
But sure as hell I know what is going on inside my own stuff.
So when I noticed that even though I write correct code, it’s damn impossible to match directional lights to my own envlight, and in surface shaders, environment-mapped reflections come out rotated frellin’ 90+ degrees, I set out to explore the biggest black box of all.
First of all, I took out the staples – Omnifreaker’s UberSurface and DS Default material aka dzDefault. Needless to say – they handle mapped reflections differently. So differently it’s almost ridiculous. Take a look.
// all the images in this post were rendered with my “fixed” DelightGI envlight //
So, how do we go about determining what is right? Simple. We enclose the scene in a “real” environment sphere and raytrace the whole thing. In my case, it’s a sphere 10 m in diameter, translated 5 m down on the Y axis so as to be intersected by the ground plane through its equator; the sphere does not cast shadows, and it has UberSurface applied, with the map both in diffuse and ambient channels to make its reflection bright enough; it is also excluded from GI (“Occlusion” set to off) so as not to cast GI shadows = not to obscure the HDR IBL environment light.
Sure how the map lies on the sphere will depend on the UV mapping of the sphere. But since we are creating our scenes in DS, we will use DS-specific primitive spheres as our reference.
Now we see that shaders finally start behaving consistently (and note that dzDefault does Fresnel attenuation of reflection when IoR is set above 0! Even when refraction strength is zero! Neat, huh? But RadiumFabric looks to win regarding “fireflies” – probably because it makes use of more “up-to-date” inner workings of the trace() shadeop…):
And here is a render with the “fixed” mapped reflection in my RadiumFabric shader (with a raytraced reflection for comparison – and even though there are more fireflies from this angle, dzDefault has even more – I checked) :
These reflections are not 100% identical – and they should not be – but they are close enough (and all the directions match).
How did I “fix” my shaders? Well, the fix didn’t have much to do with the shaders per se, but with the coordinate system DS uses. I had a hunch there is something weird going on – when I had to rotate “up” vectors in shaders like 3Delight’s PhysicalSun so that they’d make sense in DS – but it’s the first time it really hit me in the face. The thing is, you can’t rotate certain calls within certain functions.
But! You can create a new, rotated coordinate system for proper envmap alignment and use it in your shaders, in turn. It takes a few lines in the render script, calling specific Renderman interface routines.
Just one more reason for me not to support my shaders used without my render scripts: y’know, I don’t render without my scripts anymore, so for me, it wouldn’t have been easy (and I am a freebie provider, after all; I don’t have _that_ much free time I could devote to dealing with each and every potential issue).