C++ 在GLSL中优化光线跟踪着色器
我已经编写了一个基于体素化的光线跟踪器,它工作正常,但速度非常慢 当前光线跟踪器代码如下所示:C++ 在GLSL中优化光线跟踪着色器,c++,opengl,optimization,glsl,gpu,C++,Opengl,Optimization,Glsl,Gpu,我已经编写了一个基于体素化的光线跟踪器,它工作正常,但速度非常慢 当前光线跟踪器代码如下所示: #version 430 //normalized positon from (-1, -1) to (1, 1) in vec2 f_coord; out vec4 fragment_color; struct Voxel { vec4 position; vec4 normal; vec4 color; }; struct Node { //children
#version 430
//normalized positon from (-1, -1) to (1, 1)
in vec2 f_coord;
out vec4 fragment_color;
struct Voxel
{
vec4 position;
vec4 normal;
vec4 color;
};
struct Node
{
//children of the current node
int children[8];
};
layout(std430, binding = 0) buffer voxel_buffer
{
//last layer of the tree, the leafs
Voxel voxels[];
};
layout(std430, binding = 1) buffer buffer_index
{
uint index;
};
layout(std430, binding = 2) buffer tree_buffer
{
//tree structure
Node tree[];
};
layout(std430, binding = 3) buffer tree_index
{
uint t_index;
};
uniform vec3 camera_pos; //position of the camera
uniform float aspect_ratio; // aspect ratio of the window
uniform float cube_dim; //Dimenions of the voxelization cube
uniform int voxel_resolution; //Side length of the cube in voxels
#define EPSILON 0.01
// Detect whether a position is inside of the voxel with size size located at corner
bool inBoxBounds(vec3 corner, float size, vec3 position)
{
bool inside = true;
position-=corner;//coordinate of the position relative to the box coordinate system
//Test that all coordinates are inside the box, if any is outisde, the point is out the box
for(int i=0; i<3; i++)
{
inside = inside && (position[i] > -EPSILON);
inside = inside && (position[i] < size+EPSILON);
}
return inside;
}
//Get the distance to a box or infinity if the box cannot be hit
float boxIntersection(vec3 origin, vec3 dir, vec3 corner0, float size)
{
dir = normalize(dir);
vec3 corner1 = corner0 + vec3(size,size,size);//Oposite corner of the box
float coeffs[6];
//Calculate the intersaction coefficients with te 6 bonding planes
coeffs[0] = (corner0.x - origin.x)/(dir.x);
coeffs[1] = (corner0.y - origin.y)/(dir.y);
coeffs[2] = (corner0.z - origin.z)/(dir.z);
coeffs[3] = (corner1.x - origin.x)/(dir.x);
coeffs[4] = (corner1.y - origin.y)/(dir.y);
coeffs[5] = (corner1.z - origin.z)/(dir.z);
//by default the distance to the box is infinity
float t = 1.f/0.f;
for(uint i=0; i<6; i++){
//if the distance to a boxis negative, we set it to infinity as we cannot travel in the negative direction
coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i];
//The distance is the minumum of the previous calculated distance and the current distance
t = inBoxBounds(corner0,size,origin+dir*coeffs[i]) ? min(coeffs[i],t) : t;
}
return t;
}
#define MAX_TREE_HEIGHT 11
int nodes[MAX_TREE_HEIGHT];
int levels[MAX_TREE_HEIGHT];
vec3 positions[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()
{
int count = 0; //count the iterations of the algorithm
vec3 r = vec3(f_coord.x, f_coord.y, 1.f/tan(radians(40))); //direction of the ray
r.y/=aspect_ratio; //modify the direction based on the windows aspect ratio
vec3 dir = r;
r += vec3(0,0,-1.f/tan(radians(40))) + camera_pos; //put the ray at the camera position
fragment_color = vec4(0);
int max_level = int(log2(voxel_resolution));//height of the tree
push(0,0,vec3(-cube_dim));//set the stack
float tc = 1.f; //initial color value, to be decreased whenever a voxel is hit
//tree variables
int level=0;
int node=0;
vec3 corner;
do
{
//pop from stack
sp--;
node = nodes[sp];
level = levels[sp];
corner = positions[sp];
//set the size of the current voxel
float size = cube_dim / pow(2,level);
//set the corners of the children
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 coeffs[8];
for(int child=0; child<8; child++)
{
//Test non zero childs, zero childs are empty and thus should be discarded
coeffs[child] = tree[node].children[child]>0?
//Get the distance to your child if it's not empty or infinity if it's empty
boxIntersection(r, dir, corners[child], size) : 1.f/0.f;
}
int indices[8] = {0,1,2,3,4,5,6,7};
//sort the children from closest to farthest
for(uint i=0; i<8; i++)
{
for(uint j=i; j<8; j++)
{
if((coeffs[j] < coeffs[i]))
{
float swap = coeffs[i];
coeffs[i] = coeffs[j];
coeffs[j] = swap;
int iSwap = indices[i];
indices[i] = indices[j];
indices[j] = iSwap;
vec3 vSwap = corners[i];
corners[i] = corners[j];
corners[j] = vSwap;
}
}
}
//push to stack
for(uint i=7; i>=0; i--)
{
if(!isinf(coeffs[i]))
{
push(tree[node].children[indices[i]],
level+1, corners[i]);
}
}
count++;
}while(level < (max_level-1) && sp>0);
//set color
fragment_color = vec4(count)/100;
}
#版本430
//归一化位置从(-1,-1)到(1,1)
在vec2 Fu coord;
输出vec4片段颜色;
结构体素
{
vec4位;
vec4正常;
vec4颜色;
};
结构体类型
{
//当前节点的子节点
智力儿童[8];
};
布局(std430,绑定=0)缓冲区体素\u缓冲区
{
//树的最后一层,叶子
体素体素[];
};
布局(std430,binding=1)缓冲区索引
{
uint指数;
};
布局(std430,绑定=2)缓冲区树\u缓冲区
{
//树形结构
节点树[];
};
布局(std430,binding=3)缓冲区树索引
{
uint t_指数;
};
统一的vec3摄像机位置//摄像机的位置
均匀浮动纵横比;//窗口的纵横比
均匀浮点数//体素化立方体的维数
统一的int体素分辨率//体素中立方体的边长
#定义ε0.01
//检测一个位置是否位于位于角点大小的体素内部
bool内边界(vec3角点、浮点大小、vec3位置)
{
bool-inside=true;
位置-=角;//相对于长方体坐标系的位置坐标
//测试所有坐标是否在框内,如果有任何坐标在框外,则该点在框外
for(int i=0;i-ε);
内=内&(位置[i] 对于(uint i=0;i来说,似乎在八叉树的每一层中测试所有体素中的大多数体素与射线的交点,并在每一层中对它们进行排序(按一定距离)。
我提议另一种办法
如果光线与边界框相交(八叉树的0级),则光线在框的两个面上相交。或者在角点或边上相交,这些都是“角点”情况
查找三维光线平面交点的方法如下所示。可以通过测试点是否位于面的两个三角形之一的内部来查找交点是否在面(四边形)内,如
从相机获取最远的交点I0
。同时让r
成为朝向相机方向I0
的光线的单位向量
为I0
坐标查找最深的体素。这是离相机最远的体素。
现在,我们需要通过另一个面,在该体素中光线的退出坐标I0e
。虽然您可以再次对所有6个面进行计算,但如果您的体素是X、Y、X对齐的,并且您在与八叉树相同的坐标系中定义光线,则计算会简化很多
通过光线的r
单位向量对I0e
应用一点位移(例如,最小体素大小的1/1000):I1=I0e+r/1000
。找到这些I1
的体素。这是体素光线交点排序列表中的下一个体素
重复查找I1e
然后I2
然后I2e
然后I3
等,直到边界框退出。交叉体素列表已排序
使用八叉树可以根据存储信息的方式进行优化:所有可能的节点或刚刚使用的节点。包含数据的节点或只是“指针”到另一个包含数据的容器。这是另一个问题的问题。突出的第一件事是框交叉函数。请看一看更快的版本。由于所有轴上的框大小都是一致的,并且不需要超出正常值,因此可以得到更轻的版本。本质上,使用数学而不是蛮力a测试每个长方体平面的方法
此外,尽可能避免临时存储。例如,可以根据需要计算每个八叉树框的角点数组。当然,根据上述建议,这些角点数组将更改为框中心
由于节点
,级别
和位置
总是一起访问,请尝试将它们放在新的单个结构中,并作为单个单元访问
稍后再看…GPU上的线程执行可能是大规模并行的,但这并不意味着所有线程都彼此独立运行。线程组执行完全相同的指令,唯一的区别是输入数据。这意味着分支和循环不能使线程在执行过程中发生分歧因此,也不允许他们提前终止
您的示例显示了这方面最极端的情况:当一组线程中的所有工作都只与一个线程相关的可能性很高时
为了缓解这种情况,您应该尝试减少组中线程(或总线程)的执行长度差异(在您的情况下为迭代次数)。这可以通过设置每个着色器过程的迭代次数限制,并仅重新安排需要更多迭代的线程/像素来实现。为什么不将网格存储在3D纹理中并直接使用3D纹理坐标?不需要在空间细分周围进行任何堆栈和填充查看我的GLSL尝试从何而来这是我的光线跟踪器。也看看已经编码的那个。这里的问题是数据的可伸缩性和渐进复杂性