lobbel

08-27-2010, 12:19 AM

Hello,

I need some professionel help. For empty space leaping in

volume raycasting I store the octree in a 3d texture. To be

more precisely, I store only the max depth of the tree. For

instance a depth of 5 results in 8^5 (32768) boxes). So the

desired 3d texture (format: GL_RGB) is 2^5 32x32x32.

I create the octree on the cpu and every node (at max depth level) of the octree samples the volume with respect to it's current size. For example the box of a node has a size of

[0.5,0.0,0.0] - [0.75,0.25,0.25]. The volume may 256x256x256.

The space this node samples then is from

256*0.5, 256*0.0, 256*0.0 till 256*0.75,256*0.25,256*0.25

I sample one extra voxel at the borders of each node to avoid

interpolation errors (later on GPU side).

with the coordinates of the box I can calculate the indices

to write the min and max values of each node in an array that I later upload to th 3d texture. To create the 3d texture for storing the octree, I first create an array on the cpu side. for this example it's size is 32*32*32*3 because of depth 5. I take the size threetimes because I create a RGB texture. While creating the octree I write the min and the max value that has been calculated by sampling the node. And I only write those values at the lowest depth off course, here at depth 5. Additionally to the octree I create a min-max texture. This texture is as wide and as heights as there are values. That means, a 8bit volume data set has values from 0 to 255. so the texture size is 256*256. In this

texture I write for every min max pair whether the data is visible or not.

Before continuing with the problem, here is some code

node sampling

if(currDepth == maxDepth)

{

/*

max depth has been reached, incr. count

*/

childrenCount++;

/*

aabb boundaries

*/

glm::vec3 llf = aabb.getLowerLeft();

glm::vec3 ur = aabb.getUpperRight();

/*

vol size

*/

glm::vec3 vecSize = volume->getDimension();

/*

*/

valMin = 255.0f;

valMax = 0;

unsigned char val = 0;

unsigned char* pvoxels = static_cast<unsigned char*(Node::volume->getVoxels());

float tmpX0 = vecSize.x * llf.x;

float tmpX1 = vecSize.x * ur.x;

float tmpY0 = vecSize.y * llf.y;

float tmpY1 = vecSize.y * ur.y;

float tmpZ0 = vecSize.z * llf.z;

float tmpZ1 = vecSize.z * ur.z;

/*

calc start and end points with one additional sample

to avoid interpolating artifacts

*/

int x0 = static_cast<int>(floor(tmpX0));-1;

int x1 = static_cast<int>(floor(tmpX1));+1;

int y0 = static_cast<int>(floor(tmpY0));-1;

int y1 = static_cast<int>(floor(tmpY1));+1;

int z0 = static_cast<int>(floor(tmpZ0));-1;

int z1 = static_cast<int>(floor(tmpZ1));+1;

if(x0 < 0) { x0 = 0; }

if(x1 > static_cast<int>(floor(vecSize.x))) { x1 = static_cast<int>(floor(vecSize.x)); }

if(y0 < 0) { y0 = 0; }

if(y1 > static_cast<int>(floor(vecSize.y))) { y1 = static_cast<int>(floor(vecSize.y)); }

if(z0 < 0) { z0 = 0; }

if(z1 > static_cast<int>(floor(vecSize.z))) { z1 = static_cast<int>(floor(vecSize.z)); }

int xy = vecSize.x * vecSize.y;

/*

sample sub volume for min/max values

*/

for(int z = z0; z < z1; z++)

{

for(int y = y0; y < y1; y++)

{

for(int x = x0; x < x1; x++)

{

val = pvoxels[z * xy + y * static_cast<int>(vecSize.x) + x];

if(val < valMin) { valMin = val; }

if(val > valMax) { valMax = val; }

}

}

}

/*

calc coordinates to get correct index for

writing the min/max values at the correct position

of the octree data array

*/

float u = static_cast<int>((ur.x * sideLength) - 1.0f);

float v = static_cast<int>((ur.y * sideLength) - 1.0f);

float w = static_cast<int>((ur.z * sideLength) - 1.0f);

int sl = static_cast<int>(sideLength);

int suv = sl * sl;

int idxMin = w * suv * 3 + v * sl * 3 + u * 3;

int idxMax = w * suv * 3 + v * sl * 3 + u * 3 + 1;

octreeData[idxMin] = valMin;

octreeData[idxMax] = valMax;

}

