鼠标悬停时 GLSL 高亮网格
GLSL highlight mesh on mouse over
我正在为我的 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
在渲染中,场景的每个网格通常由模型矩阵、视图矩阵和投影矩阵进行变换。
投影矩阵:
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。
查看矩阵:
视图矩阵描述了观察场景的方向和位置。视图矩阵从 worrd space 转换为视图(眼睛)space.
模型矩阵:
模型矩阵定义场景中网格的位置、方向和相对大小。模型矩阵将顶点位置从网格变换到世界 space.
您决定在片段着色器中使用视图 space 坐标进行操作。
在片段着色器中,顶点位置 (position
) 在视图中 space,因为您在顶点着色器中对其进行了变换。模型的顶点由矩阵M
转换为世界space,由矩阵V
从世界space转换为视图space:
position = V * M * vec4(vertex_position, 1.0);
但是,相机的位置坐标 (cam_pos
) 是世界坐标 space。这意味着您必须仅通过视图矩阵 V
对其进行变换。
cameraPos = vec3( V * vec4(cam_pos, 1.0) );
注意,此操作的结果始终是 vec3(0.0, 0.0, 0.0)
,因为相机的视图 space 位置是视图 space 的原点。眼睛位置定义了视图的原点space系统(矩阵):
cameraPos = vec3( 0.0, 0.0, 0.0 );
此外,rayDir
是一个方向向量而不是一个点。这意味着您不能通过 4*4 矩阵对其进行变换,因为您不想将矩阵的平移部分应用于它。通常,当您变换方向向量时,您必须使用 4*4 矩阵左上角 3*3 的转置逆。但是由于视图矩阵是正交矩阵,所以使用左上角的 3*3 就足够了。
rayDir
是世界 space 中的一个方向,所以你必须通过视图矩阵 V
对其进行变换。
rayDir = mat3(V) * ray_dir;
注意,如果在函数Engine::Physics::RayCast::ViewToWorldSpace
:
中省略了从视图space到世界space的光线变换
// glm::vec3 ray_wor = glm::inverse(view) * ray_eye; // skip this
ray_eye = glm::normalize(ray_eye);
return ray_eye;
那么你也可以省略顶点着色器中的向后变换:
rayDir = ray_dir;
我正在为我的 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
在渲染中,场景的每个网格通常由模型矩阵、视图矩阵和投影矩阵进行变换。
投影矩阵:
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。查看矩阵:
视图矩阵描述了观察场景的方向和位置。视图矩阵从 worrd space 转换为视图(眼睛)space.模型矩阵:
模型矩阵定义场景中网格的位置、方向和相对大小。模型矩阵将顶点位置从网格变换到世界 space.
您决定在片段着色器中使用视图 space 坐标进行操作。
在片段着色器中,顶点位置 (position
) 在视图中 space,因为您在顶点着色器中对其进行了变换。模型的顶点由矩阵M
转换为世界space,由矩阵V
从世界space转换为视图space:
position = V * M * vec4(vertex_position, 1.0);
但是,相机的位置坐标 (cam_pos
) 是世界坐标 space。这意味着您必须仅通过视图矩阵 V
对其进行变换。
cameraPos = vec3( V * vec4(cam_pos, 1.0) );
注意,此操作的结果始终是 vec3(0.0, 0.0, 0.0)
,因为相机的视图 space 位置是视图 space 的原点。眼睛位置定义了视图的原点space系统(矩阵):
cameraPos = vec3( 0.0, 0.0, 0.0 );
此外,rayDir
是一个方向向量而不是一个点。这意味着您不能通过 4*4 矩阵对其进行变换,因为您不想将矩阵的平移部分应用于它。通常,当您变换方向向量时,您必须使用 4*4 矩阵左上角 3*3 的转置逆。但是由于视图矩阵是正交矩阵,所以使用左上角的 3*3 就足够了。
rayDir
是世界 space 中的一个方向,所以你必须通过视图矩阵 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;