没有法线的光线追踪阴影偏差
Ray traced shadow bias without normal
我正在遵循 Infinity Ward 的光线追踪阴影方法,他们从深度缓冲区重建世界位置并从中向光线投射光线。我发现使用这种方法,每像素 1 条光线,我得到了非常糟糕的阴影痤疮,这可能是因为数值错误。明显的解决方法是将世界位置沿法线方向移动少量,但我无法访问法线。我认为一旦我为每个像素拍摄多条光线,它可能会消失,但出于性能原因,我试图坚持使用 1 条光线。有任何选择,还是我注定无法访问法线?
void main() {
const vec2 pixelCenter = vec2(gl_LaunchIDNV.xy) + vec2(0.5);
const vec2 uv = pixelCenter/vec2(gl_LaunchSizeNV.xy);
uint rayFlags = gl_RayFlagsOpaqueNV | gl_RayFlagsSkipClosestHitShaderNV | gl_RayFlagsTerminateOnFirstHitNV;
float tMin = 0.001;
float tMax = 10000.0;
// sample the current depth
float depth = texture(depthTexture, uv).r;
// reconstruct world position of pixel
vec3 origin = reconstructPosition(uv, depth, pc.inverseViewProjection);
// ray direction is the inverse of the light direction
vec3 direction = normalize(-pc.light_direction.xyz);
// everything is considered in shadow until the miss shader is called
payload = vec3(0);
traceNV(AS, rayFlags, 0xFF, 0, 0, 0, origin, tMin, direction, tMax, 0);
// store either the original 0, or 1 if the miss shader executed
// if the miss shader executes it means it was able to 'reach' the light from the current pixel's position
imageStore(shadowTexture, ivec2(gl_LaunchIDNV.xy), vec4(payload, 1.0));
}
您可以通过查找相邻像素的位置(例如在 x 和 y 方向上偏移 1)并计算它们各自的方向向量与当前像素的叉积来重建法线的近似值。
例如:
Px = reconstructPositionFromPixel(pixelCenter + vec2(1.0, 0.0));
Py = reconstructPositionFromPixel(pixelCenter + vec2(0.0, 1.0));
Tx = normalize(Px - pixelCenter); // approximation of tangent
Ty = normalize(Py - pixelCenter); // approximation of bitangent
N = cross(Tx, Ty);
请注意,只要深度值平滑变化,这会产生非常好的结果。但是,如果存在深度边缘,即 2 个完全不同的深度值(例如,与相机的距离不同的不同对象),这将完全失败。这可以通过测量 Px
和 Py
的距离来检测,并根据启发式阈值简单地拒绝这个近似法线。
此外,此方法无法从法线贴图中恢复法线信息。
我正在遵循 Infinity Ward 的光线追踪阴影方法,他们从深度缓冲区重建世界位置并从中向光线投射光线。我发现使用这种方法,每像素 1 条光线,我得到了非常糟糕的阴影痤疮,这可能是因为数值错误。明显的解决方法是将世界位置沿法线方向移动少量,但我无法访问法线。我认为一旦我为每个像素拍摄多条光线,它可能会消失,但出于性能原因,我试图坚持使用 1 条光线。有任何选择,还是我注定无法访问法线?
void main() {
const vec2 pixelCenter = vec2(gl_LaunchIDNV.xy) + vec2(0.5);
const vec2 uv = pixelCenter/vec2(gl_LaunchSizeNV.xy);
uint rayFlags = gl_RayFlagsOpaqueNV | gl_RayFlagsSkipClosestHitShaderNV | gl_RayFlagsTerminateOnFirstHitNV;
float tMin = 0.001;
float tMax = 10000.0;
// sample the current depth
float depth = texture(depthTexture, uv).r;
// reconstruct world position of pixel
vec3 origin = reconstructPosition(uv, depth, pc.inverseViewProjection);
// ray direction is the inverse of the light direction
vec3 direction = normalize(-pc.light_direction.xyz);
// everything is considered in shadow until the miss shader is called
payload = vec3(0);
traceNV(AS, rayFlags, 0xFF, 0, 0, 0, origin, tMin, direction, tMax, 0);
// store either the original 0, or 1 if the miss shader executed
// if the miss shader executes it means it was able to 'reach' the light from the current pixel's position
imageStore(shadowTexture, ivec2(gl_LaunchIDNV.xy), vec4(payload, 1.0));
}
您可以通过查找相邻像素的位置(例如在 x 和 y 方向上偏移 1)并计算它们各自的方向向量与当前像素的叉积来重建法线的近似值。
例如:
Px = reconstructPositionFromPixel(pixelCenter + vec2(1.0, 0.0));
Py = reconstructPositionFromPixel(pixelCenter + vec2(0.0, 1.0));
Tx = normalize(Px - pixelCenter); // approximation of tangent
Ty = normalize(Py - pixelCenter); // approximation of bitangent
N = cross(Tx, Ty);
请注意,只要深度值平滑变化,这会产生非常好的结果。但是,如果存在深度边缘,即 2 个完全不同的深度值(例如,与相机的距离不同的不同对象),这将完全失败。这可以通过测量 Px
和 Py
的距离来检测,并根据启发式阈值简单地拒绝这个近似法线。
此外,此方法无法从法线贴图中恢复法线信息。