creation of minmax data for the 2d texture

for(int max = 0; max < 256; max++)

{

for(int min = 0; min < 256; min++)

{

for(int i = min; i <= max; i++)

{

mm[max * 256 + min] = opacities[i] > 0 ? 255 : 0;

}

}

}

Then I update both textures (the 2d minmax tex and the 3d octree tex). As filter I set for both texture GL_NEAREST.

So far so good. You may have look at the two pictures.

Nothing suspicious for the moment, right ? :D

These pictures are taken without any octree. Just simple

volume raycasting

http://img834.imageshack.us/i/rcesl1.jpg/

http://img833.imageshack.us/i/rcesl2.png/

The following pictures show the result with octree texture.

So, in the picture below you can see a lot of artifacts cause by non visible boxes. But at this position there should be visible boxes because these areas contain data and according to the min max texture they are visible.

http://img715.imageshack.us/i/rcesl3.jpg/

In the picture below I draw nonvisible data blue and depth is

set to 1, (2 boxes on each axis).

Actually there shoudnlt be any non blue boxes. because each of the 8 big boxes contain

data with respect to the min max texture.

Here is some shader code to show you how I access the textures.

The function esl() is not complete. It actually contains calculations

to step icnrease tCurr by a value so that sampelPos skips empty regions

completely. But to keep it simple and for testing it only return's if there

is visible data or not.

int esl(vec3 samplePos, vec3 rayIncr, float stepWide, float tCurr)

{

vec3 mm = texture(texOctree, samplePos).rgb;

float val = texture(texMinMax, mm.rg).r;

if(val == 1.0)

return 1;

else

return 0;

}

samplePos = first + t * rayDirection;

if(esl(samplePos, rayIncr, tIncr, t) == 1)

{

sample volume

}

Hope there is someone who give me a good advice.

regards,

lobbel

I need some professionel help. For empty space leaping in

volume raycasting I store the octree in a 3d texture. To be

more precisely, I store only the max depth of the tree. For

instance a depth of 5 results in 8^5 (32768) boxes). So the

desired 3d texture (format: GL_RGB) is 2^5 32x32x32.

I create the octree on the cpu and every node (at max depth level) of the octree samples the volume with respect to it's current size. For example the box of a node has a size of

[0.5,0.0,0.0] - [0.75,0.25,0.25]. The volume may 256x256x256.

The space this node samples then is from

256*0.5, 256*0.0, 256*0.0 till 256*0.75,256*0.25,256*0.25

I sample one extra voxel at the borders of each node to avoid

interpolation errors (later on GPU side).

with the coordinates of the box I can calculate the indices

to write the min and max values of each node in an array that I later upload to th 3d texture. To create the 3d texture for storing the octree, I first create an array on the cpu side. for this example it's size is 32*32*32*3 because of depth 5. I take the size threetimes because I create a RGB texture. While creating the octree I write the min and the max value that has been calculated by sampling the node. And I only write those values at the lowest depth off course, here at depth 5. Additionally to the octree I create a min-max texture. This texture is as wide and as heights as there are values. That means, a 8bit volume data set has values from 0 to 255. so the texture size is 256*256. In this

texture I write for every min max pair whether the data is visible or not.

Before continuing with the problem, here is some code

node sampling

if(currDepth == maxDepth)

