2017-03-06 7 views
0

J'ai quelques problèmes concernant la marche de traçage/rayons sphère GLSL en utilisant les fonctions SDF:GLSL: Comment calculer une direction de rayon en utilisant la matrice de projection?

Mon programme principal (C++, en utilisant Vulkan) génère un quad écran et fournit le vertex shader avec un par-sommet inPosition. Le vertex shader a accès à la résolution de la fenêtre, à la matrice de projection et à la matrice de vue. La matrice de projection est générée avec glm::perspective(45.0, 1920/1080, 0.1, 100.0);.

Dans le vertex shader, j'essaie de calculer un rayon (position et direction en utilisant des coordonnées homogènes) provenant de l'origine à vec4(0.0, 0.0, 0.0, 1.0) à travers le plan image. Je suis confus où placer le plan de l'image et a choisi vec4(inPosition.xy, -5.0, 1.0) pour maintenant regarder le long de l'axe z négatif.

Le code suivant représente mon vertex shader:

#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); 
} 

J'utilise la matrice de projection pour transformer les directions à l'espace mondial et déformer le cube de l'unité à la résolution de la fenêtre. Cependant, dans mon fragment shader, les fonctions SDF et les intersections ne fonctionnent pas correctement. Je peux seulement voir une sphère si je mets les mêmes valeurs pour la distance et le rayon. Voir le fragment shader:

#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); 
    } 
} 

Ma question est: Comment j'appliquer la matrice de projection correctement? Et si elle est déjà appliquée correctement, pourquoi suis-je incapable de définir une position/un rayon différent pour la sphère SDF?

+1

Si vous utilisez Vulkan, vous devez le marquer de cette façon au lieu d'utiliser le tag OpenGL. – BDL

Répondre

1

Voici mon code pour calculer un rayon dans l'espace-monde à partir des coordonnées d'un fragment. Il utilise un ensemble de variables uniformes qui imitent l'ancien pipeline de fonctionnalités fixes (variables uniformes GLUP) dans le code suivant. La partie difficile est d'appliquer correctement la transformée de la fenêtre, et en tenant compte du fait que certaines variables sont dans [-1,1] et d'autres dans [0,1] (m'a fait me cogner la tête sur le mur).

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; 
} 

Un exemple shader fragment qui dessine des sphères raytracing en utilisant 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); 
    } 
} 

Le code complet est disponible dans ma bibliothèque GEOGRAM: http://alice.loria.fr/software/geogram/doc/html/index.html (src/lib/geogram_gfx/Glup/Shaders).