oct 树生成在最后一步出错
Posted
技术标签:
【中文标题】oct 树生成在最后一步出错【英文标题】:oct tree generation going wrong at the last step 【发布时间】:2018-06-08 19:56:59 【问题描述】:注意:此问题已从其原始形式进行了大幅修改
我正在尝试通过实现结合体素化的八叉树数据结构来创建对数光线追踪器,以实现快速光线追踪。
目前我遇到了光线碰撞检测问题。
预期的输出应该是带有法线贴图的体素化斯坦福龙。
目前的问题是某些区域是透明的:
完整的龙:
透明区域:
从这些图像中可以清楚地看出几何是正确的,但碰撞检查是错误的。
此过程涉及 2 个片段着色器:
体素化片段着色器:
#version 430
in vec3 f_pos;
in vec3 f_norm;
in vec2 f_uv;
out vec4 f_color;
struct Voxel
vec4 position;
vec4 normal;
vec4 color;
;
struct Node
int children[8];
;
layout(std430, binding = 0) buffer voxel_buffer
Voxel voxels[];
;
layout(std430, binding = 1) buffer buffer_index
uint index;
;
layout(std430, binding = 2) buffer tree_buffer
Node tree[];
;
layout(std430, binding = 3) buffer tree_index
uint t_index;
;
out vec4 fragment_color;
uniform int voxel_resolution;
uniform int cube_dim;
int getVIndex(vec3 position, int level)
float size = cube_dim / pow(2,level);
int bit2 = int(position.x > size);
int bit1 = int(position.y > size);
int bit0 = int(position.z > size);
return 4*bit2 + 2*bit1 + bit0;
void main()
uint m_index = atomicAdd(index, 1);
voxels[m_index].position = vec4(f_pos*cube_dim,1);
voxels[m_index].normal = vec4(f_norm,1);
voxels[m_index].color = vec4(f_norm,1);
int max_level = int(log2(voxel_resolution));
int node = 0;
vec3 corner = vec3(-cube_dim);
int child;
for(int level=0; level<max_level-1; level++)
float size = cube_dim / pow(2,level);
vec3 corners[] =
corner, corner+vec3(0,0,size),
corner+vec3(0,size,0), corner+vec3(0,size,size),
corner+vec3(size,0,0), corner+vec3(size,0,size),
corner+vec3(size,size,0), corner+vec3(size,size,size);
vec3 offsetPos = (vec3(voxels[m_index].position));
child = getVIndex(offsetPos-corner, level);
int mrun = 500;
while ((tree[node].children[child] <= 0) && (mrun > 0))
mrun--;
if( (atomicCompSwap( tree[node].children[child] , 0 , -1) == 0 ))
tree[node].children[child] = int(atomicAdd(t_index, 1));
if(mrun < 1)
discard;
if(level==max_level-2)
break;
node = tree[node].children[child];
corner = corners[child];
tree[node].children[child] = int(m_index);
我理解逻辑可能不太清楚,所以让我解释一下:
我们从 3D psoition voxels[m_index].position = vec4(f_pos*cube_dim,1);
开始,我们知道有一个立方体,尺寸为 (-cube_dim,-cube_dim,-cube_dim) 到 (cube_dim,cube_dim,cube_dim)
所以一个立方体,其对角线在原点相交,边长为 2*cube_dim。它被分成多个边长为 2*cube_dim/voxel_resolution 的小立方体。基本上这只是一个立方体细分 n 次以形成笛卡尔网格。
使用此坐标,我们从大立方体开始,将其细分为 8 个大小相等的子空间,并检测这些子空间中的哪些包含该坐标。
我们这样做,直到找到包含该位置的最小框。
光线追踪器
#version 430
in vec2 f_coord;
out vec4 fragment_color;
struct Voxel
vec4 position;
vec4 normal;
vec4 color;
;
struct Node
int children[8];
;
layout(std430, binding = 0) buffer voxel_buffer
Voxel voxels[];
;
layout(std430, binding = 1) buffer buffer_index
uint index;
;
layout(std430, binding = 2) buffer tree_buffer
Node tree[];
;
layout(std430, binding = 3) buffer tree_index
uint t_index;
;
uniform vec3 camera_pos;
uniform float aspect_ratio;
uniform float cube_dim;
uniform int voxel_resolution;
float planeIntersection(vec3 origin, vec3 ray, vec3 pNormal, vec3 pPoint)
pNormal = normalize(pNormal);
return (dot(pPoint,pNormal)-dot(pNormal,origin))/dot(ray,pNormal);
#define EPSILON 0.001
bool inBoxBounds(vec3 corner, float size, vec3 position)
bool inside = true;
position-=corner;
for(int i=0; i<3; i++)
inside = inside && (position[i] > -EPSILON);
inside = inside && (position[i] < size+EPSILON);
return inside;
float boxIntersection(vec3 origin, vec3 dir, vec3 corner0, float size)
dir = normalize(dir);
vec3 corner1 = corner0 + vec3(size,size,size);
vec3 normals[6] =
vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ;
float coeffs[6];
for(uint i=0; i<3; i++)
coeffs[i] = planeIntersection(origin, dir, normals[i], corner0);
for(uint i=3; i<6; i++)
coeffs[i] = planeIntersection(origin, dir, normals[i], corner1);
float t = 1.f/0.f;
for(uint i=0; i<6; i++)
coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i];
t = inBoxBounds(corner0,size,origin+dir*coeffs[i]) ? min(coeffs[i],t) : t;
return t;
void sort(float elements[8], int indices[8], vec3 vectors[8])
for(uint i=0; i<8; i++)
for(uint j=i; j<8; j++)
if(elements[j] < elements[i])
float swap = elements[i];
elements[i] = elements[j];
elements[j] = swap;
int iSwap = indices[i];
indices[i] = indices[j];
indices[j] = iSwap;
vec3 vSwap = vectors[i];
vectors[i] = vectors[j];
vectors[j] = vSwap;
int getVIndex(vec3 position, int level)
float size = cube_dim / pow(2,level);
int bit2 = int(position.x > size);
int bit1 = int(position.y > size);
int bit0 = int(position.z > size);
return 4*bit2 + 2*bit1 + bit0;
#define MAX_TREE_HEIGHT 11
int nodes[8*MAX_TREE_HEIGHT];
int levels[8*MAX_TREE_HEIGHT];
vec3 positions[8*MAX_TREE_HEIGHT];
int sp=0;
void push(int node, int level, vec3 corner)
nodes[sp] = node;
levels[sp] = level;
positions[sp] = corner;
sp++;
void main()
vec3 r = vec3(f_coord.x, f_coord.y, 1.f/tan(radians(40)));
r.y/=aspect_ratio;
vec3 dir = r;
r += vec3(0,0,-1.f/tan(radians(40))) + camera_pos;
fragment_color = vec4(0);
//int level = 0;
int max_level = int(log2(voxel_resolution));
push(0,0,vec3(-cube_dim));
float tc = 1.f;
int level=0;
int node=0;
do
sp--;
node = nodes[sp];
level = levels[sp];
vec3 corner = positions[sp];
float size = cube_dim / pow(2,level);
vec3 corners[] =
corner, corner+vec3(0,0,size),
corner+vec3(0, size,0), corner+vec3(0,size,size),
corner+vec3(size,0,0), corner+vec3(size,0,size),
corner+vec3(size,size,0), corner+vec3(size,size,size);
float t = boxIntersection(r, dir, corner, size*2);
if(!isinf(t))
tc *= 0.9f;
float coeffs[8];
for(int child=0; child<8; child++)
if(tree[node].children[child]>0)
coeffs[child] = boxIntersection(r, dir, corners[child], size);
else
coeffs[child] = 1.f/0.f;
int indices[8] = 0,1,2,3,4,5,6,7;
sort(coeffs, indices, corners);
for(uint i=7; i>=0; i--)
if(!isinf(coeffs[i]))
push(tree[node].children[indices[i]],
level+1, corners[i]);
while(level < (max_level-1) && sp>0);
if(level==max_level-1)
fragment_color = abs(voxels[node].normal);
else
fragment_color=vec4(tc);
在这里,我们从最大的立方体开始,测试与每组 8 个子对象的交集(细分立方体产生的 8 个立方体)。每次成功检测到碰撞时,我们都会沿着树向下移动,直到到达描述实际几何图形的最低级别,然后根据该级别为场景着色。
调试和问题
重要的部分是有2个缓冲区,一个用于存储除叶子之外的树,一个用于存储叶子。
所以在体素化和光线追踪中,最后一层需要区别对待。
我注意到的关于透明度的问题如下:
它只发生在与笛卡尔网格对齐的平面上
当光线向负方向移动时(向下 或左侧)。 (至少这是我的印象,但不是 100% 一定)
我不确定自己做错了什么。
编辑:
原来的问题似乎已经修复,但是光线追踪器仍然存在错误。我已经编辑了问题以反映问题的当前状态。
【问题讨论】:
如果您投反对票,请告诉我问题所在,以便我可以相应地编辑我的帖子 在 0 级,您的子立方体的大小是cube_dim
的一半,所以计算应该是:float size = cube_dim / pow(2,level+1);
?
发生了变化,变量名称目前有点误导。立方体有尺寸(-cube_dim,cube dim)(负到正),这意味着边长为 cube_dim 的立方体实际上是初始立方体大小的一半(我为令人困惑的变量名称道歉)
@samgak,也请参考问题的最新变化,因为问题现在完全不同了
coeffs[child] = 1.f/0.f
是 inf
。因此,sort
函数可能会因为无法处理 inf
s 而失败。
【参考方案1】:
这个错误来自于上面提到的 cmets 中的某个人的排序功能,尽管原因不同。
发生的事情是,我认为排序函数会修改传递给它的数组,但它似乎是在复制数据,所以它没有返回任何东西。
换句话说:
void sort(float elements[8], int indices[8], vec3 vectors[8])
for(uint i=0; i<8; i++)
for(uint j=i; j<8; j++)
if((elements[j] < elements[i]))
float swap = elements[i];
elements[i] = elements[j];
elements[j] = swap;
int iSwap = indices[i];
indices[i] = indices[j];
indices[j] = iSwap;
vec3 vSwap = vectors[i];
vectors[i] = vectors[j];
vectors[j] = vSwap;
不会在元素、索引和向量内返回正确的值,因此调用此函数只会浪费计算周期。
【讨论】:
然后在每个参数类型前加上inout
:void sort(inout float elements[8], inout int indices[8], inout vec3 vectors[8])
以上是关于oct 树生成在最后一步出错的主要内容,如果未能解决你的问题,请参考以下文章
用VS2008在Form1.cs设计时 DataGridView添加项目数据源最后一步老出错 求
关于“horizon view,在配置Composer出错问题”解决方法