Archived

This topic is now archived and is closed to further replies.

Raloth

day/night terrain lighting

Recommended Posts

I''m looking for a lighting algorithm that will let the lighting of the ground change depending on where the "sun" is. That way I could have the sun rise and set to simulate a day. Unfortunately there is nothing out there that I can find . I don''t think lightmaps are going to be possible since it will take too much time to recalculate them on the fly. Hardware lights or changing vertex color is not an option either because it will emphasize the popping in level of detail. So...what''s left?

Share this post


Link to post
Share on other sites
Lightmapping can work. Just precalculate a set of lightmaps for different positions of the sun in the sky at regular intervals, and interpolate between the appropriate two lightmaps from the set.

Also, per-pixel lighting might be something you could look into. If I''m not mistaken, a flipcode IOTD featured lighting that was uniform across all levels of detail.

Share this post


Link to post
Share on other sites
I wrote a ROAM heightmap engine that used vertex lighting to do that effect, and it didn''t emphasize the popping because you just blend the light colors -- it''s really easy. That means as you interpolate the vertex positions, you also interpolate the vertex colors.

~CGameProgrammer( );

-- Post screenshots of your projects. 100+ posts already in the archives.

Share this post


Link to post
Share on other sites
hm.. instead of a lightmap create a normalmap and depending on what your card supports use the dotproduct of lightdirection and normal. on my gf3 i could hardly feel any slowdown and it was a lot faster than opengls vertex lighting (though others dont seem to have that problem, so i guess the slow vertex lighting had other reasons).

also: if the normal map has the same dimension as the heightmap the result should look like vertex lighting at highest detail (or maybe not, i think gouraud was only calculating light for the corners and interpolates instead of interpolating the normals... hint meaning: the filter mode for the normal map will influence the look of the lighting).

cleaning out an experimental fragment program (as i can only emulate them at 0.1fps instead of 90fps) it might look like this (detail textures are left out, at some point just multiply the result of the lighting stuff with the texture):


!!ARBfp1.0

ATTRIB globc = fragment.texcoord[0];
ATTRIB lightcol = fragment.color.primary;
ATTRIB lightdir = fragment.color.secondary;
ATTRIB ambient = whatever;

TEMP norm, ldir;
TEMP light;

TXP norm, globc, texture[0], 2D;

#bring components into range of -1,1 (if stored as 0,1)
MAD norm, norm, 2, -1;
MAD ldir, lightdir, 2, -1;
DP3 light, norm, ldir;
MAD result.color, light, lightcol, ambient;
MOV out.a, 1;

END


if your have an nvidia and use register combiners these parts of the setup handle lighting:


//do lighting basic

glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_SECONDARY_COLOR_NV, GL_EXPAND_NORMAL_NV, GL_RGB);
glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV,
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);

//do lighting ambient

glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, GL_WHEREEVER_AMBIENT_IS_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV,
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);




[edited by - Trienco on November 17, 2003 2:25:17 AM]

Share this post


