Getting data out of a lightmap texture

Started by
2 comments, last by JohnRaptis 12 years, 2 months ago
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!
Advertisement
Are you already reaching memory limits? If you can get away with a 25% or 50% size, perhaps you could just keep a copy of the lightmap so that you don't have to lock/unlock to grab the RGB (just grab it directly from memory). And it seems the lightmap is just intensity only, so perhaps you could save the copy as a two-dimensional unsigned char array.

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.

Are you already reaching memory limits? If you can get away with a 25% or 50% size, perhaps you could just keep a copy of the lightmap so that you don't have to lock/unlock to grab the RGB (just grab it directly from memory). And it seems the lightmap is just intensity only, so perhaps you could save the copy as a two-dimensional unsigned char array.


I would LOVE to save it as an unsigned char array... but I've been trying to find the GL texture type to do this, and I didn't see it anywhere (at least, not that is supported on iPhone). Do you know of one? Finding the documentation on iPhone is hard, because Apple seems to have these boundaries of what you will and won't "want" to do, and doesn't bother to tell you anything else.


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[/quote]

This is how stumped I am: I'm not even 100% sure clear on how one creates a shader... I've never used them before. Making it more complex, I need something that would work on DirectX, OpenGL, and OpenGLES. smile.png I once tried to make a shader that would make my screen greyscale, and it wouldn't even compile for me. :) Because I always have some pressing project or another, I've put shaders in my "later" box.

In theory the shader just reads a pixel I specify on the texture (can I specify these on the fly? Or would a shader have to be compiled for each grid position??) and then draws everything multiplied by that pixel until I turn the shader back off.

This topic is closed to new replies.

Advertisement