{

/*

max depth has been reached, incr. count

*/

childrenCount++;

/*

aabb boundaries

*/

glm::vec3 llf = aabb.getLowerLeft();

glm::vec3 ur = aabb.getUpperRight();

/*

vol size

*/

glm::vec3 vecSize = volume->getDimension();

/*

*/

valMin = 255.0f;

valMax = 0;

unsigned char val = 0;

unsigned char* pvoxels = static_cast<unsigned char*(Node::volume->getVoxels());

float tmpX0 = vecSize.x * llf.x;

float tmpX1 = vecSize.x * ur.x;

float tmpY0 = vecSize.y * llf.y;

float tmpY1 = vecSize.y * ur.y;

float tmpZ0 = vecSize.z * llf.z;

float tmpZ1 = vecSize.z * ur.z;

/*

calc start and end points with one additional sample

to avoid interpolating artifacts

*/

int x0 = static_cast<int>(floor(tmpX0));-1;

int x1 = static_cast<int>(floor(tmpX1));+1;

int y0 = static_cast<int>(floor(tmpY0));-1;

int y1 = static_cast<int>(floor(tmpY1));+1;

int z0 = static_cast<int>(floor(tmpZ0));-1;

int z1 = static_cast<int>(floor(tmpZ1));+1;

if(x0 < 0) { x0 = 0; }

if(x1 > static_cast<int>(floor(vecSize.x))) { x1 = static_cast<int>(floor(vecSize.x)); }

if(y0 < 0) { y0 = 0; }

if(y1 > static_cast<int>(floor(vecSize.y))) { y1 = static_cast<int>(floor(vecSize.y)); }

if(z0 < 0) { z0 = 0; }

if(z1 > static_cast<int>(floor(vecSize.z))) { z1 = static_cast<int>(floor(vecSize.z)); }

int xy = vecSize.x * vecSize.y;

/*

sample sub volume for min/max values

*/

for(int z = z0; z < z1; z++)

{

for(int y = y0; y < y1; y++)

{

for(int x = x0; x < x1; x++)

{

val = pvoxels[z * xy + y * static_cast<int>(vecSize.x) + x];

if(val < valMin) { valMin = val; }

if(val > valMax) { valMax = val; }

}

}

}

/*

calc coordinates to get correct index for

writing the min/max values at the correct position

of the octree data array

*/

float u = static_cast<int>((ur.x * sideLength) - 1.0f);

float v = static_cast<int>((ur.y * sideLength) - 1.0f);

float w = static_cast<int>((ur.z * sideLength) - 1.0f);

int sl = static_cast<int>(sideLength);

int suv = sl * sl;

int idxMin = w * suv * 3 + v * sl * 3 + u * 3;

int idxMax = w * suv * 3 + v * sl * 3 + u * 3 + 1;

octreeData[idxMin] = valMin;

octreeData[idxMax] = valMax;

}

creation of minmax data for the 2d texture

for(int max = 0; max < 256; max++)

{

for(int min = 0; min < 256; min++)

{

for(int i = min; i <= max; i++)

{

mm[max * 256 + min] = opacities[i] > 0 ? 255 : 0;

}

}

}

Then I update both textures (the 2d minmax tex and the 3d octree tex). As filter I set for both texture GL_NEAREST.

So far so good. You may have look at the two pictures.

Nothing suspicious for the moment, right ? :D

These pictures are taken without any octree. Just simple

volume raycasting

http://img834.imageshack.us/i/rcesl1.jpg/

http://img833.imageshack.us/i/rcesl2.png/

The following pictures show the result with octree texture.

So, in the picture below you can see a lot of artifacts cause by non visible boxes. But at this position there should be visible boxes because these areas contain data and according to the min max texture they are visible.

http://img715.imageshack.us/i/rcesl3.jpg/

In the picture below I draw nonvisible data blue and depth is

set to 1, (2 boxes on each axis).

Actually there shoudnlt be any non blue boxes. because each of the 8 big boxes contain

data with respect to the min max texture.

Here is some shader code to show you how I access the textures.

The function esl() is not complete. It actually contains calculations

to step icnrease tCurr by a value so that sampelPos skips empty regions

completely. But to keep it simple and for testing it only return's if there

is visible data or not.

int esl(vec3 samplePos, vec3 rayIncr, float stepWide, float tCurr)

{

vec3 mm = texture(texOctree, samplePos).rgb;

float val = texture(texMinMax, mm.rg).r;

if(val == 1.0)

return 1;

else

return 0;

}

samplePos = first + t * rayDirection;

if(esl(samplePos, rayIncr, tIncr, t) == 1)

{

sample volume

}

Hope there is someone who give me a good advice.

regards,

lobbel