Link to post
Share on other sites
what about spherical harmonics?it can generate soft shadows and be a global elimination solution and can be used with dynamic lighting but static geometry(the shape of the geometry doesn't change).I think it is good for terrain rendering.
also you can use light scatering to simulate the sun,sky and fog colors.

[edited by - mohamed adel on November 17, 2003 6:34:33 AM]

Share this post


Link to post
Share on other sites
If you dont have PixelShaders you'll have to do sofware DOT3 on the normal map. Of course you could do this without PS but it will suck because for a 90deg angle between the sun and the normal the texel will be all black. you have to bias the DOT3 to get ambient light.
What i did:

1. create a normal map and resample it down to 1/4 original size (to smooth shadows).
2. each frame get the sun's position (normalized vector because the sun is a directionl light)
3. DOT3 each pixel (RGB) in the subsampled normal map with the suns position and add the value of the ambient color.
4. upload the texture and use it in another pass using a modulate texture environment.

here's the code:

(the heightmap has to be 2^n +1 x 2^n +1 )

void CNormalMap::UpdateLightMap(CVector3 vLight)
{
int i, size;

size = Img.GetWidth() * Img.GetHeight();

BYTE* ptr = Img.GetDataPointer();
BYTE* res = Light.GetDataPointer();

float dot, r, g, b;

for (i = 0; i < size; i++)
{
r = *ptr * 0.00392156862f - 0.5f;
g = *(ptr + 1) * 0.00392156862f - 0.5f;
b = *(ptr + 2) * 0.00392156862f - 0.5f;
dot = 4 * (r * vLight.x + g * vLight.y + b * vLight.z);

dot = (dot + 1.0f) * 0.5f;

if (dot < 0.3f) dot = 0.3f;
if (dot > 1.0f) dot = 1.0f;

*res = dot * 255.0f;

ptr += 3;
res += 1;
}

Tex.LoadFromBuf(Light.GetDataPointer(), Light.GetWidth(), Light.GetHeight(), 1, false);
}

void CNormalMap::ComputeNormals(BYTE* SrcImg, int ImgSize, CVector3* vNormals)
{
int i, j;
float dX, dY;
CVector3 vN;
unsigned char h;

for (i = 0; i < ImgSize; i++)
{
for (j = 0; j < ImgSize; j++)
{
// Do Y Sobel filter

h = SrcImg[((i - 1 + ImgSize) % ImgSize) * ImgSize + (j + 1) % ImgSize];
dY = ((float) h) / 255.0f * -1.0f;

h = SrcImg[(i % ImgSize) * ImgSize + (j + 1) % ImgSize];
dY += ((float) h) / 255.0f * -2.0f;

h = SrcImg[((i + 1) % ImgSize) * ImgSize + (j + 1) % ImgSize];
dY += ((float) h) / 255.0f * -1.0f;

h = SrcImg[((i - 1 + ImgSize) % ImgSize) * ImgSize + (j - 1 + ImgSize) % ImgSize];
dY += ((float) h) / 255.0f * 1.0f;

h = SrcImg[(i % ImgSize) * ImgSize + (j - 1 + ImgSize) % ImgSize];
dY += ((float) h) / 255.0f * 2.0f;

h = SrcImg[((i + 1) % ImgSize) * ImgSize + (j - 1 + ImgSize) % ImgSize];
dY += ((float) h) / 255.0f * 1.0f;

// Do X Sobel filter

h = SrcImg[((i - 1 + ImgSize) % ImgSize * ImgSize + (j - 1 + ImgSize) % ImgSize)];
dX = ((float) h) / 255.0f * -1.0f;

h = SrcImg[((i - 1 + ImgSize) % ImgSize) * ImgSize + j % ImgSize];
dX += ((float) h) / 255.0f * -2.0f;

h = SrcImg[((i - 1 + ImgSize) % ImgSize) * ImgSize + (j + 1) % ImgSize];
dX += ((float) h) / 255.0f * -1.0f;

h = SrcImg[((i + 1) % ImgSize) * ImgSize + (j - 1 + ImgSize) % ImgSize];
dX += ((float) h) / 255.0f * 1.0f;

h = SrcImg[((i + 1) % ImgSize) * ImgSize + j % ImgSize];
dX += ((float) h) / 255.0f * 2.0f;

h = SrcImg[((i + 1) % ImgSize) * ImgSize + (j + 1) % ImgSize];
dX += ((float) h) / 255.0f * 1.0f;

vN.x = -dX;
vN.y = -dY;
vN.z = 1;

vN = Normalize(vN);

vNormals[i * ImgSize + j] = CVector3(-vN.y, vN.z, -vN.x);
}
}
}

void CNormalMap::ComputeNormalMap(BYTE* SrcImg, int ImgSize)
{
CImage TempImg;
int i, j;

CVector3* Normals;

Normals = new CVector3[ImgSize * ImgSize];

ComputeNormals(SrcImg, ImgSize, Normals);

int Size = ImgSize - 1;
CVector3 vSum;

Img.Create(Size / Scale, Size / Scale, 3);
Light.Create(Size / Scale, Size / Scale, 3);

TempImg.Create(Size, Size, 3);

for (i = 0; i < Size; i++)
{
for (j = 0; j < Size; j++)
{
vSum = Normals[i * ImgSize + j] +
Normals[i * ImgSize + j + 1] +
Normals[(i + 1) * ImgSize + j] +
Normals[(i + 1) * ImgSize + j + 1];

vSum = Normalize(vSum);

TempImg.PutPixel((i * Size + j) * 3 ,(BYTE) ((vSum.x + 1.0f) / 2.0f * 255.0f));
TempImg.PutPixel((i * Size + j) * 3 + 1,(BYTE) ((vSum.y + 1.0f) / 2.0f * 255.0f));
TempImg.PutPixel((i * Size + j) * 3 + 2,(BYTE) ((vSum.z + 1.0f) / 2.0f * 255.0f));
}
}

delete [] Normals;

GL.ScaleImage(GL_RGB, Size, Size, GL_UNSIGNED_BYTE, TempImg.GetDataPointer(), Size / Scale, Size / Scale, GL_UNSIGNED_BYTE, Img.GetDataPointer());

TempImg.Destroy();

// Img.SaveToFile("data\\normal.bmp");

}


damn, i cant belive how much my code rules

EDIT: Eelco pointed out something. so if the sunlight.y < 0 just skip the dot3

[ My Site ]
All your source are belong to us
/*ilici*/


[edited by - Ilici on November 17, 2003 1:09:01 PM]

Share this post


Link to post
Share on other sites
something to take into account..

as you rotate your lightdirection vector all around, youll get in trouble when the sun sinks down the horizon.
obviously, you dont want any light to fall on your terrain anymore then. but unless you have a realtime shadow solution for your terrain, triangles on a slope might still face the light. therefore youll need to wink out the strength of the light quickly but smoothly as the sun goes below the horizon.

Share this post


Link to post
Share on other sites