我正在 GLSL 中实现光线行进,以通过表面点可视化渲染球形对象。从某些角度查看物体时会出现此问题 - 表面位置计算变得不正确。
这是我的片段着色器:
#version 400
out vec4 FragColor;
in vec3 color;
in vec3 Normal;
in vec3 CurrentPos;
uniform int sunCount;
uniform int planetCount;
uniform vec4 lightColor;
uniform vec3 lightPos[64];
uniform vec3 viewPos;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform vec3 sphereOrigin[64];
uniform float sphereRadius[64];
// SDF for spheres
float SDF(in vec3 p, in vec3 c, float r) {
return length(p - c) - r;
}
vec3 calculateSurfacePos() {
// Get normalized device coordinates (NDC) from gl_FragCoord
vec2 uv_nds = (gl_FragCoord.xy / vec2(1366.0, 768.0)) * 2.0 - 1.0;
vec4 uv_clip = vec4(uv_nds, -1.0, 1.0);
vec4 uv_eye = inverse(projectionMatrix) * uv_clip;
uv_eye = vec4(uv_eye.x, uv_eye.y, -1.0, 0.0);
vec3 uv_wor = (inverse(viewMatrix) * uv_eye).xyz;
uv_wor = normalize(uv_wor);
highp float t = 0.0;
highp vec3 ro = viewPos;
highp vec3 rd = uv_wor;
for (int i = 0; i < 256; i++) {
highp vec3 p = ro + rd * t;
float dist = 1e10;
for (int j = 0; j < planetCount; j++) {
dist = min(dist, SDF(p, sphereOrigin[j], sphereRadius[j]));
}
if (dist < 0.001) return p;
if (t > 100.) break;
t += dist;
}
return vec3(0.0);
}
void main() {
vec3 surfacePos = calculateSurfacePos();
FragColor = vec4(surfacePos, 1.0);
}
我尝试过的:
使用 SDF 方法实现射线行进来查找与多个球体的交点。 正确地将设备坐标标准化为世界坐标。 使用循环沿着射线行进,直到找到交叉点或达到限制。 如果找到则返回交点,否则返回默认值。
当前问题:
计算的表面位置 (surfacePos) 未提供与球体的预期交点。相反,我得到了意想不到的颜色或根本没有交叉点。 我不确定我计算射线方向和行进逻辑的方法是否正确。
问题:
从剪辑空间转换到眼睛空间再到世界空间时,我的转换逻辑是否有错误? 我应该考虑光线行进的任何优化或最佳实践吗? 如何更有效地调试交叉点以确保我的 SDF 返回正确的距离?
所以我这样做的方式是将所有行星位置的列表发送到片段着色器,但我需要单独发送它们以及所有行星的列表,为什么?很简单,因为对于每个行星,我们需要检查它们的光线方向内是否有相交,所以最终我的理论是检查光线与 UV 坐标相交的位置,但后来我发现这是因为计算 t += dist看起来很容易受到浮点误差的影响。
vec3 computeShadow(in vec3 lightPos) {
float t = 0.0;
vec3 ro = lightPos;
vec3 rd = normalize(sphereOrigin - lightPos);
float maxDistToSphere = length(lightPos - sphereOrigin) - sphereRadius;
for (int i = 0; i < 100 && t < maxDistToSphere; i++) {
vec3 p = ro + t * rd; // Position along the ray
float minDist = 1e9;
// Check to each sphere (planet)
for (int k = 0; k < planetCount; k++) {
vec3 toPlanet = normalize(spheresOrigin[k] - lightPos);
// Check if the planet is roughly in the ray direction
if (dot(rd, toPlanet) > 0.99) { // Allowing a slight tolerance
float dist = SDF(p, spheresOrigin[k], spheresRadius[k]);
minDist = min(minDist, dist);
}
}
// If the ray is close enough to a surface, consider it in shadow
if (minDist < 0.01) return vec3(t / float(80) * 4.0); // Soft shadow effect
t += minDist; // Step forward along the ray
}
// If no shadow encountered, return light
return vec3(1.0);
}
maxDistToSphere 是太阳与球体中行星点之间的长度 在 if 条件下
if (dot(rd, toPlanet) > 0.99)
我们检查这些行星的方向,如果它们与我们当前正在检查的行星对齐,它们是否完全对齐,我们计算它们的距离,如果 t 达到高于 maxDistToSphere 的距离,则返回软阴影,因此没有行星处于同一方向