glsl - 光线追踪立方体内的球体

标签 glsl trace raytracing

我正在尝试对立方体内的球体进行光线追踪。立方体简单地由 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 实际上不是必需的,但它给出了光线接触球体的一侧有多远的想法,这使它看起来很阴暗。 step(0, d) 函数用于查看是否有任何交点,max(0, d) 用于防止着色器在 sqrt(<0) 故障时暂停,两者都可以防止代码分支。

引用:我从 https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection 得到计算

编辑:这是问题的视频:Video

最佳答案

您的光线应该通过获取给定片段和相机位置之间的方向来计算。 (在 View 空间中,这将是原点。)顶点法线与它完全无关。

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

更好的方法是在顶点着色器中输出顶点的 View 空间位置。在片段着色器中,计算从原点到片段 View 空间位置的射线。然后,使用该射线执行射线相交测试。光栅化器将正确插入 View 空间位置。您也可以在片段着色器中自行计算,但硬件在这方面非常擅长,因此让它为您执行此操作是有意义的。

说了这么多,您当前实现的主要问题是使用顶点法线来计算光线。那是错误的。您只需要相机位置和片段位置。如果您仔细查看您的视频,您会发现在所有方面都绘制了相同的内容,无论相对于相机的位置如何。

对于一个简单的球体,您所需要的只是相机片段光线。计算从包含它的线到球体中心的距离。如果它小于球体的半径,那就是命中。

关于glsl - 光线追踪立方体内的球体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57133990/

相关文章:

opengl - 基于索引的纹理高度图(OpenGL/GLSL)

colors - 如何正确混合两个三角形的颜色并消除对角线涂抹

visual-studio - 默认情况下,如何按构建顺序对 Visual Studio 的构建输出进行排序?

glsl - Vulkan 采样器的 maxLod 会不会太高?

iphone - glDrawArrays 在每一帧上分配内存

c# - 如何调试/跟踪 System.Web.Services.Protocols.SoapHttpClientProtocol.invoke(来自 Windows 移动设备)?

java - 有没有办法跟踪已发布的 Android 应用程序的日志

graphics - 通过透明表面计算阴影

c++ - 在光线追踪器中实现软阴影

c++ - 带phong阴影和光线追踪的伪像