opengl 在同一场景中进行光线投射和栅格化时,世界坐标错误

q3qa4bjr  于 2023-03-22  发布在  其他
关注(0)|答案(1)|浏览(156)

我正在渲染两个立方体,边长都为1,以原点为中心(min coords. [-.5,-.5,-.5],max coords. [.5,.5,.5]),一个具有光栅化(顶点由顶点着色器设置),另一个使用光线投射(每个像素投射一条射线穿过一个立方体)。问题是当通过投影变换时,这两个像素似乎不共享世界空间坐标,视图、模型矩阵及其逆矩阵。
一个是通过将顶点坐标传递给GL顶点着色器,通过规范放置gl_Position来绘制:
gl_Position = proj * view * model * vec4(in_position, 1);
第二个是通过光线投射使用逆投影,视图矩阵。模型矩阵设置为单位。
要生成光线投射的光线开始位置,我们调用:vec4 ray_origin = inverse(view) * vec4(0,0,0,1);
要生成光线投射的光线方向,我们调用:

uniform vec2 resolution; // width, height of viewport in pixels

vec3 ray_dir(){

    // Normalized device coordinates, state after perspective divide
    vec2 ndc = (gl_FragCoord.xy / resolution) * 2.0 - 1.0;

    // From normalized screenspace, compute the clip space coordinates
    // Compute the clip space coordinates
    vec4 clip = vec4( ndc.xy, -1, 0);

    // Transform to eye space, using the inverse of the projection matrix
    vec4 eye = inverse(proj) * clip;
    eye = vec4(eye.xy, -1, 0);

    // Transform to world space, using the inverse of the view matrix
    vec4 world = inverse(view) * view_coords;

    return normalize(world.xyz);

}

最后,为了对立方体进行光线投射,我们使用一个简单的光线盒相交函数:

uniform vec3 start; // camera position in world space
vec2 cube_intersection() {
    vec3 dir = ray_dir();
    // no intersection means vec.x > vec.y
    float size = 0.5;
    vec3 t_min = (-vec3(size) - start) / dir;
    vec3 t_max = (+vec3(size) - start) / dir;
    vec3 t1 = min(t_min, t_max);
    vec3 t2 = max(t_min, t_max);    
    float t_near = max(max(t1.x, t1.y), t1.z);
    float t_far = min(min(t2.x, t2.y), t2.z);
    return vec2(max(t_near, 0), t_far);
}

其中,开始被提供为保持相机位置的统一,并且丢弃其中t_near > t_far
应该注意的是,视图和投影矩阵没有什么不寻常的,视图在所有熟悉的地方保持旋转和平移,投影矩阵是一个简单的透视矩阵。

结果应该是在世界空间中相同渲染的两个立方体,但实际情况并非如此!

这里,相机被设置为与原点的距离为1,x1c 0d1x
距离为0.5?它正好比

大2.0倍
摄影机距离为10时,它会更接近正确的大小,而

距离为100使其几乎完全适合栅格化框

  • 请注意,我在每个投影矩阵中显著调整了FOV *

如您所见,摄影机越靠近原点,光线投射球体的大小就越大,摄影机越远离原点,光线投射球体的大小就越小,逐渐接近正确的世界空间大小。
如果我将开始(摄像机)位置乘以(1.0 / length(start) + 1.0),那么突然之间我们就得到了立方体的正确世界空间大小!
在这里,它又是在距离1处:

uniform vec3 start; // camera position in world space
vec2 cube_intersection() {
    vec3 dir = ray_dir();
    // no intersection means vec.x > vec.y
    float size = 0.5;
    vec3 t_min = (-vec3(size) - start*(1.0 / length(start) + 1.0)) / dir; // **here**
    vec3 t_max = (+vec3(size) - start*(1.0 / length(start) + 1.0)) / dir; // **here**
    vec3 t1 = min(t_min, t_max);
    vec3 t2 = max(t_min, t_max);    
    float t_near = max(max(t1.x, t1.y), t1.z);
    float t_far = min(min(t2.x, t2.y), t2.z);
    return vec2(max(t_near, 0), t_far);
}

问题是,如果摄像机指向远离原点的任何数量,立方体开始平移2倍,如果它是由顶点着色器光栅化 * 而不是由片段着色器光线投射 *!
这里我们从摄像机位置(sqrt(3),sqrt(3),sqrt(3))

看点(0,0,0.5
我有一种感觉,这与w分量有关,它在光栅化过程中(在片段着色器之后)划分顶点位置,但我不确定问题到底是什么,也不知道如何在光线投射过程中补救。

光线投射和光栅化不能在世界空间中混合,这肯定不是一个根本的限制?

olmpazwi

olmpazwi1#

在2014年的Khronos post的帮助下解决了这个问题。
最后,我们的想法是计算裁剪空间平截体的近点和远点,分别是(X,Y,-1,1)和(X,Y,1,1),光线原点是近点,光线方向是远点和近点之间的归一化向量。
这个工作的关键是在顶点着色器中为全屏四边形的所有顶点计算这些变量,

in vec2 in_texcoord_0; // the uvs of the fullscreen quad!
uniform mat4 proj, view, model;
out vec4 v_nearpos, v_farpos;

void main(){ // VERTEX SHADER
    v_nearpos = inverse(proj*view*model) * vec4(in_texcoord_0.xy * 2 - 1, -1, 1) ;
    v_farpos = inverse(proj*view*model) * vec4(in_texcoord_0.xy * 2 - 1, +1, 1) ;
    // etc...
}

然后,在片段着色器中(插值后从顶点着色器!),除以它们的w分量以获得正确的倒数深度。

vec3 nearpos = v_nearpos.xyz / v_nearpos.w;
    vec3 farpos = v_farpos.xyz / v_farpos.w;

    vec3 ray_pos = nearpos;
    vec3 ray_dir = normalize(farpos - nearpos);

使用立方体相交函数产生正确的世界空间立方体:

vec2 cube_intersection(vec3 start, vec3 dir) {
    // no intersection means vec.x > vec.y
    float size = 0.5;

    vec3 t_min = (-vec3(size) - start) / dir;
    vec3 t_max = (+vec3(size) - start) / dir;

    vec3 t1 = min(t_min, t_max);
    vec3 t2 = max(t_min, t_max);
    
    float t_near = max(max(t1.x, t1.y), t1.z);
    float t_far = min(min(t2.x, t2.y), t2.z);

    return vec2(t_near, t_far);
}

希望这对其他人有帮助!如果对你有帮助,请投票!

我想这花了我一周的时间才弄明白的原因是,除非你真的很接近原点,否则效果并不明显!另外,每个光线投射着色器玩具代码都不需要与顶点着色器几何体互操作,所以它们只是使用我之前使用的代码的等价物!

相关问题