iam当前正在编写带有三角形相交的光线跟踪器。我已经将网格导出为.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 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;
}
之后,我得到类似的信息: 如您所见,我在反射,但是在球体内也看到了立方体。这是否意味着我无法通过射线三角形相交测试?或最小距离不正确?
问题的根源可能是什么?
答案 0 :(得分:1)
这不能解决您的所有问题,但这只是答案的一部分。
在您的代码中,对Barycentric coordinates有一个基本的误解。重心坐标不是坐标,但是它们是[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
与射线R0的原点的距离X
为:
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
是三角形的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;
}
在函数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;
}