【问题标题】:Intersect a ray with a triangle in GLSL C++在 GLSL C++ 中将射线与三角形相交
【发布时间】:2020-04-03 01:49:26
【问题描述】:

我正在尝试在片段着色器中与三角形进行射线相交,如果它碰撞我将在纹理中绘制一个黑点,如果不碰撞我将绘制纹理颜色。但它没有效果,我不知道如何解决它。

它是地面的着色器,具有来自顶点的几何图形的坐标,该几何图形将在地面着色器之后绘制。颜色也来自顶点着色器,并且光点是一个在空间中有一个点的 vec3,我想创建一条从片段位置到光点的射线,看看它是否与我创建的几何体相碰撞在代码中。在我需要查看纹理中的交点是否为 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 的交集,树是几何着色器,地面是两个三角形,阴影是在地面着色器中通过交集计算产生的:

【问题讨论】:

  • 在具有数组中实际点的 PC 上运行此代码可能更容易。也更容易调试。
  • 明白,我会尝试创建一个程序,看看它是否有效,谢谢你的想法。我需要它到接下来的两天.....

标签: c++ opengl glsl shader fragment-shader


【解决方案1】:

首先请注意,dotcross 是内置的 glsl 函数。

编写一个 GLSL 函数来评估一个点是否在 3 维空间中的三角形内:

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

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

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)

注意,没有必要对ND进行归一化,因为D * dot(PA - R0, N) / dot(D, N)等于normalze(D) * dot(PA - R0, normalze(N)) / dot(normalze(D), normalze(N))


要确定一个点是否在三角形内,必须测试从角点到交点的线是否在连接到角点的 to 腿之间。三角形由点ABC定义,要测试的点是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 );
} 

【讨论】:

  • 相交有效,我现在面临如何在屏幕上绘制这些黑色像素的问题,因为几何平面即将出现在屏幕上。但这是另一个问题。在这里,我尝试绘制碎片,或纹理,或黑色像素。 if(!intersect) FragColor = texture(groundTexture, TexCoord); else FragColor = vec4( 0.0, 0.0, 0.0, 1.0);
  • 不,我的问题是如何获取地面碎片坐标,你知道,我有地面四边形和几何四边形,我需要知道如何获取地面的每个点并进行测试反对几何作为射线原点。
  • @InfJ 我不确定我是否理解你的问题。你想知道ray.Origin到三角形交点的距离吗?
  • 我有两个四边形,一个作为地面投影,另一个是带有树纹理的几何着色器,我在 3D 空间有一个点,将是我的光线的方向,但我不知道如何获取地面的片段坐标来投射我的光线,在我的地面片段着色器中,我有方向坐标、几何四边形矢量坐标,但我的原点需要是投影矩阵坐标中的片段。在我的顶点着色器中,我正在传递这个位置gl_Position = projection * view * model * vec4(aPos, 1.0); imagePos = gl_Position.xyz;
  • 我发现了。我将地面位置放置在世界位置,但是空间和树几何中的点我使用了凸轮位置,它永远不会起作用。谢谢,现在它就像一个魅力!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-21
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
相关资源
最近更新 更多