You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL射线投射器鼠标悬停网格高亮异常问题排查

问题排查与修正方案

你的核心问题出在射线方向和相机位置的空间转换不匹配,只有相机沿-Z轴(视图矩阵为单位矩阵)时,错误的变换结果刚好巧合符合预期,旋转相机后空间不匹配的问题就彻底暴露出来了。下面一步步拆解问题和修正方法:

1. 顶点着色器中的核心错误

先看你原来的顶点着色器代码:

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

这里存在两个严重的空间转换逻辑错误:

  • 射线方向(ray_dir):你在C++代码中计算的ray_wor是世界空间的射线方向,但你用模型矩阵M把它转到了模型空间,而顶点输出的position是视图空间的坐标(V*M*vertex_position)。两个向量不在同一个空间,点积计算出的夹角完全没有意义。
  • 相机位置(cam_pos):cam_pos本身是世界空间的相机位置,视图矩阵V已经能把它转换到视图空间,但你多乘了模型矩阵M——相机和模型是独立的,这会导致相机位置被错误地应用了模型的平移/旋转变换,完全偏离了实际位置。

2. 片段着色器的可选优化

你原来计算vDir的代码:

vec3 vDir = normalize(position.xyz - cameraPos);

根据视图空间的定义,相机本身就在视图空间的原点,所以这个向量其实就是相机指向顶点的方向,可以直接简化为normalize(position.xyz),省去相机位置的计算步骤,既高效又避免额外错误。

修正后的完整代码

顶点着色器

#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 ray_dir; // 世界空间射线方向
out vec3 rayDir_view; // 视图空间射线方向

void main () {
    // 把世界空间射线方向转换到视图空间(方向向量用w=0,避免平移影响)
    rayDir_view = vec3(V * vec4(ray_dir, 0.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 rayDir_view; // 视图空间射线方向

void main () {
    vec4 test = vec4(0, 0, 0, 0);
    
    // 视图空间中相机在原点,直接取顶点位置作为相机到顶点的方向
    vec3 camToVertex = normalize(position.xyz);
    // 计算方向夹角的余弦,clamp避免精度问题导致acos报错
    float cosAngle = dot(camToVertex, rayDir_view);
    cosAngle = clamp(cosAngle, -1.0, 1.0);
    float angle = degrees(acos(cosAngle));
    
    if(angle < 5.0) {
        test = vec4(1, 0, 0, 1);
    }
    
    // 注意:如果light_pos是世界空间,需要转换到视图空间再计算距离
    // vec3 light_pos_view = vec3(V * vec4(light_pos, 1.0));
    // float intensity = (1.0 / length(position.xyz - light_pos_view)) + 0.25;
    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轴时正常?因为此时视图矩阵V是单位矩阵,若模型矩阵M也是单位矩阵,错误的变换结果刚好和正确结果一致,暂时掩盖了问题。
  • 光照计算的潜在问题:你当前的light_pos如果是世界空间,直接和视图空间的position计算距离是错误的,需要像射线方向一样转换到视图空间,代码中已添加注释提示。

内容的提问来源于stack exchange,提问作者Calum McManus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:49:46