Hi all,
I'm working on an isometric game that draws in three layers:
1) Ground level
2) Lightmap (multiplier)
3) Any object with vertical height
An example screenshot is here:
http://i.imgur.com/a3X7w.jpg
When I draw my vertical objects, I want to draw them lit based on the status of the lightmap at their BASE-- that way I get nice silhouettes of trees and things that are in front of the lightmap. I am currently doing this via a brute force method, of computing the distance of the object in question to a light source, and dimming it accordingly.
Because of memory constraints (this game is coming out for iPhone as well as PC), I cannot count on the lightmap texture being the same size as the screen-- thus I don't really have the option of masking off these objects in the lightmap. The Lightmap could be 50% the size of the screen, or even 25% in severe memory cases.
The normal way I'd approach this is too slow... it would go something like this:
Color TreeColor=LightMapTexture.GetRGB(TreeBase.x,TreeBase.y);
SetColor(TreeColor);
Tree.Draw(TreeBase.x,TreeBase.y);
That involves locking and unlocking the lightmap. I could set it up to happen only once a frame, but when I do it, the delay is... more than I expected it to be.
Someone mentioned to me that it might be possible to accomplish this with a shader. So my question is... is is possible to accomplish this with a shader? On both DirectX and OpenGLES? I'm very, very bad at shaders, so if it's possible, if you could kinda point me in the right direction, I'd appreciate it!
Thanks!
Yes, definitely. In fact, I'd advise merging the base/lightmap passes together so that it all happens in one shader while you're at it. While my iPhone knowledge is a bit shaky, I believe the fillrate on the embedded GPU is totally crap and the way things are set up you're going to have at *least* 2x overdraw, generally more (1x for drawing the map, another 1x for multiplying by the lightmap, and any 'vertical' objects are icing on the cake)
So, two bits of rendering advice:
1) Fix your base pass. This should actually be fairly simple, assuming you're using only opaque/alpha-tested objects:
a) Turn on stencil testing as follows:
- Enable stencil writes
- Set the stencil 'pass' op to zero/decrement depending on taste
- Set the stencil 'fail' op to keep
- Set the comparison operator to 'equal'
- Set the stencil ref value to 1 (though technically anything else could work)
- Clear stencil to whatever you decided to make the stencil ref value in the previous step at the beginning of the frame
b) Sort your non-flat objects front-to-back (in your case, bottom-to-top might be a better way of phrasing it) and draw them.
c) Disable stencil writes, (but make sure you keep tests on!) then draw the ground.
d) Postprocess if necessary.
In plain English, this will mark areas you've already drawn to and instruct the GPU to not bother drawing/saving the results of drawing for any later pixels you've already touched, which should hopefully improve performance somewhat, especially when you have a lot of objects.
2) Use shaders for getting at the on-GPU lightmap:
a) Figure out where in the lightmap you want the object to sample in affine 0-1 space
b) Pass that in as a shader constant
c) In the pixel shader, feed that constant into a lightmap texture sample function as a sample position/texture coordinate
d) Multiply the object's base color by that lightmap value before returning from the shader.
e) Marvel at the power of programmable shading!
The mechanics of most of these are fairly straightforward to express in GLSL/HLSL, so I'll leave them as an excercise. If you're really stumped I'd be happy to give you some direction, however
For extra credit, you may be able to enhance object drawing with a separate 'lightmap offset' texture that'd look very much like a normal map. This would basically constitute computing where you want to look in the lightmap relative to where you are in the sprite and saving it to a texture. Ideally the result would be in a signed, two-component compressed texture but I don't think there's something directly like that available for iPhone.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.