Glsl 光线跟踪立方体中的球体

Glsl 光线跟踪立方体中的球体,glsl,trace,raytracing,Glsl,Trace,Raytracing,我试图用光线追踪立方体中的球体。立方体由12个带法线的三角形构成 立方体具有单位坐标和单位法线。所以在它的局部空间内(介于-1和1之间),应该有一个半径为0.5的球体 所以我认为我应该在顶点着色器中计算光线:光线原点是插值的顶点位置,光线方向是顶点法线(或者它的相反方向,但我认为这不重要)。插值应该完成剩下的工作 然后在片段着色器中,我应该计算光线与球体的交点,如果存在交点,则更改片段的颜色 在立方体的正面和背面,结果似乎是正确的,但在左侧、右侧、顶部和底部,结果似乎来自错误的角度。我应该总是看

我试图用光线追踪立方体中的球体。立方体由12个带法线的三角形构成

立方体具有单位坐标和单位法线。所以在它的局部空间内(介于-1和1之间),应该有一个半径为0.5的球体

所以我认为我应该在顶点着色器中计算光线:光线原点是插值的顶点位置,光线方向是顶点法线(或者它的相反方向,但我认为这不重要)。插值应该完成剩下的工作

然后在片段着色器中,我应该计算光线与球体的交点,如果存在交点,则更改片段的颜色

在立方体的正面和背面,结果似乎是正确的,但在左侧、右侧、顶部和底部,结果似乎来自错误的角度。我应该总是看到中间的球体,而不是在那些方面。

有人能告诉我我做错了什么吗

以下是着色器代码:

顶点着色器:

#version 400

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNor;

uniform mat4 uProj;
uniform mat4 uView;
uniform mat4 uModel;

out vec3 vRayPos;
out vec3 vRayDir;

void main(void)
{
  gl_Position = uProj * uView * uModel * vec4(aPos, 1);
  vRayPos = aPos;
  vRayDir = inverse(mat3(uModel)) * aNor;
}
片段着色器:

#version 400

in vec3 vRayPos;
in vec3 vRayDir;

out vec4 oFrag;

void main(void)
{
  const vec3 sphereCenter = vec3(0, 0, 0);
  const float sphereRadius = 0.5;

  vec3 rayPos = vRayPos;
  vec3 rayDir = normalize(vRayDir);
  float a = dot(rayDir, rayDir); // TODO: rayDir is a unit vector, so: a = 1.0?
  float b = 2 * dot(rayDir, (rayPos - sphereCenter));
  float c = dot(rayPos - sphereCenter, rayPos - sphereCenter) - sphereRadius * sphereRadius;
  float d = b * b - 4 * a * c;
  float t = min(-b + sqrt(max(0, d)) / 2, -b - sqrt(max(0, d)) / 2);

  vec3 color = (1.0 - step(0, d)) * vec3(0.554, 0.638, 0.447) + step(0, d) * abs(t) * vec3(0.800, 0.113, 0.053);

  oFrag = vec4(color, 1);
}

注意:因子t实际上不是必需的,但它给出了光线与球体接触的距离,这使它看起来有阴影。步长(0,d)函数用于查看是否存在任何交点,最大值(0,d)用于防止着色器在sqrt上停止(应通过获取给定片段和摄影机位置之间的方向来计算光线。(在视图空间中,这将是原点。)顶点法线与此完全无关

从技术上讲,可以在顶点着色器中计算光线,并将其作为插值传递给片段着色器。但是,这可能会给出错误的结果,因为输出将是线性的,这是不正确的

更好的方法是在顶点着色器中输出顶点的视图空间位置。在片段着色器中,计算从原点到片段视图空间位置的光线。然后,使用该光线执行光线相交测试。光栅化器将正确插值视图空间位置。您还可以计算在片段着色器中使用self,但是硬件在这方面非常好,所以让它为您这样做是有意义的

话虽如此,当前实现的主要问题是使用顶点法线来计算光线。这是错误的。您所需要的只是摄影机位置和片段位置。如果仔细查看视频,您将看到,无论相对于摄影机的位置如何,在所有边上绘制的都是相同的东西


对于一个简单的球体,您所需要的只是相机碎片光线。计算从包含该光线的线到球体中心的距离。如果它小于球体的半径,则为命中。这可能会引起您的兴趣。无论如何,单位球体的半径为
1.0
,而不是
0.5
,您可以使用这个。你有这个问题的截图吗?还有你的光线方向/位置描述听起来很像我看第一个链接我是怎么做的(四元覆盖整个屏幕,位置是顶点位置,但方向是位置焦点而不是法线!)我从来没有说过我想要一个单位球体。我知道它只是一个半球体,所以它不会触及立方体的边缘。我会更详细地查看您提供的链接,但快速查看似乎没有帮助。ray pos/dir不是通常的那种渲染大屏幕填充四边形的类型(我以前做过很多)。这是不同的:我在一个立方体中有一个场景,它是以通常的方式渲染的。立方体的每一面都是相机镜头本身,以与该面法线相反的方向观看。但我同意我的代码的这一部分可能有问题。我在我的作品中添加了一个视频!哦,这与我教的完全不同。无论如何,f或者你正在做的事情
vRayDir=inverse(mat3(uModel))*aNor;
看起来可疑。我会使用你的立方体中心,通过立方体一半大小+正常方向的焦距来转换它,以获得焦点,然后
vRayDir=vRayPos-focal_point
…我从来没有说过,但我明白这个想法可能来自何方。我说的是,我不能在3D上使用简单的广告牌粒子。但如果我读对了你的评论,我认为还是有可能的:保持广告牌不旋转并更改其视口角度。谢谢。顺便说一句,很酷的视频!我不认为OP适用于传统的光线跟踪器,在这种情况下,你的答案是相关的。它与6(或3)的效果不同摄像机不是一个…是的,我同意这就是为什么我一开始没有回答…只是评论/暗示可能是也可能不是…中间的球可能只是OP方关于效果行为的错误假设。你们都是对的。你们的讨论让我意识到我不知道我想要什么,我正在解决问题B这是我需要的。我开始这个项目的原因是因为我想有一些基于数学计算的3D粒子(我从一个球体开始,因为它很容易)。我滥用立方体来获得深度,因为当围绕Y轴旋转时,广告牌将不可见。我现在认为这只是我需要的普通光线跟踪,但我的屏幕四边形仅限于立方体的光栅化表面,并且对象始终与立方体相对。我将向您报告。@3Dave after clearification看起来像您在更正确的位置 :) +1