Sign in to follow this  

Light transport on 3d grid? Dont know how to name that technique

Recommended Posts

WiredCat    1451

So basically i have 256x256x256 grid that stores values 0 or 255 (0 = no light -> 255 light)


since i use a heightmap (in texture) to create a terrain i only have 256 levels of height in it)


Now here comes the thing i want shadows (only for terran) and i tried shadowmapping but that doesn't seem to be working liek it shoudl at all, i want to use shadowvolumes but they are patented so its not an option, so i decided to make some sort of volumetric baking 


i basically set the grid to be fully lit by light then i go through heightmap texture) and do raymarch (x, y from heightmap, z) so i basically have a heght point on grid (taken from heightmap) and go in the direction of 'light direction' and make every cell (except the first one i hit) to 0 (so theres no light) -> then i make a 2d lightmap texture out of computed 3d grid basically i take the pixel at the height defined on heightmap. 


i think idea sounds legit but i get not exactly what i wanted, maye somone have encountered similar problem.





first screenshot is taken from light point of view (camera is in light position)

unsigned char LIGHT_GRID[TerraDim][TerraDim][TerraDim]; //16 MB for 256x256x256 grid...

t3dpoint<int> point_to_grid_cell(vec3 vector)
	vec3 dist = vector - min;
	t3dpoint<int> result;
vec3 BoxLen = (max - min);

result.x =  int(( (dist.x / float(BoxLen.x)) * 255.0)+0.5);
result.y =  int(( (dist.y / float(BoxLen.y)) * 255.0)+0.5);
result.z =  int(( (dist.z / float(BoxLen.z)) * 255.0)+0.5);
          return result;

unsigned char lightmap[TerraDim * TerraDim * 4];
unsigned int lmap_tex;

void Make32bppLightmap(bool normals_calced, vec3 lightpos)
	vec3 cell_size = (max - min) / 255.0;
	cell_size.y = 1.0 / 255.0;
	if (!normals_calced) GenerateNormals();

vec3 center = (max + min) / 2.0;
vec3 light_dir = Normalize( vectorAB(lightpos, center) );

for (int x=0; x < TerraDim; x++)
for (int y=0; y < TerraDim; y++)
for (int z=0; z < TerraDim; z++)
	LIGHT_GRID[x][y][z] = 255; //set light everywhere

for (int y=0; y < TerraDim; y++)
for (int x=0; x < TerraDim; x++)

float height = float(hpdata[y*TerraDim*4 + x*4 + 0]);

vec3 act_vertex = terrain[y*TerraDim + x].v + vec3(0.0, HEIGHT_SCALE*(height / 255.0), 0.0);

t3dpoint<int> cell = point_to_grid_cell(act_vertex);

//now raymarch through volume
for (int i=1; i < 320; i++)
	vec3 transition;
	transition.x = light_dir.x * cell_size.x * float(i)*0.9;
	transition.y = light_dir.y * cell_size.y * float(i)*0.9;
	transition.z = light_dir.z * cell_size.z * float(i)*0.9;
t3dpoint<int> cell2 = point_to_grid_cell(act_vertex+transition);

if ( (cell2.x > 255) || (cell2.y > 255) || (cell2.z > 255) )  break;
if ( (cell2.x < 0) || (cell2.y < 0) || (cell2.z < 0) ) break;

if (cell == cell2) continue;

LIGHT_GRID[cell2.x][cell2.y][cell2.z] = 0;
cell = cell2;


//we have the light data now we must somehow port that from 3d to 2d
for (int x=0; x < TerraDim; x++)
for (int z=0; z < TerraDim; z++)
	lightmap[z*TerraDim*4 + x*4 + 0] = 255; //default theres light everywhere
	lightmap[z*TerraDim*4 + x*4 + 1] = 0;
	lightmap[z*TerraDim*4 + x*4 + 2] = 0;
	lightmap[z*TerraDim*4 + x*4 + 3] = 255;

	float height = float(hpdata[z*TerraDim*4 + x*4 + 0]);
	vec3 act_vertex = terrain[z*TerraDim + x].v + vec3(0.0, HEIGHT_SCALE*(height / 255.0), 0.0);
	t3dpoint<int> cell = point_to_grid_cell(act_vertex);
	lightmap[z*TerraDim*4 + x*4 + 0] = LIGHT_GRID[cell.x][cell.y][cell.z];


//upload texture to GPU

glGenTextures(1, &lmap_tex);
glBindTexture(GL_TEXTURE_2D, lmap_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TerraDim, TerraDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, lightmap);

Edited by WiredCat

Share this post

Link to post
Share on other sites
Krypt0n    4721

That is called "horizon mapping", it's not uncommon for baking occlusion of terrain and bumpmaps.


there are optimizations to make it faster e.g. height-surfing, which is basically how voxel raycasting in those oldschool games (e.g. commanche) worked, just inverse.

if your lightsource is restricted to the pixel/texel axis, you can implement it very efficiently with MMX or SSE.


to improve quality it's common to send out a few more rays per texel, with a bit of jittering. sometimes just a tiny gaussian blur can also increase the perceptual quality.



I lovely trick is also to split the 8bit of the lightmap into a two 4bit values that represent a min-max for the cone of visibility. imagin that like the time of day of sun rise and sun set, that way you can in realtime, with just one constant buffer parameter interpolate the shadow (that works only with you assume the sun travels on an 90degree axis to the terrain)

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this