Atlas and Sunlight
by J.C. Smith · in Torque Game Engine Advanced · 08/28/2007 (9:01 pm) · 5 replies
Hello everyone. I've recently been working on a tgedaynight port for Josh's MMO Kit on TGEA (a TGE version of his code can be found in the resource section here or in his Kit). One of the issues of course with Atlas is that it doesn't respond to sunlight. I found it was very easy to get it working with sunlight though, so maybe this will be of useful to some of you out there. I also have one minor issue that is happening and will mention that here. This problem only happens if you are changing the sunlights value over time.
To get in basic functionality all you need to edit in code is your AtlasClipMapBlenderCache.cpp or AtlasClipMapUniqueCache.cpp to pass the sunlight to your shader.
Before the DrawIndexedPrimitive calls you want to add something like this:
Note in the above example it's using tgeDNCurrentColor's red value and assuming this is uniform for your green and blue. If you aren't using the daynight code and you wanted to get suns ambient light or sunlight in there you would want to pull the current color from there (sun->ambient or something similar).
You'd then just have to edit the shaders (atlasblenderps20p.hlsl for example for the pixel shader 2.0 blender terrain shader) so that they multiply the suns color by the terrain color. example below from atlasblenderps20p.hlsl:
That will give you some simple lighting in your scene. If your sunlight is changing over time though (as in the tgedaynight code) then you will run into a problem over time, in that your atlas textures will become borked, as some do not update properly to the new lighting. With subtle changes this doesn't become evident for a while, but if you are going from like 3am to noon, you will wind up with rather nasty black blotches.
I'm still trying to find a real fix for this, but currently I have found a workaround for it. When it ticks to a new hour of gametime (in my case every 3 minutes) I had it reset the textures with this line of code:
This works, but it does cause the graphics to hitch momentarily. It's pretty fast on my system (under 100 ms) but I have a pretty fast system here. On slower systems it might hitch much longer. For now that is a reasonable workaround, I'd like to find a better way of handling it. I have experimented with only resurrecting the atlas textures but had problems getting that to function properly (never worked). If anyone has any ideas feel free to share them.
A shot of this in action can be seen here: http://www.therepopulation.com/daynighttest.jpg
To get in basic functionality all you need to edit in code is your AtlasClipMapBlenderCache.cpp or AtlasClipMapUniqueCache.cpp to pass the sunlight to your shader.
Before the DrawIndexedPrimitive calls you want to add something like this:
F32 sunlightcolor = tgeDNCurrentColor.x; GFX->setPixelShaderConstF (PC_USERDEF1, (float*)&sunlightcolor, 1);
Note in the above example it's using tgeDNCurrentColor's red value and assuming this is uniform for your green and blue. If you aren't using the daynight code and you wanted to get suns ambient light or sunlight in there you would want to pull the current color from there (sun->ambient or something similar).
You'd then just have to edit the shaders (atlasblenderps20p.hlsl for example for the pixel shader 2.0 blender terrain shader) so that they multiply the suns color by the terrain color. example below from atlasblenderps20p.hlsl:
Fragout main(v2f IN,
uniform sampler2D opacity : register(S0),
uniform sampler2D lightMap : register(S1),
uniform sampler2D tex1 : register(S2),
uniform sampler2D tex2 : register(S3),
uniform sampler2D tex3 : register(S4),
uniform sampler2D tex4 : register(S5), // note added a comma
uniform float sun : register (C6) // <-- this is new
)
{
Fragout OUT;
// Get colors
float4 o = tex2D(opacity, IN.tex);
float4 t1 = tex2D(tex1, IN.tex1);
float4 t2 = tex2D(tex2, IN.tex2);
float4 t3 = tex2D(tex3, IN.tex3);
float4 t4 = tex2D(tex4, IN.tex4);
float4 lm = tex2D(lightMap, IN.lmTex);
// Blend
OUT.col = lm * (o.r * t1 + o.g * t2 + o.b * t3 + o.a * t4) * sun; // note: this is changed
return OUT;
}That will give you some simple lighting in your scene. If your sunlight is changing over time though (as in the tgedaynight code) then you will run into a problem over time, in that your atlas textures will become borked, as some do not update properly to the new lighting. With subtle changes this doesn't become evident for a while, but if you are going from like 3am to noon, you will wind up with rather nasty black blotches.
I'm still trying to find a real fix for this, but currently I have found a workaround for it. When it ticks to a new hour of gametime (in my case every 3 minutes) I had it reset the textures with this line of code:
GFX->resurrectTextureManager();
This works, but it does cause the graphics to hitch momentarily. It's pretty fast on my system (under 100 ms) but I have a pretty fast system here. On slower systems it might hitch much longer. For now that is a reasonable workaround, I'd like to find a better way of handling it. I have experimented with only resurrecting the atlas textures but had problems getting that to function properly (never worked). If anyone has any ideas feel free to share them.
A shot of this in action can be seen here: http://www.therepopulation.com/daynighttest.jpg
#2
08/31/2007 (12:29 pm)
Brilliant Brian! Where do I send the flowers/chocolate/beer?
#3
But this is definatly useful for non lightmapped terrian. Thanks Brian and JC.
Ves
09/01/2007 (11:22 am)
Now that 1.03 is released with full(??) support for lightmaps on atlas terrian. I would think it should be possible to just pre-generate a number of lightmaps and swap the lightmap in and out when the time changes. I haven't looked at the other resources and I have only a limited knowledge of how lightmaps work in torque so I am probably way oversimplifying. But this is definatly useful for non lightmapped terrian. Thanks Brian and JC.
Ves
#4
@Ves: I was playing with the new lightmapped terrain yesterday. I haven't gotten it ported correctly yet, I'm getting a crash, I'm sure I borked something in merging the code. I did test it though on 1.03 first, and noticed there is a loadAlternateLightmap function that allows you to load in a saved lightmap. As a test of that I disabled the relight code for Atlas and had it load a precreated lightmap (mapnamelightmap.atlas) every time it loads, and that worked. I'm not sure how fast it can read those on the fly, as haven't tested it yet. But if it can read them in at a quick rate you could use that.
09/01/2007 (6:43 pm)
Excellent Brian. Thanks for sharing that. @Ves: I was playing with the new lightmapped terrain yesterday. I haven't gotten it ported correctly yet, I'm getting a crash, I'm sure I borked something in merging the code. I did test it though on 1.03 first, and noticed there is a loadAlternateLightmap function that allows you to load in a saved lightmap. As a test of that I disabled the relight code for Atlas and had it load a precreated lightmap (mapnamelightmap.atlas) every time it loads, and that worked. I'm not sure how fast it can read those on the fly, as haven't tested it yet. But if it can read them in at a quick rate you could use that.
#5
One of the cooler things about this atlas format is how it handles loading and unloading. From looking at the code in loadAlternateLightMap it looks like it spins off a thread to load the file then later (below) checks to see if its loaded before swaping out the lightmaps and such.:
If one were to thread that method call (loadAlternateLightMap) it looks like the inards are setup to load while still rendering without a hitch. So the player shouldn't get a stutter while it loads the new lightmap. This atlas stuff is way cool!!!
Thanks for pointing me at the loadAlternateLightMap method. Its looks like that method was built for exactly FOR day/night cycles :).
Ves
09/02/2007 (6:31 am)
I'm not sure how fast it can read those on the fly, as haven't tested it yet. But if it can read them in at a quick rate you could use that.One of the cooler things about this atlas format is how it handles loading and unloading. From looking at the code in loadAlternateLightMap it looks like it spins off a thread to load the file then later (below) checks to see if its loaded before swaping out the lightmaps and such.:
while(!mClipMap->fillWithTextureData())
{
syncThreads();
Platform::sleep(10);
}If one were to thread that method call (loadAlternateLightMap) it looks like the inards are setup to load while still rendering without a hitch. So the player shouldn't get a stutter while it loads the new lightmap. This atlas stuff is way cool!!!
Thanks for pointing me at the loadAlternateLightMap method. Its looks like that method was built for exactly FOR day/night cycles :).
Ves
Torque Owner Brian Richardson
FragOut pixClipmap( PixClipMapConnectData IN, uniform sampler2D diffuseMap[mapCount] : register(S0)) { FragOut OUT; // Do a layered blend into accumulator, so most detail when we have it will show through... OUT.col = tex2D(diffuseMap[0], IN.texCoord[0].xy); for(int i=1; i<mapCount; i++) { float scaleFactor = saturate(IN.fade[i] * 2.0); float4 layer = tex2D(diffuseMap[i], IN.texCoord[i].xy); OUT.col = lerp(layer, OUT.col , scaleFactor); } return OUT; }and just add a
Before the return and you should be good. That way you won't need to flush the clipmap textures at all, and this will work for non-blended Atlas terrains as well.