Проблема при рендеринге треугольной сетки с отражениями в webgl и opengles

В настоящее время я пишу трассировщик лучей с пересечением треугольника. Я экспортировал сетку в формате.raw, чтобы иметь координаты каждой вершины. И я заметил одну вещь: когда я поворачиваю камеру, это как бы показывает скрытую сетку.

я публикую свою функцию пересечения треугольников ниже в моем фрагменте шейдера

bool hitTriangle(vec3 orig,vec3 dir,vec3 a,vec3 b,vec3 c,out vec3 uvt,out vec3 triangleNormal){
   float eps=1e-8;
   vec3 ab=b-a;
   vec3 ac=c-a;

   triangleNormal = normalize(cross(ab,ac));
   vec3 n=cross(dir,ac);

   float det=dot(ab,n);
   // if the determinant is negative the triangle is backfacing
   // if the determinant is close to 0, the ray misses the triangl
   if(det<=eps){ return false;}

   vec3 ao=orig-a;
   float u=dot(ao,n)/det;
   if(u<0.0 || u>1.0){ return false;}

   vec3 e=cross(ao,ab);
   float v=dot(dir,e)/det;
   if(v<0.0||u+v>1.0){ return false;}

   float t= dot(ac,e)/det;
   uvt = vec3(u,v,t);
   return true;
}

под ним функция 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;


    //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;
        bool isHit = hitTriangle(R_.orig,R_.dir, a.xyz,b.xyz,c.xyz,uvt, triangleNormal);
        if (isHit) {
            vec3 intersect = R_.orig + R_.dir*uvt.z;
            float z = intersect.z;
            if (z>mindist) {
                hitPos1 = intersect;

                mindist = z;
                weHitSomething = true;
                material.type = METAL;
                material.albedo = vec3(.0, .8, .8);
                normal = triangleNormal;
                hitPos = hitPos1;
            }
        }      
    }      

    return weHitSomething;
}

и ниже находится функция Trace, которая проходит по трассе

//Trace is the main function of the max bounces
vec3 Trace(out Ray ray, Sphere lightSource){

    vec3 hitPos, normal;
    bool isShpere;
    Material material;
    vec3 color = vec3(1.);
    vec3 attenuation = vec3(1.);
    vec3 light = vec3(1.,1.,1.), shadow = vec3(1.,1.,1.);

    //this if for every ray to bounce 4 times.(hopefully)
    for(int i=0; i< MAX_BOUNCES; i++){

        // we check if we hit something
        if(hitScene(ray, hitPos, normal, material, lightSource)){

            //we calculate the new direction
            vec3 direction = normalize(reflect(ray.dir, normal));

            ray = Ray(hitPos, direction); 

            color *= material.albedo * attenuation*hitPos;
            attenuation *= material.albedo;    
        }

        else{
            color = attenuation;
        }
    }

    return color;
}

и после этого я получаю нечто подобное: Как вы видите, я получаю отражения, но я также вижу куб внутри сферы. Означает ли это, что я провалил тесты пересечения луч-треугольник? или минимальное расстояние не правильно??

Что может быть источником проблемы?

1 ответ

Решение

Это не решит все ваши проблемы, но это одна часть ответа.

В вашем коде есть базовое недопонимание барицентрических координат. Барицентрические координаты не являются координатами, но представляют собой 3 значения в диапазоне [0, 1]. Представьте барицентрические координаты в виде 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;
}
Другие вопросы по тегам