GLSL:如何使用投影矩阵计算光线方向?

时间:2017-03-06 19:10:08

标签: c++ matrix glsl raycasting vulkan

我在使用SDF功能的GLSL中有关于球体追踪/射线行进的一些问题:

我的主程序(C ++,使用Vulkan)生成一个屏幕四边形,并为顶点着色器提供每顶点inPosition。顶点着色器可以访问窗口分辨率,投影矩阵和视图矩阵。使用glm::perspective(45.0, 1920/1080, 0.1, 100.0);生成投影矩阵。

在顶点着色器中,我尝试通过图像平面计算来自vec4(0.0, 0.0, 0.0, 1.0)的原点的光线(使用齐次坐标的位置和方向)。我很困惑放置图像平面的位置,现在选择vec4(inPosition.xy, -5.0, 1.0)来查看负z轴。

以下代码代表我的顶点着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

struct Ray
{
  vec4 pos;
  vec4 dir;
};

layout(binding = 0) uniform UniformBufferObject {
  vec3 res;
  mat4 projection;
  mat4 view;
} ubo;

layout(location = 0) in vec3 inPosition;

layout(location = 0) out vec3 iResolution;
layout(location = 1) out Ray iRay;

out gl_PerVertex {
  vec4 gl_Position;
};

void main() {
  fragCoord = vec2(
    ((inPosition.x+1)/2) * (ubo.res.x-1),
    ((inPosition.y+1)/2) * (ubo.res.y-1)
  );
  iResolution = ubo.res;
  gl_Position = vec4(inPosition, 1.0);
  vec4 direction = inverse(ubo.projection) * vec4(inPosition.xy, -5.0, 1.0);
  iRay.dir = direction;
  iRay.pos = vec4(direction.xy, 0.0, 1.0);
}

我使用投影矩阵将方向转换为世界空间,并将单位立方体扭曲到窗口分辨率。但是,在我的片段着色器中,SDF功能和交叉点无法正常工作。如果我为距离和半径设置相同的值,我只能看到一个球体。请参见片段着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

struct Ray
{
  vec4 pos;
  vec4 dir;
};

layout(location = 0) in vec3 iResolution;
layout(location = 1) in Ray iRay;

layout(location = 0) out vec4 outColor;

float sdfSphere(vec3 p, float r) 
{
  return length(p) - r;
}

bool intersect(Ray ray)
{
  for(int i = 0; i < 100; i++) {
    float hit = sdfSphere((ray.pos.xyz + vec3(0.0, 0.0, -11.0)), 11.0);
    ray.pos += hit * ray.dir;
    if (hit < 0.001) {
      return true;
    }
  }
  return false;
}

void main() 
{
  bool result = intersect(iRay);
  if(result == false) {
    outColor = vec4(0.0, 0.0, 0.0, 1.0);
  } else {
    outColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
}

我的问题是:如何正确应用投影矩阵?如果它已经正确应用,为什么我无法为SDF球体设置不同的位置/半径?

2 个答案:

答案 0 :(得分:2)

这是我的代码,用于从片段的坐标计算世界空间中的光线。它使用一组统一变量来模拟以下代码中的旧固定功能管道(GLUP统一变量)。棘手的部分是正确应用视口变换,并考虑到[-1,1]中的一些变量和[0,1]中的其他变量(让我把头撞到墙上)。

struct Ray {
    vec3 O; // Origin
    vec3 V; // Direction vector
};

// Notes: GLUP.viewport = [x0,y0,width,height]
// clip-space coordinates are in [-1,1] (not [0,1]) !

// Computes the ray that passes through the current fragment
// The ray is in world space.
Ray glup_primary_ray() {
    vec4 near = vec4(
    2.0 * ( (gl_FragCoord.x - GLUP.viewport[0]) / GLUP.viewport[2] - 0.5),
    2.0 * ( (gl_FragCoord.y - GLUP.viewport[1]) / GLUP.viewport[3] - 0.5),
        0.0,
        1.0
    );
    near = GLUP.inverse_modelviewprojection_matrix * near ;
    vec4 far = near + GLUP.inverse_modelviewprojection_matrix[2] ;
    near.xyz /= near.w ;
    far.xyz /= far.w ;
    return Ray(near.xyz, far.xyz-near.xyz) ;
}

// Updates fragment depth from a point in world space
void glup_update_depth(in vec3 M_world_space) {
    vec4 M_clip_space = GLUP.modelviewprojection_matrix * vec4(M_world_space,1.0);
    float z = 0.5*(1.0 + M_clip_space.z/M_clip_space.w);
    glup_FragDepth = (1.0-z)*gl_DepthRange.near + z*gl_DepthRange.far;
}

使用glup_primary_ray()绘制光线跟踪球体的示例片段着色器:

in vec3 C; // center in world space;
in float r;

void main(void) {
  Ray R = glup_primary_ray();
    vec3 M,N;

    if(
    glupIsEnabled(GLUP_CLIPPING) &&
    GLUP.clipping_mode == GLUP_CLIP_SLICE_CELLS
    ) {
    N = GLUP.world_clip_plane.xyz;
    float w = GLUP.world_clip_plane.w;
    float t = -(w + dot(N,R.O)) / dot(N,R.V);
    M = R.O + t*R.V;
    if(dot(M-C,M-C) > r*r) {
        discard;
    }
    } else {
    vec3 D = R.O-C;    
    float a = dot(R.V,R.V);
    float b = 2.0*dot(R.V,D);
    float c = dot(D,D)-r*r;
    float delta = b*b-4.0*a*c;

    if(delta < 0.0) {
        discard;
    }
    float t = (-b-sqrt(delta))/(2.0*a);
    M = R.O + t*R.V;
    N = M-C;
    //insert here code to compute the shading with N

    //update the depth buffer
    glup_update_depth(M);
    }   
}

我的GEOGRAM库中提供了完整的代码:http://alice.loria.fr/software/geogram/doc/html/index.html(src / lib / geogram_gfx / GLUP / shaders)。

答案 1 :(得分:-1)

我花了几天的时间来解决这个问题,因为我需要获得一个精确的解决方案,才能在VR中使用光线同步并具有双眼同步视图。 产生的有效解决方案是使用模型视图投影矩阵来反转出现在顶点着色器中的标准化设备坐标: Computing ray origin and direction from Model View Projection matrices for raymarching