Это не решит все ваши проблемы, но это одна часть ответа.
В вашем коде есть базовое недопонимание барицентрических координат .Барицентрические координаты не являются координатами, но представляют собой 3 значения в диапазоне [0, 1].Представьте, что барицентрические координаты представляют собой 3 веса, которые взвешивают 3 угловые точки треугольника.
Сумма 3 шкал барицентрической координаты равна 1:
b.x + b.x + b.z == 1
Если треугольник равенопределяется тремя точками A
, B
и C
, тогда точка X
на треугольнике, которая определяется барицентрической координатой, может быть рассчитана по формуле:
X = A * b.x + B * b.y + C * b.z
Этоозначает, что вычисление пересечения distance
в hitScene
должно быть адаптировано следующим образом:
vec3 intersect = a.xyz * uvt.x + b.xyz * uvt.y + c.xyz * uvt.z;
Это расстояние до начала луча - это длина вектора от начала координат доточка пересечения в направлении луча.
В общем, я бы рассчитал это примерно так:
float z = dot(intersect - R_.orig, normalize(R_.dir));
Далее алгоритм вычисления барицентрических координат выглядит странно.Я очень уверен, что это должно быть uvt = vec3(t, u, v)
, а не uvt = vec3(u, v, t);
.Но это требует дальнейшего изучения.
Пересечение треугольника и луча можно рассчитать следующим образом:
луч определяется точкой R0
и направлением D
.
. Плоскость определяется треугольником с тремя точками PA
, PB
и PC
.
.Нормальный вектор плоскости можно рассчитать по перекрестному произведению двух ветвей треугольника:
N = normalize( cross(PC-PA, PB-PA)
Нормальное расстояние n
от точки R0
до плоскости:
n = | R0 - PA | * cos(alpha) = dot(PA - R0, N)
Из этого следует, что расстояние d
от точки пересечения X
до начала луча R0 равно:
d = n / cos(beta) = n / dot(D, N)
Точка пересечения X
составляет:
X = R0 + D * d = R0 + D * dot(PA - R0, N) / dot(D, N)
Применяя к вашему коду, я рекомендую использовать следующую функцию hitTriangle
:
- Возвращаемое значение равно
true
, если луч пересекает примитив треугольника в положительном направлении луча.(dir
) после происхождения луча (orig
). - Выход
N
- это вектор нормали плоскости, который определяется треугольником. - Выход
uvt
- это Барицентрические координаты треугольника. - Выход
x
- это точка пересечения треугольника. - А вывод
dist
- это расстояние от начала луча (orig
) до точки пересечения x
.
Обратите внимание, направление луча (dir
) должно быть нормализовано.Выходные значения имеют смысл только в том случае, если функция возвращает true
.
bool hitTriangle(
vec3 orig, vec3 dir, vec3 a, vec3 b, vec3 c,
out vec3 uvt, out vec3 N, out vec3 x, out float dist) {
float eps=1e-8;
vec3 ab = b - a;
vec3 ac = c - a;
N = normalize(cross(ab, ac));
dist = dot(a - orig, N) / dot(dir, N);
x = orig + dir * dist;
vec3 ax = x - a;
float d00 = dot(ab, ab);
float d01 = dot(ab, ac);
float d11 = dot(ac, ac);
float d20 = dot(ax, ab);
float d21 = dot(ax, ac);
float denom = d00 * d11 - d01 * d01; // determinant
// if the determinant is negative the triangle is backfacing
// if the determinant is close to 0, the ray misses the triangl
if ( denom <= eps )
return false;
uvt.y = (d11 * d20 - d01 * d21) / denom;
if ( uvt.y < 0.0 || uvt.y > 1.0 )
return false;
uvt.z = (d00 * d21 - d01 * d20) / denom;
if ( uvt.z < 0.0 || uvt.z > 1.0 )
return false;
uvt.x = 1.0 - uvt.y - uvt.z;
if ( uvt.x < 0.0 || uvt.x > 1.0 )
return false;
return true;
}
Используйте тест попадания (hitTriangle
) в функции hitScene
следующим образом:
bool hitScene(Ray R_, out vec3 hitPos, out vec3 normal, out Material material, Sphere lightSource){ // na thimithw na thesw to isShpere false stin trace synartisi
vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
float mindist = 1000.;
bool weHitSomething = false;
vec3 hitPos1 = vec3(0.),triangleNormal = vec3(0.,0.,0.), sphereNormal;
vec3 ray_dir = normalize(R_.dir);
//here we chck all the mesh if we hit a triangle if the mesh and we keep the closest hitpoint
for (int i = 0; i < vertsCount; i += 3) {
a = texelFetch(uMeshData, ivec2(i, 0), 0);
b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));
vec3 uvt;
vec3 intersect;
float z;
bool isHit = hitTriangle(R_.orig, ray_dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);
if (isHit)
{
if (z < mindist && z > 0.001)
{
hitPos1 = intersect;
mindist = z;
weHitSomething = true;
material.type = METAL;
material.albedo = vec3(.0, .8, .8);
normal = triangleNormal;
hitPos = hitPos1;
}
}
}
return weHitSomething;
}