Почему ограничивающая рамка вокруг объекта выглядит странным образом?
Я создаю трассировщик лучей на основе OpenGL для полигональных моделей. Основная структура собирается отобразить результаты в четырехугольник из фрагментного шейдера. Для ускорения работы приложения используются BVH-деревья. Поскольку в GLSL нет рекурсии, я решил найти другой способ обхода ограничивающих рамок. Узлы bvh (включая блоки) и координаты примитивов отправляются фрагментному шейдеру в буфер хранения шейдера.
Я использую основную идею, описанную в: Обход BVH-дерева с нитями в шейдерах.
В приведенном выше решении используются ссылки, " которые используются для пропуска узлов, которые не нужно оценивать ". Есть ссылка попадания: к какому узлу перейти в случае попадания, и есть ссылка промаха: к какому узлу перейти в случае промаха.
На самом деле я не использую ссылки для перехода между полями, так как у меня есть полное двоичное дерево, которое упрощает навигацию между различными глубинами. Но основная концепция аналогична приведенной выше ссылке. Я храню узлы в порядке ширины.
К сожалению, когда программа работает, получается странный результат. Я могу видеть объект с частично трассировкой лучей и ограничивающую рамку. Ограничительная рамка имеет серый цвет, но этот цвет должен совпадать с цветом фона.
На рисунке ниже показано текущее состояние. Вы должны увидеть конус на сером фоне, но вместо него вы можете увидеть серую ограничительную рамку вокруг его объекта.
... и как это должно выглядеть (это версия без bvh-tree)
Вот мой фрагментный шейдер:
#version 460 core
layout(std140, binding=0) buffer primitives{
vec3 primitiveCoordinates[];
};
struct FlatBvhNode //I checked the data and it works fine.
{
// base aligment aligned offset
vec4 min; // 16 byte 0
vec4 max; // 16 byte 16
int order; // 4 byte 32
int isLeaf; // 4 byte 36
int createdEmpty;// 4 byte 40 //it is because of the complete binary tree
int leftOrRight; // 4 byte 44
vec4 indices[10];// 32 byte 48
};
layout(std430, binding=2) buffer TNodes
{
FlatBvhNode nodes[]; // the nodes of the tree in breadth-first order
};
out vec4 FragColor;
in vec3 p;
uniform vec3 wEye;
struct Light{
vec3 Le, La;
vec3 direction;
vec3 position;
};
uniform Light lights[];
struct Ray{
vec3 orig, dir;
};
struct Hit{
vec3 orig, dir, normal;
float t;
};
Hit rayTriangleIntersect(Ray ray, vec3 v0, vec3 v1, vec3 v2){
// This works well, so I don't include.
}
vec3 getCoordinatefromIndices(float index){
return primitiveCoordinates[int(index)];
}
Hit firstIntersect(Ray ray, int i){
Hit besthit;
besthit.t=-1;
for (int j=0;j<nodes[i].indices.length();j++){
vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x);
vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y);
vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z);
Hit hit=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);
if (hit.t==-1){ continue; }
if (hit.t>0 && (besthit.t>hit.t || besthit.t<0)){
besthit=hit;
}
}
return besthit;
}
bool rayIntersectWithBox(const vec4 boxMin, const vec4 boxMax, const Ray r) {
vec3 invdir = 1.0 / r.dir.xyz;
const vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
const vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;
const vec3 tmax = max(f, n);
const vec3 tmin = min(f, n);
const float t1 = min(tmax.x, min(tmax.y, tmax.z));
const float t0 = max(max(tmin.x, max(tmin.y, tmin.z)), 0.0f);
return t1 >= t0;
}
//This is where should be the problem.
//This is the method responsible for evaluating the bboxes.
//Instead of the links I can reach the childs with 2*i+1 or 2*i+2 and I can also get the parent
//with an inverse (int(ceil(i-2)/2))
Hit traverseBvhNode(Ray ray, FlatBvhNode node){
Hit result;
int next = 0;
for (int i = 0; i < nodes.length(); i++) {
if (i != next) { continue; }
bool hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);
if (nodes[i].createdEmpty==1){ hit=false;}
if (hit) {
if (nodes[i].isLeaf==1 && nodes[i].createdEmpty!=1){ return firstIntersect(ray, i);}
next = 2*i+1;
}
else if (!hit) {
if (nodes[i].leftOrRight==0){ next = i+1; }
else if (nodes[i].leftOrRight==1){ next = int(ceil(i-2)/2);
if (next==5){
result.t=-1;
return result;
}
}
}
}
return result;
}
Hit traverseBvhTree(Ray ray){
Hit hit;
if (rayIntersectWithBox(nodes[0].min, nodes[0].max, ray)){
return traverseBvhNode(ray, nodes[0]);
}
return hit;
}
vec3 trace(Ray ray){
vec3 color= vec3(0, 0, 0);
vec3 ka= vec3(0.135, 0.2225, 0.1575);
vec3 kd= vec3(0.54, 0.89, 0.63);
Hit hit=traverseBvhTree(ray);
if (hit.t==-1){ return lights[0].La; }
color=lights[0].La*ka;
// The below part is under contruction, but functions well.
Ray shadowRay;
shadowRay.orig=hit.orig+hit.normal*0.001f;
shadowRay.dir=lights[0].direction;
float cosTheta = dot(hit.normal, lights[0].direction)/(length(hit.normal)*length(lights[0].direction));
if (cosTheta > 0){
color+=lights[0].Le*cosTheta*kd;
float cosDelta=dot(hit.normal, normalize(-ray.dir + lights[0].direction));
if (cosDelta>0){
color=color+lights[0].Le*vec3(0.316228, 0.316228, 0.316228)*pow(0.1, cosDelta);
}
}
return color;
}
void main()
{
Ray ray;
ray.orig = wEye;
ray.dir = normalize(p - wEye);
FragColor = vec4(trace(ray), 1);
}
Любая помощь приветствуется.
Обновление 1:
Я воспользовался советом Rabbid76 и изменил rayIntersectWithBox
оператор возврата метода из return t1 >= t0;
к return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;
Как вы можете видеть на снимке, видимой ограничительной рамки нет, но с объектом все еще есть проблемы.
Обновление 2:
Я обновил traverseBvhNode
кода фрагментного шейдера. Я уверен, что алгоритм теперь обходит все узлы, поскольку я проверил,i==nodes.length()
и это правда.
К сожалению, результат в окне все тот же, что и в моем последнем обновлении.
Вот модификации traverseBvhNode
метод:
Для простоты помещаю содержимое
firstIntersection
способtraverseBvhNode
метод.Кроме того, я перемещаю
return besthit
линию до конца метода traverseBvhNode, потому что мне нужно пройти все узлы, прежде чем вернуться с лучшим пересечением.Я также изменил цикл for на цикл while, как с
while
Я могу изменить значениеi
в блоке, тогда как for не разрешает такого рода действия.Потому что есть некоторые
continue
инструкции также вtraverseBvhNode
метод, я поставилi++
инструкция в начале метода.
Вот обновленный код:
Hit traverseBvhNode(Ray ray, FlatBvhNode node){
Hit besthit;
besthit.t=-1;
int db=0;
Hit hitreal;
int next = 0;
int i=-1;
while(i<=nodes.length()) {
i++;
if(i >nodes.length()){ break;}
if (i != next) { continue; }
bool hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);
if (nodes[i].createdEmpty==1){ hit=false; }
if (hit) {
if (nodes[i].isLeaf==1 && nodes[i].createdEmpty!=1){
db++;
for (int j=0;j<nodes[i].indices.length();j++){
if (mod(nodes[i].indices[j].x, 1)==0 && mod(nodes[i].indices[j].y, 1)==0 && mod(nodes[i].indices[j].z, 1)==0){
vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x).xyz;
vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y).xyz;
vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z).xyz;
hitreal=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);
if (hitreal.t==-1){ continue; }
if (hitreal.t>0 && (besthit.t>hitreal.t || besthit.t<0)){
besthit=hitreal;
}
}
else{ continue;}
}
}
else{ next = 2*i+1;}
}
else {
if (nodes[i].leftOrRight==0){ next = i+1; }
else if (nodes[i].leftOrRight==1){
int id=int(ceil(i-2)/2);
FlatBvhNode parent=nodes[id];
while(parent.leftOrRight==1){
parent=nodes[int(ceil(parent.order-2)/2)];
}
next = parent.order+1;
i=next-1;
}
}
}
return besthit;
}
2 ответа
Таким образом, обход дерева ограничивающего объема истекал из-за нескольких ошибок, кроме того, я также обновил rayIntersectionWithBox
метод (Спасибо, что полетел на Rabbid76)
Итак, вот метод обхода bvh и rayIntersectWithBox
. Я также переместилfirstintersection
в метод обхода. Мое решение похоже на это: Threaded-Bvh, за исключением того, что я не использовал предопределенные ссылки в узлах. Я использовал алгоритм в ширину, чтобы добраться до родительского или дочернего узла.
bool rayIntersectWithBox(vec4 boxMin, vec4 boxMax, Ray r) {
vec3 invdir = 1.0 / r.dir.xyz;
vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;
vec3 tmax = f * sign(invdir);
vec3 tmin = n * sign(invdir);
return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;
}
Hit traverseBvhNode(Ray ray, FlatBvhNode node){
Hit besthit;
besthit.t=-1;
bool hit;
Hit hitreal;
int i=0;
while (i<=nodes.length()) {
if (nodes[i].isLeaf==1){
for (int j=0;j<nodes[i].indices.length();j++){
if (mod(nodes[i].indices[j].x, 1)==0 && mod(nodes[i].indices[j].y, 1)==0 && mod(nodes[i].indices[j].z, 1)==0){
vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x).xyz;
vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y).xyz;
vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z).xyz;
hitreal=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);
if (hitreal.t==-1){ continue; }
if (hitreal.t>0 && (besthit.t>hitreal.t || besthit.t<0)){
besthit=hitreal;
}
}
}
if (nodes[i].leftOrRight==0){
i=i+1;
continue;
}
else if (nodes[i].leftOrRight==1){
int id=int(ceil(i-2)/2);
FlatBvhNode parent=nodes[id];
while (parent.leftOrRight==1){
parent=nodes[int(ceil(parent.order-2)/2)];
if (parent.order==0){
return besthit;
}
}
i = parent.order+1;
continue;
}
}
hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);
if (hit) {
if (nodes[i].isLeaf==0){
i=2*i+1;
continue;
}
}
else {
if (nodes[i].order==0){
break;
}
if (nodes[i].leftOrRight==0) {
i=i+1;
continue;
}
node-nál vagyunk.
else if (nodes[i].leftOrRight==1){
FlatBvhNode parent=nodes[int(ceil(i-2)/2)];
while (parent.leftOrRight==1){
parent=nodes[int(ceil(parent.order-2)/2)];
if (parent.order==0){
if (parent.order==0){
return besthit;
}
}
}
i = parent.order+1;
continue;
}
}
}
return besthit;
}
Алгоритм в rayIntersectWithBox
кажется неправильным.
Луч пересекает прямоугольник, если минимум меньше максимального, для всех трех измерений отдельно. Кроме того, вы должны учитывать направление луча. Это означает, что вы должны оценить минимум и максимум в зависимости от знака компонента вектора направления (sign(invdir)
).
Я предлагаю:
bool rayIntersectWithBox(const vec4 boxMin, const vec4 boxMax, const Ray r) {
vec3 invdir = 1.0 / r.dir.xyz;
const vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
const vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;
const vec3 tmax = f * sign(invdir);
const vec3 tmin = n * sign(invdir);
return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;
}