源码来源:vtkOpenGLCamera.cxx


🔧 构造函数与析构函数

构造函数

vtkOpenGLCamera::vtkOpenGLCamera()
{
  this->WCDCMatrix = vtkMatrix4x4::New();      // World to Device Coordinate
  this->WCVCMatrix = vtkMatrix4x4::New();      // World to View Coordinate
  this->NormalMatrix = vtkMatrix3x3::New();    // 法向量变换矩阵
  this->VCDCMatrix = vtkMatrix4x4::New();      // View to Device Coordinate
  this->LastRenderer = nullptr;
}

初始化四个关键矩阵:

矩阵名称 用途 维度
WCDCMatrix 世界坐标→设备坐标 4×4
WCVCMatrix 世界坐标→视图坐标(ModelView) 4×4
NormalMatrix 法向量变换(用于光照计算) 3×3
VCDCMatrix 视图坐标→设备坐标(投影矩阵) 4×4

析构函数

vtkOpenGLCamera::~vtkOpenGLCamera()
{
  this->WCDCMatrix->Delete();
  this->WCVCMatrix->Delete();
  this->NormalMatrix->Delete();
  this->VCDCMatrix->Delete();
}

释放所有动态分配的矩阵对象。


🎬 Render 方法

void vtkOpenGLCamera::Render(vtkRenderer* ren)

核心功能:设置 OpenGL 视口和剪裁区域

关键步骤:

1. 获取渲染窗口和状态

vtkOpenGLRenderWindow* win = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow());
vtkOpenGLState* ostate = win->GetState();

2. 检测立体渲染

this->Stereo = (ren->GetRenderWindow())->GetStereoRender();

3. 设置视口(Viewport)

ren->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft + 1);
ostate->vtkglViewport(lowerLeft[0], lowerLeft[1], usize, vsize);

获取平铺渲染的大小和原点,设置 OpenGL 视口范围。

4. 启用剪裁测试

ostate->vtkglEnable(GL_SCISSOR_TEST);

5. 设置剪裁区域

if (this->UseScissor)
{
  // 使用自定义剪裁矩形
  ostate->vtkglScissor(this->ScissorRect.GetX(), this->ScissorRect.GetY(),
    this->ScissorRect.GetWidth(), this->ScissorRect.GetHeight());
  this->UseScissor = false;
}
else
{
  // 使用默认视口大小
  ostate->vtkglScissor(lowerLeft[0], lowerLeft[1], usize, vsize);
}

6. 清空缓冲区

if ((ren->GetRenderWindow())->GetErase() && ren->GetErase())
{
  ren->Clear();  // 清除颜色和深度缓冲区
}

📐 UpdateViewport 方法

void vtkOpenGLCamera::UpdateViewport(vtkRenderer* ren)

功能: 更新视口设置(与 Render 方法类似,但不进行清空操作)

这个方法用于在渲染过程中动态调整视口,而不需要重新清空缓冲区。


🔑 GetKeyMatrices 方法(最重要)

void vtkOpenGLCamera::GetKeyMatrices(vtkRenderer* ren, vtkMatrix4x4*& wcvc, 
  vtkMatrix3x3*& normMat, vtkMatrix4x4*& vcdc, vtkMatrix4x4*& wcdc)

功能: 计算并缓存相机的四个关键变换矩阵

缓存检查机制

if (ren != this->LastRenderer || this->MTime > this->KeyMatrixTime ||
    ren->GetMTime() > this->KeyMatrixTime)
{
  // 重新计算矩阵
}

仅当以下条件满足时才重新计算:

  • 渲染器改变
  • 相机修改时间 > 矩阵计算时间
  • 渲染器修改时间 > 矩阵计算时间

矩阵计算过程

1. 获取 ModelView 矩阵(世界→视图)

this->WCVCMatrix->DeepCopy(this->GetModelViewTransformMatrix());

2. 提取并计算法向量矩阵

for (int i = 0; i < 3; ++i)
{
  for (int j = 0; j < 3; ++j)
  {
    this->NormalMatrix->SetElement(i, j, this->WCVCMatrix->GetElement(i, j));
  }
}
this->NormalMatrix->Invert();

提取 ModelView 矩阵的左上角 3×3 部分,然后求逆。这用于在着色器中正确变换法向量。

3. 转置 ModelView 矩阵

this->WCVCMatrix->Transpose();

为了与 OpenGL 列主序(column-major)存储格式兼容。

4. 获取投影矩阵(视图→设备)

this->VCDCMatrix->DeepCopy(
  this->GetProjectionTransformMatrix(ren->GetTiledAspectRatio(), -1, 1));
this->VCDCMatrix->Transpose();

获取投影矩阵并转置。参数 -1, 1 指定近、远裁剪平面。

5. 计算复合矩阵(世界→设备)

vtkMatrix4x4::Multiply4x4(this->WCVCMatrix, this->VCDCMatrix, this->WCDCMatrix);

WCDC = WCVC × VCDC \text{WCDC} = \text{WCVC} \times \text{VCDC} WCDC=WCVC×VCDC

这是最终的 MVP(Model-View-Projection)矩阵。

6. 更新时间戳

this->KeyMatrixTime.Modified();
this->LastRenderer = ren;

🎯 坐标系转换流程图

World Coordinates (世界坐标)
        ↓
   [WCVC Matrix]  ← ModelView 矩阵
        ↓
View Coordinates (视图坐标)
        ↓
   [VCDC Matrix]  ← Projection 矩阵
        ↓
Device Coordinates (设备坐标 -1 to 1)
        ↓
   [Viewport]     ← 屏幕像素坐标

💡 关键设计特点

特点 说明
矩阵缓存 避免重复计算,提高性能
时间戳检查 只在必要时更新矩阵
法向量矩阵 独立计算用于光照计算
列主序 通过转置适配 OpenGL 存储格式
平铺渲染支持 支持多视口/多窗口渲染
剪裁区域 灵活的视口裁剪机制

这个实现是现代 OpenGL 渲染管线中相机系统的标准做法。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