在 GLSL C++ 中将光线与三角形相交
Intersect a ray with a triangle in GLSL C++
我正在尝试在片段着色器中使光线与三角形相交,如果它发生碰撞,我将在纹理中绘制一个黑点,如果不发生碰撞,我将绘制纹理颜色。但是没有效果,不知如何解决。
它是地面的着色器,有一个来自顶点的几何体的坐标,这个几何体将在地面着色器之后绘制。颜色也来自顶点着色器,光点是一个 vec3,在 space 中有一个点,我想创建一条从片段位置到光点的光线,看看它是否与几何碰撞我在代码中创建的。在我需要查看纹理中的交点是否为 alpha 之后,但这将是下一个问题,现在我需要查看地面几何体的阴影。
#version 330 core
#define INTERSECT_EPSILON 0.0001
out vec4 FragColor;
in vec2 TexCoord;
in vec3 geometryP;
in vec3 lampP;
in vec3 colorP;
in vec3 imagePos;
//texture samplers
uniform sampler2D groundTexture;
uniform sampler2D treeTexture;
struct Ray
{
vec3 Origin;
vec3 Direction;
};
float dot(vec3 firstPoint, vec3 secondPoint)
{
return (firstPoint.x * secondPoint.x + firstPoint.y * secondPoint.y + firstPoint.z * secondPoint.z);
}
vec3 cross(vec3 firstPoint, vec3 secondPoint)
{
vec3 crossResult;
crossResult.x = firstPoint.y*secondPoint.z - firstPoint.z*secondPoint.y;
crossResult.y = firstPoint.z*secondPoint.x - firstPoint.x*secondPoint.z;
crossResult.z = firstPoint.x*secondPoint.y - firstPoint.y*secondPoint.x;
return crossResult;
}
bool IntersectTriangle(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
float hit;
vec3 barycentricCoord;
vec3 triangleNormal;
vec3 e0 = p1 - p0;
vec3 e1 = p0 - p2;
triangleNormal = cross(e1 , e0);
float valueDot = 1.0 / dot( triangleNormal, ray.Direction );
vec3 e2 = ( valueDot ) * ( p0 - ray.Origin );
vec3 i = cross(ray.Direction , e2);
barycentricCoord.y = dot( i, e1 );
barycentricCoord.z = dot( i, e0 );
barycentricCoord.x = 1.0 - (barycentricCoord.z + barycentricCoord.y);
hit = dot( triangleNormal, e2 );
return (hit > INTERSECT_EPSILON) && (barycentricCoord.x > 0 && barycentricCoord.y >0 && barycentricCoord.z > 0);
}
void main()
{
vec3 firstPlane[3];
firstPlane[0] = geometryP + vec3(-0.2, -0.2, 0.0);
firstPlane[1] = geometryP + vec3(0.2, -0.2, 0.0);
firstPlane[2] = geometryP + vec3(0.2, 0.5, 0.0);
Ray ray1;
ray1.Origin = imagePos;
ray1.Direction = lampP;
bool intersect = IntersectTriangle(ray1, firstPlane[0], firstPlane[1], firstPlane[2]);
vec3 secondPlane[3];
secondPlane[0] = geometryP + vec3(0.2, -0.2, 0.0);
secondPlane[1] = geometryP + vec3(-0.2, 0.5, 0.0);
secondPlane[2] = geometryP + vec3(0.2, 0.5, 0.0);
if(!intersect)
{
intersect = IntersectTriangle(ray1, secondPlane[0], secondPlane[1], secondPlane[2]);
}
if(!intersect)
FragColor = mix(texture(groundTexture, TexCoord), texture(treeTexture, TexCoord), 0.2);
else
FragColor = vec4(colorP, 0.0);
}
有人可以帮我吗?
编辑:光线的结果,我没有测试与树纹理 alpha 的交集,树是几何着色器,地面是两个三角形,阴影是在地面着色器中用交集制作的计算:
首先请注意,dot
and cross
是内置的 glsl 函数。
编写一个 GLSL 函数来评估一个点是否在 3 维三角形内 space:
float PointInOrOn( vec3 P1, vec3 P2, vec3 A, vec3 B )
{
vec3 CP1 = cross(B - A, P1 - A)
vec3 CP2 = cross(B - A, P2 - A)
return step(0.0, dot(CP1, CP2));
}
bool PointInTriangle( vec3 px, vec3 p0, vec3 p1, vec3 p2 )
{
return
PointInOrOn(px, p0, p1, p2) *
PointInOrOn(px, p1, p2, p0) *
PointInOrOn(px, p2, p0, p1);
}
还有另一个与平面相交的函数(由 3 个点定义,由一条射线:
struct Ray
{
vec3 Origin;
vec3 Direction;
};
vec3 IntersectPlane(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
vec3 D = ray.Direction;
vec3 N = cross(p1-p0, p2-p0);
vec3 X = ray.Origin + D * dot(p0 - ray.Origin, N) / dot(D, N);
return X;
}
求交点并判断是否在三角形中:
bool IntersectTriangle(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
vec3 X = IntersectPlane(ray, p0, p1, p2);
return PointInTriangle(X, p0, p1, p2);
}
看下面的解释
射线与三角形基元的交点
光线由点 R0
和方向 D
定义。
该平面由具有三个点 PA
、PB
和 PC
.
的三角形定义
平面的法向量可以通过三角形2条边的叉积计算得到:
N = 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)
注意,N
和D
不需要归一化,因为D * dot(PA - R0, N) / dot(D, N)
等于normalze(D) * dot(PA - R0, normalze(N)) / dot(normalze(D), normalze(N))
。
要找出一个点是否在三角形内部,必须测试从角点到交点的线是否在连接到角点的 to 腿之间。三角形由点 A
、B
、C
定义,要测试的点是 P
:
bool PointInOrOn( P1, P2, A, B )
{
CP1 = cross( B - A, P1 - A )
CP2 = cross( B - A, P2 - A )
return dot( CP1, CP2 ) >= 0
}
bool PointInOrOnTriangle( P, A, B, C )
{
return PointInOrOn( P, A, B, C ) &&
PointInOrOn( P, B, C, A ) &&
PointInOrOn( P, C, A, B );
}
我正在尝试在片段着色器中使光线与三角形相交,如果它发生碰撞,我将在纹理中绘制一个黑点,如果不发生碰撞,我将绘制纹理颜色。但是没有效果,不知如何解决。
它是地面的着色器,有一个来自顶点的几何体的坐标,这个几何体将在地面着色器之后绘制。颜色也来自顶点着色器,光点是一个 vec3,在 space 中有一个点,我想创建一条从片段位置到光点的光线,看看它是否与几何碰撞我在代码中创建的。在我需要查看纹理中的交点是否为 alpha 之后,但这将是下一个问题,现在我需要查看地面几何体的阴影。
#version 330 core
#define INTERSECT_EPSILON 0.0001
out vec4 FragColor;
in vec2 TexCoord;
in vec3 geometryP;
in vec3 lampP;
in vec3 colorP;
in vec3 imagePos;
//texture samplers
uniform sampler2D groundTexture;
uniform sampler2D treeTexture;
struct Ray
{
vec3 Origin;
vec3 Direction;
};
float dot(vec3 firstPoint, vec3 secondPoint)
{
return (firstPoint.x * secondPoint.x + firstPoint.y * secondPoint.y + firstPoint.z * secondPoint.z);
}
vec3 cross(vec3 firstPoint, vec3 secondPoint)
{
vec3 crossResult;
crossResult.x = firstPoint.y*secondPoint.z - firstPoint.z*secondPoint.y;
crossResult.y = firstPoint.z*secondPoint.x - firstPoint.x*secondPoint.z;
crossResult.z = firstPoint.x*secondPoint.y - firstPoint.y*secondPoint.x;
return crossResult;
}
bool IntersectTriangle(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
float hit;
vec3 barycentricCoord;
vec3 triangleNormal;
vec3 e0 = p1 - p0;
vec3 e1 = p0 - p2;
triangleNormal = cross(e1 , e0);
float valueDot = 1.0 / dot( triangleNormal, ray.Direction );
vec3 e2 = ( valueDot ) * ( p0 - ray.Origin );
vec3 i = cross(ray.Direction , e2);
barycentricCoord.y = dot( i, e1 );
barycentricCoord.z = dot( i, e0 );
barycentricCoord.x = 1.0 - (barycentricCoord.z + barycentricCoord.y);
hit = dot( triangleNormal, e2 );
return (hit > INTERSECT_EPSILON) && (barycentricCoord.x > 0 && barycentricCoord.y >0 && barycentricCoord.z > 0);
}
void main()
{
vec3 firstPlane[3];
firstPlane[0] = geometryP + vec3(-0.2, -0.2, 0.0);
firstPlane[1] = geometryP + vec3(0.2, -0.2, 0.0);
firstPlane[2] = geometryP + vec3(0.2, 0.5, 0.0);
Ray ray1;
ray1.Origin = imagePos;
ray1.Direction = lampP;
bool intersect = IntersectTriangle(ray1, firstPlane[0], firstPlane[1], firstPlane[2]);
vec3 secondPlane[3];
secondPlane[0] = geometryP + vec3(0.2, -0.2, 0.0);
secondPlane[1] = geometryP + vec3(-0.2, 0.5, 0.0);
secondPlane[2] = geometryP + vec3(0.2, 0.5, 0.0);
if(!intersect)
{
intersect = IntersectTriangle(ray1, secondPlane[0], secondPlane[1], secondPlane[2]);
}
if(!intersect)
FragColor = mix(texture(groundTexture, TexCoord), texture(treeTexture, TexCoord), 0.2);
else
FragColor = vec4(colorP, 0.0);
}
有人可以帮我吗?
编辑:光线的结果,我没有测试与树纹理 alpha 的交集,树是几何着色器,地面是两个三角形,阴影是在地面着色器中用交集制作的计算:
首先请注意,dot
and cross
是内置的 glsl 函数。
编写一个 GLSL 函数来评估一个点是否在 3 维三角形内 space:
float PointInOrOn( vec3 P1, vec3 P2, vec3 A, vec3 B )
{
vec3 CP1 = cross(B - A, P1 - A)
vec3 CP2 = cross(B - A, P2 - A)
return step(0.0, dot(CP1, CP2));
}
bool PointInTriangle( vec3 px, vec3 p0, vec3 p1, vec3 p2 )
{
return
PointInOrOn(px, p0, p1, p2) *
PointInOrOn(px, p1, p2, p0) *
PointInOrOn(px, p2, p0, p1);
}
还有另一个与平面相交的函数(由 3 个点定义,由一条射线:
struct Ray
{
vec3 Origin;
vec3 Direction;
};
vec3 IntersectPlane(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
vec3 D = ray.Direction;
vec3 N = cross(p1-p0, p2-p0);
vec3 X = ray.Origin + D * dot(p0 - ray.Origin, N) / dot(D, N);
return X;
}
求交点并判断是否在三角形中:
bool IntersectTriangle(Ray ray, vec3 p0, vec3 p1, vec3 p2)
{
vec3 X = IntersectPlane(ray, p0, p1, p2);
return PointInTriangle(X, p0, p1, p2);
}
看下面的解释
射线与三角形基元的交点
光线由点 R0
和方向 D
定义。
该平面由具有三个点 PA
、PB
和 PC
.
平面的法向量可以通过三角形2条边的叉积计算得到:
N = 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)
注意,N
和D
不需要归一化,因为D * dot(PA - R0, N) / dot(D, N)
等于normalze(D) * dot(PA - R0, normalze(N)) / dot(normalze(D), normalze(N))
。
要找出一个点是否在三角形内部,必须测试从角点到交点的线是否在连接到角点的 to 腿之间。三角形由点 A
、B
、C
定义,要测试的点是 P
:
bool PointInOrOn( P1, P2, A, B )
{
CP1 = cross( B - A, P1 - A )
CP2 = cross( B - A, P2 - A )
return dot( CP1, CP2 ) >= 0
}
bool PointInOrOnTriangle( P, A, B, C )
{
return PointInOrOn( P, A, B, C ) &&
PointInOrOn( P, B, C, A ) &&
PointInOrOn( P, C, A, B );
}