You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何计算世界空间中3D点的像素坐标?GLM相关技术疑问

嘿,你的整体流程完全正确!先给你吃个定心丸😉

3D点转像素坐标的流程确认

从世界空间点p转换到屏幕像素坐标的标准流程就是:世界空间 → 相机空间(视图矩阵) → 透视投影(投影矩阵) → NDC坐标 → 屏幕像素坐标,你对这个流程的理解完全没问题。

关于世界→相机空间的转换矩阵(视图矩阵)

你提到的“相机到世界矩阵的逆矩阵”其实就是视图矩阵,它的作用就是把世界空间的点转换到以相机为原点的相机空间。

为什么需要相机上向量?

没错,计算视图矩阵确实需要上向量(Up Vector),它的核心作用是确定相机的姿态(倾斜角度)

  • 相机方向(cameraDir)只能告诉你相机“看向哪里”,但无法确定相机是“正立”还是“歪着”(比如看向正前方时,相机可以向左转90度,画面就会变成横向的)。
  • 上向量用来约束相机的“顶部朝向”,比如我们通常用世界空间的(0,1,0)作为上向量,确保相机的顶部和世界的y轴方向一致,避免画面倾斜。
  • 如果上向量和相机方向完全平行(比如相机朝上看,上向量也朝上),GLM的lookAt函数会自动调整上向量,避免生成奇异矩阵。

用GLM计算视图矩阵的代码实现

GLM已经封装了现成的函数glm::lookAt来生成视图矩阵,直接用它最方便,不需要手动推导矩阵:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

// 你的已知参数
glm::vec3 cameraPos = glm::vec3(0.f, 0.f, -5.f); // 相机在世界空间的位置
glm::vec3 cameraDir = glm::vec3(0.f, 0.f, 1.f);  // 相机看向的方向
glm::vec3 cameraUp = glm::vec3(0.f, 1.f, 0.f);   // 世界空间的上向量(通常用这个)

// 生成视图矩阵:世界空间 → 相机空间
glm::mat4 viewMatrix = glm::lookAt(
    cameraPos,                // 相机位置
    cameraPos + cameraDir,    // 相机看向的目标点(位置+方向)
    cameraUp                  // 相机的上向量
);

手动推导的逻辑(如果你想理解原理)

glm::lookAt的底层逻辑是构建相机的正交基:

  1. 计算前向向量forward = glm::normalize(cameraDir)
  2. 计算右向向量right = glm::normalize(glm::cross(forward, cameraUp))(注意叉乘顺序,确保右向方向正确)
  3. 修正上向量correctedUp = glm::normalize(glm::cross(right, forward))(避免原上向量和前向不垂直)
  4. 构建旋转矩阵(把世界空间的轴转到相机空间的轴),再加上平移矩阵(把相机原点移到世界原点,即平移-cameraPos),两者组合就是视图矩阵。

后续流程补充(帮你补全整个转换)

得到视图矩阵后,完整的转换步骤代码大概是这样:

// 1. 世界空间点转齐次坐标
glm::vec4 pWorld = glm::vec4(p, 1.f);

// 2. 转相机空间
glm::vec4 pView = viewMatrix * pWorld;

// 3. 生成透视投影矩阵(假设FOV=45度,宽高比=16:9,近裁剪面0.1,远裁剪面100)
glm::mat4 projMatrix = glm::perspective(glm::radians(45.f), 16.f/9.f, 0.1f, 100.f);

// 4. 转NDC坐标(标准化设备坐标)
glm::vec4 pNDC = projMatrix * pView;
pNDC /= pNDC.w; // 透视除法,得到[-1,1]范围的NDC

// 5. NDC转屏幕像素坐标(假设屏幕宽1920,高1080)
int screenWidth = 1920;
int screenHeight = 1080;
int pixelX = static_cast<int>((pNDC.x + 1.f) * 0.5f * screenWidth);
int pixelY = static_cast<int>((1.f - pNDC.y) * 0.5f * screenHeight); // 翻转y轴,因为屏幕y是从上到下

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

火山引擎 最新活动