GLSL在鼠标上突出显示网格

时间:2018-01-20 03:20:06

标签: c++ opengl glsl raycasting glm-math

我正在为我的opengl项目开发一个raycaster,我希望能够突出显示我正在盘旋的网格部分。

我使用以下方法从鼠标位置获取方向向量:

glm::vec3 Engine::Physics::RayCast::ViewToWorldSpace(glm::vec2 screenPos, 
float depth, glm::mat4 projection, glm::mat4 view, graphics::Window* window)
{
//Screen to Normalised Device Coordinates
float x = (2.0f * screenPos.x) / window->getWidth() - 1.0f;
float y = 1.0f - (2.0f * screenPos.y) / window->getHeight();
float z = 1.0f;
glm::vec3 ray_nds = glm::vec3(x, y, z);

//Normalised Device Coordinates to 4d Homogeneous Clip Coordinates
glm::vec4 ray_clip = glm::vec4(ray_nds.x, ray_nds.y, -1.0, 1.0);

//4d Homogeneous Clip Coordinates to Eye (Camera) Coordinates
glm::vec4 ray_eye = glm::inverse(projection) * ray_clip;
ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.0, 0.0);

//Eye (Camera) Coordinates to 4d World Coordinates
glm::vec3 ray_wor = glm::inverse(view) * ray_eye;
ray_wor = glm::normalize(ray_wor);
return ray_wor;
}

然后,我想检查是否指向网格的一部分,因此我将相机位置和此方向向量传递给这些着色器。

顶点

#version 410

layout (location = 0) in vec3 vertex_position;
layout (location = 2) in vec2 VertexUV;

uniform mat4 P;
uniform mat4 V = mat4(1.0);
uniform mat4 M = mat4(1.0);
out vec2 uv;
out vec4 position;

uniform vec3 cam_pos;
out vec3 cameraPos;
uniform vec3 ray_dir;
out vec3 rayDir;

void main () {

rayDir = vec3(M * vec4(ray_dir,1.0));
cameraPos = vec3(V * M * vec4(cam_pos,1.0)); 
gl_Position = P * V * M * vec4(vertex_position, 1);
position = V * M * vec4(vertex_position, 1.0);
uv = VertexUV;
}

分片

#version 410

out vec4 fragment_colour; // final colour of surface

in vec4 position;
in vec2 uv;

uniform vec3 light_pos;
uniform vec3 light_ambient;
uniform sampler2D texture2D;

in vec3 cameraPos;
in vec3 rayDir;

void main () {
vec4 test = vec4(0, 0, 0, 0);

vec3 vDir = normalize(position.xyz - cameraPos);
float cosAngle = dot(vDir, rayDir);
float angle = degrees(acos(cosAngle));

if(angle < 5)
{
    test = vec4(1, 0, 0, 1);
}


float intensity = (1.0 / length(position.xyz - light_pos))+0.25;
intensity = clamp(intensity, 0, 1);
vec4 ambient = vec4(light_ambient, 1);
fragment_colour = ((vec4(texture(texture2D, uv).rgb, 1.0) * intensity) * 
ambient)+test;
}

目前我可以在某些相机旋转时看到高光部分(相机围绕模型旋转),但是当相机直接面向-Z轴时,它只能真正跟随鼠标。知道我做错了吗?

这是一个与相机对齐的gif,但请记住,如果相机移动,它会中断。

Working at correct camera angle

1 个答案:

答案 0 :(得分:0)

在渲染中,场景的每个网格通常由模型矩阵,视图矩阵和投影矩阵进行变换。

  • 投影矩阵:
    投影矩阵描述了从场景的3D点到视口的2D点的映射。

  • 查看矩阵:
    视图矩阵描述了查看场景的方向和位置。视图矩阵从wolrd空间转换为视图(眼睛)空间。

  • 模型矩阵:
    模型矩阵定义场景中网格的位置,方向和相对大小。模型矩阵将顶点位置从网格转换为世界空间。


您决定使用视图空间坐标在片段着色器中执行操作。

在片段着色器中,顶点位置(position)位于视图空间中,因为您在顶点着色器中对其进行了变换。模型的顶点通过矩阵M转换为世界空间,并通过矩阵V从世界空间转换为视图空间:

position = V * M * vec4(vertex_position, 1.0);


但是,摄像机的位置坐标(cam_pos)是世界空间坐标。这意味着您必须仅通过视图矩阵V对其进行转换。

cameraPos = vec3( V * vec4(cam_pos, 1.0) ); 

请注意,此操作的结果始终为vec3(0.0, 0.0, 0.0),因为摄像机的视图空间位置是视图空间的原点。眼睛位置定义了视空间系统的原点(矩阵):

cameraPos = vec3( 0.0, 0.0, 0.0 ); 


此外,rayDir是方向向量而不是点。这意味着您无法通过4 * 4矩阵对其进行变换,因为您不希望将矩阵的平移部分应用于此矩阵。通常,在变换方向向量时,必须使用4 * 4矩阵左上角3 * 3的转置反转。但由于视图矩阵是正交矩阵,因此使用左上3 * 3就足够了。
rayDir是世界空间中的方向,因此您必须仅使用视图矩阵V对其进行变换。

rayDir = mat3(V) * ray_dir;

注意,如果省略了从视图空间到世界空间的光线转换,请在函数Engine::Physics::RayCast::ViewToWorldSpace中添加:

// glm::vec3 ray_wor = glm::inverse(view) * ray_eye; // skip this
ray_eye = glm::normalize(ray_eye);
return ray_eye;

然后你也可以省略顶点着色器中的向后变换:

rayDir = ray_dir;