Skip to content

vtkOpenGLCamera源码详解

源码来源:vtkOpenGLCamera.cxx


构造函数与析构函数

构造函数

cpp
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

析构函数

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

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


Render 方法

cpp
void vtkOpenGLCamera::Render(vtkRenderer* ren)

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

关键步骤:

1. 获取渲染窗口和状态

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

2. 检测立体渲染

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

3. 设置视口(Viewport)

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

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

4. 启用剪裁测试

text
ostate->vtkglEnable(GL_SCISSOR_TEST);

5. 设置剪裁区域

text
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. 清空缓冲区

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

UpdateViewport 方法

cpp
void vtkOpenGLCamera::UpdateViewport(vtkRenderer* ren)

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

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


GetKeyMatrices 方法(最重要)

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

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

缓存检查机制

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

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

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

矩阵计算过程

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

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

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

cpp
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 矩阵

text
this->WCVCMatrix->Transpose();

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

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

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

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

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

text
vtkMatrix4x4::Multiply4x4(this->WCVCMatrix, this->VCDCMatrix, this->WCDCMatrix);
     $\text{WCDC} = \text{WCVC} \times \text{VCDC}$

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

6. 更新时间戳

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

坐标系转换流程图


关键设计特点

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

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

基于 VitePress 强力驱动 | 记录技术与生活