【问题标题】:Problem in rendering triangle mesh with reflections in webgl and opengles在 webgl 和 opengles 中使用反射渲染三角形网格的问题
【发布时间】:2019-02-10 14:07:59
【问题描述】:

我目前正在编写一个带有三角形相交的光线追踪器。我已将网格导出为 .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;
}

下面是遍历traces的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;
}

然后我得到类似的东西: 如您所见,我正在反射,但我在球体内也看到了立方体。这是否意味着我未通过射线三角形相交测试?还是最小距离不正确??

问题的根源是什么?

【问题讨论】:

  • 您能否提供(链接)到Minimal, Complete, and Verifiable example
  • @rabbid76 jsfiddle.net/dominuss/qLcp0rn2/5 这是一个最小的完整示例吗?如果不是我可以试试。问题是我修改了三角形相交测试,但现在我看不到反射。一点也不。不知道我的“相机”是否指向错误。
  • 更新,我的跟踪函数 100% 存在问题,因为如果我保持 for 循环与反弹,它会在 hitScene 中的 else 检查中达到 90%。

标签: javascript opengl-es glsl rendering webgl2


【解决方案1】:

这并不能解决你所有的问题,但它是答案的一部分。

在您的代码中存在对Barycentric coordinates 的基本误解。重心坐标不是坐标,但它们是 [0, 1] 范围内的 3 个值。将重心坐标想象为 3 个权重,对三角形的 3 个角点进行加权。

一个重心坐标的3个尺度之和为1:

b.x + b.x + b.z == 1

如果一个三角形由ABC三个点定义,那么由重心坐标定义的三角形上的点X可以通过以下公式计算:

X =  A * b.x + B * b.y + C * b.z

这意味着hitScene 中的交集distance 的计算必须进行如下调整:

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定义。
该平面由具有三个点PAPBPC 的三角形定义。

平面的法向量可以用三角形两条腿的叉积计算:

N  =  normalize( cross(PC-PA, PB-PA)

R0到平面的法线距离n为:

n  =  | R0 - PA | * cos(alpha)  =  dot(PA - R0, N)

由此可见,交点X到射线R0原点的距离d为:

d  =  n / cos(beta)  =  n / dot(D, N)

交点X是:

X  =  R0 + D * d  =  R0 + D * dot(PA - R0, N) / dot(D, N)

将 应用到您的代码中,我建议使用以下函数hitTriangle

  • 如果射线在射线的原点 (orig) 之后在射线的正方向 (dir) 与三角形图元相交,则返回值为 true
  • 输出N 是由三角形定义的平面的法向量。
  • 输出uvt 是三角形的Barycentric coordinates
  • 输出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;
}

在函数hitScene中使用命中测试(hitTriangle)如下:

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;
}

【讨论】:

  • github.com/Jojendersie/gpugi/blob/… 你觉得这个交集测试对吗?我已经测试了一些,我知道这对我来说最有效(我的意思是我保留的那个)......
  • 我还需要解释一下输出参数是什么,我不知道浮动命中是什么。
  • @ThodorisKoutsis 查看答案的扩展。
  • 尽快测试您的解决方案。
  • 你拯救了这一天。谢谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多