Skip to content

vtkOpenGLRenderer 详细解析

vtkOpenGLRenderer 详细解析

vtkOpenGLRenderer 是 VTK 中负责OpenGL 渲染管线的核心协调器,它管理整个场景的渲染流程,包括光照、相机、几何体、透明度处理、阴影等。

代码来源:vtkOpenGLRenderer


类的定位


主要功能模块


核心代码详解

构造函数 - 初始化所有 Pass 对象

cpp
vtkOpenGLRenderer::vtkOpenGLRenderer()
{
  this->FXAAFilter = nullptr;              // FXAA 抗锯齿滤镜
  this->DepthPeelingPass = nullptr;        // 深度剥离通道
  this->SSAOPass = nullptr;                // 环境光遮蔽通道
  this->TranslucentPass = nullptr;         // 透明度通道
  this->ShadowMapPass = nullptr;           // 阴影映射通道
  this->DepthPeelingHigherLayer = 0;       // 深度剥离层数

  this->LightingCount = -1;                // 灯光数量(-1表示未初始化)
  this->LightingComplexity = -1;           // 灯光复杂度(0-3)

  this->EnvMapLookupTable = nullptr;       // PBR 查找表
  this->EnvMapIrradiance = nullptr;        // IBL 辐照度
  this->EnvMapPrefiltered = nullptr;       // IBL 预过滤
  this->UseSphericalHarmonics = true;      // 球谐函数标志
}

设计意义:

  • 所有 Pass 对象延迟创建(按需创建),节省内存
  • 灯光计数初始化为 -1 表示"未更新"状态

核心渲染方法 - DeviceRender()

这是整个渲染流程的入口点:

cpp
void vtkOpenGLRenderer::DeviceRender()
{
  vtkTimerLog::MarkStartEvent("OpenGL Dev Render");

  // ===== 步骤1:计算 IBL (Image-Based Lighting) 纹理 =====
  // IBL:https://blog.csdn.net/shebao3333/article/details/134736437
  bool computeIBLTextures = !(this->Pass && this->Pass->IsA("vtkOSPRayPass")) &&
    this->UseImageBasedLighting && this->EnvironmentTexture;

  if (computeIBLTextures)
  {
    // 加载 PBR 相关纹理
    this->GetEnvMapLookupTable()->Load(this);           // LUT 表
    this->GetEnvMapPrefiltered()->Load(this);           // 预过滤图

    bool useSH = this->UseSphericalHarmonics;           // 使用球谐函数?

    // 如果是立方体贴图,不能使用球谐函数
    if (useSH && this->EnvironmentTexture->GetCubeMap())
    {
      vtkWarningMacro("Cannot compute spherical harmonics of a cubemap...");
      useSH = false;
    }

    // 计算球谐函数系数或辐照度纹理
    if (useSH)
    {
      if (!this->SphericalHarmonics ||
          img->GetMTime() > this->SphericalHarmonics->GetMTime())
      {
        vtkNew<vtkSphericalHarmonics> sh;
        sh->SetInputData(img);
        sh->Update();
        this->SphericalHarmonics = vtkFloatArray::SafeDownCast(
          vtkTable::SafeDownCast(sh->GetOutputDataObject(0))->GetColumn(0));
      }
    }
    else
    {
      this->GetEnvMapIrradiance()->Load(this);          // 使用辐照度纹理
    }
  }

  // ===== 步骤2:使用 RenderPass 系统 或 标准渲染流程 =====
  if (this->Pass != nullptr)
  {
    // 高级渲染通道(如 OSPRay、自定义 Pass)
    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
    s.SetFrameBuffer(nullptr);
    this->Pass->Render(&s);
  }
  else
  {
    // 标准 OpenGL 渲染流程
    this->RenderWindow->MakeCurrent();
    vtkOpenGLClearErrorMacro();

    // 三个关键步骤
    this->UpdateCamera();              // 更新相机矩阵
    this->UpdateLightGeometry();       // 更新灯光位置
    this->UpdateLights();              // 更新灯光参数 → 着色器
    this->UpdateGeometry();            // 更新并渲染几何体

    vtkOpenGLCheckErrorMacro("failed after DeviceRender");
  }

  // ===== 步骤3:清理 IBL 纹理 =====
  if (computeIBLTextures)
  {
    this->GetEnvMapLookupTable()->PostRender(this);
    this->GetEnvMapIrradiance()->PostRender(this);
    this->GetEnvMapPrefiltered()->PostRender(this);
  }

  vtkTimerLog::MarkEndEvent("OpenGL Dev Render");
}

流程图:


灯光管理 - UpdateLights()(关键方法)

这是理解 VTK 光照系统的核心:

cpp
int vtkOpenGLRenderer::UpdateLights()
{
  // ===== 步骤1:遍历所有灯光,分类 =====
  vtkLightCollection* lc = this->GetLights();
  vtkLight* light;

  int lightingComplexity = 0;  // 0=无  1=Headlight  2=LightKit  3=Positional
  int lightingCount = 0;

  vtkMTimeType ltime = lc->GetMTime();

  vtkCollectionSimpleIterator sit;
  for (lc->InitTraversal(sit); (light = lc->GetNextLight(sit));)
  {
    float status = light->GetSwitch();
    if (status > 0.0)  // 灯光是否启用
    {
      ltime = vtkMath::Max(ltime, light->GetMTime());
      lightingCount++;

      // 初始化复杂度
      if (lightingComplexity == 0)
        lightingComplexity = 1;
    }

    // 升级复杂度:多个灯或非 Headlight
    if (lightingComplexity == 1 &&
      (lightingCount > 1 || light->GetLightType() != VTK_LIGHT_TYPE_HEADLIGHT))
    {
      lightingComplexity = 2;
    }

    // 升级复杂度:有位置灯(点光源或聚光灯)
    if (lightingComplexity < 3 && light->GetPositional())
    {
      lightingComplexity = 3;
    }
  }

  // 如果没有灯光但使用 IBL,也需要一些光照
  if (this->GetUseImageBasedLighting() && this->GetEnvironmentTexture() &&
      lightingComplexity == 0)
  {
    lightingComplexity = 1;
  }

  // 自动创建灯光
  if (!lightingCount && this->AutomaticLightCreation)
  {
    vtkDebugMacro(<< "No lights are on, creating one.");
    this->CreateLight();
    // ... 重新初始化
  }

  // ===== 步骤2:如果灯光配置改变,生成 GLSL 代码 =====
  if (lightingComplexity != this->LightingComplexity ||
      lightingCount != this->LightingCount)
  {
    this->LightingComplexity = lightingComplexity;
    this->LightingCount = lightingCount;
    this->LightingUpdateTime = ltime;

    // 生成 GLSL uniform 声明
    std::ostringstream toString;
    switch (this->LightingComplexity)
    {
      case 0:  // 无光照
        this->LightingDeclaration = "";
        break;

      case 1:  // Headlight(简单头灯)
        this->LightingDeclaration = "uniform vec3 lightColor0;\n";
        break;

      case 2:  // Light Kit(多个方向灯)
        toString.clear();
        toString.str("");
        for (int i = 0; i < this->LightingCount; ++i)
        {
          toString << "uniform vec3 lightColor" << i << ";\n"
                   << "  uniform vec3 lightDirectionVC" << i << "; // normalized\n";
        }
        this->LightingDeclaration = toString.str();
        break;

      case 3:  // 位置灯(点光源、聚光灯)
        toString.clear();
        toString.str("");
        for (int i = 0; i < this->LightingCount; ++i)
        {
          toString << "uniform vec3 lightColor" << i << ";\n"
                   << "uniform vec3 lightDirectionVC" << i << "; // normalized\n"
                   << "uniform vec3 lightPositionVC" << i << ";\n"
                   << "uniform vec3 lightAttenuation" << i << ";\n"
                   << "uniform float lightConeAngle" << i << ";\n"
                   << "uniform float lightExponent" << i << ";\n"
                   << "uniform int lightPositional" << i << ";\n";
        }
        this->LightingDeclaration = toString.str();
        break;
    }
  }

  this->LightingUpdateTime = ltime;
  return this->LightingCount;
}

灯光复杂度分类:

生成的 GLSL 代码示例(Complexity=3):

cpp
// 为 3 盏灯生成的 uniform 声明

uniform vec3 lightColor0;
uniform vec3 lightDirectionVC0;
uniform vec3 lightPositionVC0;
uniform vec3 lightAttenuation0;
uniform float lightConeAngle0;
uniform float lightExponent0;
uniform int lightPositional0;

uniform vec3 lightColor1;
// ... 相同模式

几何体渲染 - UpdateGeometry()

管理所有几何体的渲染,包括选择、阴影、透明度:

cpp
int vtkOpenGLRenderer::UpdateGeometry(vtkFrameBufferObjectBase* fbo)
{
  vtkRenderTimerLog* timer = this->GetRenderWindow()->GetRenderTimer();
  VTK_SCOPED_RENDER_EVENT("vtkOpenGLRenderer::UpdateGeometry", timer);

  int i;
  this->NumberOfPropsRendered = 0;

  if (this->PropArrayCount == 0)
    return 0;

  // ===== 步骤1:处理选择模式 =====
  if (this->Selector)
  {
    VTK_SCOPED_RENDER_EVENT2("Selection", timer, selectionEvent);

    // 使用选择器进行拾取渲染
    if (this->PickFromProps)
    {
      // 从指定的 Props 中选择
      vtkProp** pa = new vtkProp*[this->PickFromProps->GetNumberOfItems()];
      int pac = 0;

      vtkCollectionSimpleIterator pit;
      for (this->PickFromProps->InitTraversal(pit);
           (aProp = this->PickFromProps->GetNextProp(pit));)
      {
        if (aProp->GetVisibility())
          pa[pac++] = aProp;
      }

      this->NumberOfPropsRendered = this->Selector->Render(this, pa, pac);
      delete[] pa;
    }
    else
    {
      // 从所有 Props 中选择
      this->NumberOfPropsRendered =
        this->Selector->Render(this, this->PropArray, this->PropArrayCount);
    }

    this->RenderTime.Modified();
    return this->NumberOfPropsRendered;
  }

  // ===== 步骤2:阴影渲染 或 标准几何渲染 =====
  int hasTranslucentPolygonalGeometry = 0;

  if (this->UseShadows)
  {
    VTK_SCOPED_RENDER_EVENT2("Shadows", timer, shadowsEvent);

    // 创建阴影 Pass
    if (!this->ShadowMapPass)
      this->ShadowMapPass = vtkShadowMapPass::New();

    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);

    // 执行阴影贴图烘焙和渲染
    this->ShadowMapPass->GetShadowMapBakerPass()->Render(&s);
    this->ShadowMapPass->Render(&s);
  }
  else
  {
    // ===== 不透明几何 =====
    timer->MarkStartEvent("Opaque Geometry");
    this->DeviceRenderOpaqueGeometry(fbo);
    timer->MarkEndEvent();

    // 检查是否有透明几何
    for (i = 0; !hasTranslucentPolygonalGeometry && i < this->PropArrayCount; i++)
    {
      hasTranslucentPolygonalGeometry =
        this->PropArray[i]->HasTranslucentPolygonalGeometry();
    }

    // ===== 透明几何 =====
    if (hasTranslucentPolygonalGeometry)
    {
      timer->MarkStartEvent("Translucent Geometry");
      this->DeviceRenderTranslucentPolygonalGeometry(fbo);
      timer->MarkEndEvent();
    }
  }

  // ===== 步骤3:抗锯齿 (FXAA) =====
  if (this->UseFXAA)
  {
    timer->MarkStartEvent("FXAA");
    if (!this->FXAAFilter)
      this->FXAAFilter = vtkOpenGLFXAAFilter::New();

    if (this->FXAAOptions)
      this->FXAAFilter->UpdateConfiguration(this->FXAAOptions);

    this->FXAAFilter->Execute(this);
    timer->MarkEndEvent();
  }

  // ===== 步骤4:体积数据 =====
  if (hasTranslucentPolygonalGeometry == 0 || !this->UseDepthPeeling ||
    !this->UseDepthPeelingForVolumes)
  {
    timer->MarkStartEvent("Volumes");
    for (i = 0; i < this->PropArrayCount; i++)
    {
      this->NumberOfPropsRendered +=
        this->PropArray[i]->RenderVolumetricGeometry(this);
    }
    timer->MarkEndEvent();
  }

  // ===== 步骤5:覆盖层(HUD、文本等) =====
  timer->MarkStartEvent("Overlay");
  for (i = 0; i < this->PropArrayCount; i++)
  {
    this->NumberOfPropsRendered +=
      this->PropArray[i]->RenderOverlay(this);
  }
  timer->MarkEndEvent();

  this->RenderTime.Modified();
  vtkDebugMacro(<< "Rendered " << this->NumberOfPropsRendered << " actors");

  return this->NumberOfPropsRendered;
}

渲染顺序流程:


不透明几何渲染 - DeviceRenderOpaqueGeometry()

cpp
void vtkOpenGLRenderer::DeviceRenderOpaqueGeometry(vtkFrameBufferObjectBase* fbo)
{
  // ===== 选择渲染方法 =====

  // 隐线去除(Wireframe)
  bool useHLR = this->UseHiddenLineRemoval &&
    vtkHiddenLineRemovalPass::WireframePropsExist(this->PropArray, this->PropArrayCount);

  if (useHLR)
  {
    vtkNew<vtkHiddenLineRemovalPass> hlrPass;
    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
    s.SetFrameBuffer(fbo);
    hlrPass->Render(&s);
    this->NumberOfPropsRendered += hlrPass->GetNumberOfRenderedProps();
  }
  else
  {
    // SSAO(Screen Space Ambient Occlusion)
    if (this->UseSSAO)
    {
      if (!this->SSAOPass)
      {
        this->SSAOPass = vtkSSAOPass::New();
        vtkNew<vtkOpaquePass> opaqueP;
        this->SSAOPass->SetDelegatePass(opaqueP);
      }

      vtkRenderState s(this);
      s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
      s.SetFrameBuffer(fbo);

      // 设置 SSAO 参数
      this->SSAOPass->SetRadius(this->SSAORadius);
      this->SSAOPass->SetBias(this->SSAOBias);
      this->SSAOPass->SetKernelSize(this->SSAOKernelSize);
      this->SSAOPass->SetBlur(this->SSAOBlur);

      this->SSAOPass->Render(&s);
      this->NumberOfPropsRendered += this->SSAOPass->GetNumberOfRenderedProps();
    }
    else
    {
      // 标准不透明渲染
      this->Superclass::DeviceRenderOpaqueGeometry();
    }
  }
}

不透明几何的三种渲染方式:


透明几何渲染 - DeviceRenderTranslucentPolygonalGeometry()

处理透明度问题的最复杂的部分:

cpp
void vtkOpenGLRenderer::DeviceRenderTranslucentPolygonalGeometry(vtkFrameBufferObjectBase* fbo)
{
  vtkOpenGLClearErrorMacro();
  vtkOpenGLRenderWindow* context = vtkOpenGLRenderWindow::SafeDownCast(this->RenderWindow);

  if (!this->UseDepthPeeling)
  {
    // ===== 不使用深度剥离:使用 OIT (Order-Independent Transparency) =====
    if (!this->TranslucentPass)
    {
      // 创建顺序无关透明度 Pass
      vtkOrderIndependentTranslucentPass* oit = vtkOrderIndependentTranslucentPass::New();
      this->TranslucentPass = oit;
    }

    vtkTranslucentPass* tp = vtkTranslucentPass::New();
    this->TranslucentPass->SetTranslucentPass(tp);
    tp->Delete();

    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
    s.SetFrameBuffer(fbo);

    this->LastRenderingUsedDepthPeeling = 0;
    this->TranslucentPass->Render(&s);
    this->NumberOfPropsRendered += this->TranslucentPass->GetNumberOfRenderedProps();
  }
  else  // 使用深度剥离
  {
# ifdef GL_ES_VERSION_3_0
    vtkErrorMacro("Built in Dual Depth Peeling is not supported on ES3...");
    this->UpdateTranslucentPolygonalGeometry();
# else
    // ===== 创建或配置深度剥离 Pass =====
    if (!this->DepthPeelingPass)
    {
      // 选择单剥离或双剥离
      if (this->IsDualDepthPeelingSupported())
      {
        vtkDebugMacro("Using dual depth peeling.");
        vtkDualDepthPeelingPass* ddpp = vtkDualDepthPeelingPass::New();
        this->DepthPeelingPass = ddpp;
      }
      else
      {
        vtkDebugMacro("Using standard depth peeling...");
        this->DepthPeelingPass = vtkDepthPeelingPass::New();
      }

      vtkTranslucentPass* tp = vtkTranslucentPass::New();
      this->DepthPeelingPass->SetTranslucentPass(tp);
      tp->Delete();
    }

    // ===== 配置体积+深度剥离 =====
    if (this->UseDepthPeelingForVolumes)
    {
      vtkDualDepthPeelingPass* ddpp =
        vtkDualDepthPeelingPass::SafeDownCast(this->DepthPeelingPass);

      if (!ddpp)
      {
        vtkWarningMacro("UseDepthPeelingForVolumes requested, but unsupported...");
        this->UseDepthPeelingForVolumes = false;
      }
      else if (!ddpp->GetVolumetricPass())
      {
        vtkVolumetricPass* vp = vtkVolumetricPass::New();
        ddpp->SetVolumetricPass(vp);
        vp->Delete();
      }
    }
    else
    {
      vtkDualDepthPeelingPass* ddpp =
        vtkDualDepthPeelingPass::SafeDownCast(this->DepthPeelingPass);
      if (ddpp)
        ddpp->SetVolumetricPass(nullptr);
    }

    // ===== 执行深度剥离渲染 =====
    this->DepthPeelingPass->SetMaximumNumberOfPeels(this->MaximumNumberOfPeels);
    this->DepthPeelingPass->SetOcclusionRatio(this->OcclusionRatio);

    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
    s.SetFrameBuffer(fbo);

    this->LastRenderingUsedDepthPeeling = 1;
    this->DepthPeelingPass->Render(&s);
    this->NumberOfPropsRendered += this->DepthPeelingPass->GetNumberOfRenderedProps();
# endif
  }

  vtkOpenGLCheckErrorMacro("failed after DeviceRenderTranslucentPolygonalGeometry");
}

透明度处理方案对比:


背景渲染 - Clear()

处理各种背景类型:

cmake
void vtkOpenGLRenderer::Clear()
{
  vtkOpenGLClearErrorMacro();

  GLbitfield clear_mask = 0;
  vtkOpenGLState* ostate = this->GetState();

  // ===== 清屏颜色 =====
  if (!this->Transparent())
  {
    ostate->vtkglClearColor(
      static_cast<GLclampf>(this->Background[0]),
      static_cast<GLclampf>(this->Background[1]),
      static_cast<GLclampf>(this->Background[2]),
      static_cast<GLclampf>(this->BackgroundAlpha));
    clear_mask |= GL_COLOR_BUFFER_BIT;
  }

  // ===== 清屏深度 =====
  if (!this->GetPreserveDepthBuffer())
  {
    ostate->vtkglClearDepth(static_cast<GLclampf>(1.0));
    clear_mask |= GL_DEPTH_BUFFER_BIT;
    ostate->vtkglDepthMask(GL_TRUE);
  }

  vtkDebugMacro(<< "glClear\n");
  ostate->vtkglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  ostate->vtkglClear(clear_mask);

  // ===== 渐变背景或纹理背景 =====
  if (!this->Transparent() && (this->GradientBackground || this->TexturedBackground))
  {
    auto oglRenWin = vtkOpenGLRenderWindow::SafeDownCast(this->GetRenderWindow());
    auto texture = this->GetCurrentTexturedBackground();

    // 使用全屏四边形 Shader 渲染
    std::string fs = vtkOpenGLRenderUtilities::GetFullScreenQuadFragmentShaderTemplate();
    ostate->vtkglDisable(GL_DEPTH_TEST);

    if (this->TexturedBackground && texture)
    {
      // 纹理背景:采样纹理
      vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Decl",
        "uniform sampler2D backgroundImage;\n"
        "//VTK::FSQ::Decl");
      vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Impl",
        "  gl_FragData[0] = vec4(texture(backgroundImage, texCoord).rgb, 1.0);\n"
        "//VTK::FSQ::Impl");
    }
    else  // 渐变背景
    {
      vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Decl",
        "uniform vec3 stopColors[2];\n"
        "uniform vec2 screenSize;\n"
        "//VTK::FSQ::Decl");

      // 根据渐变模式选择算法
      switch (this->GradientMode)
      {
        case VTK_GRADIENT_RADIAL_VIEWPORT_FARTHEST_SIDE:
          vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Impl",
            "  float value = clamp(length(texCoord - vec2(0.5)) * 2.0, 0.0, 1.0);\n"
            "//VTK::FSQ::Impl");
          break;
        case VTK_GRADIENT_HORIZONTAL:
          vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Impl",
            "  float value = texCoord.s;\n"
            "//VTK::FSQ::Impl");
          break;
        case VTK_GRADIENT_VERTICAL:
          // ... 其他情况
          break;
      }

      // 混合两种颜色
      vtkShaderProgram::Substitute(fs, "//VTK::FSQ::Impl",
        "  gl_FragData[0] = vec4(stopColors[0] * (1.0 - value) + "
        "stopColors[1] * value, 1.0);");
    }

    // 编译并执行 Shader
    this->BackgroundRenderer.reset(
      new vtkOpenGLQuadHelper(oglRenWin, nullptr, fs.c_str(), nullptr, false));

    oglRenWin->GetShaderCache()->ReadyShaderProgram(this->BackgroundRenderer->Program);

    // 设置 uniform
    if (this->TexturedBackground && texture)
    {
      texture->InterpolateOn();
      texture->Render(this);
      this->BackgroundRenderer->Program->SetUniformi(
        "backgroundImage", texture->GetTextureUnit());
    }
    else
    {
      float stopColors[2][3] = {};
      std::copy(this->Background, this->Background + 3, &stopColors[0][0]);
      std::copy(this->Background2, this->Background2 + 3, &stopColors[1][0]);

      this->BackgroundRenderer->Program->SetUniform3fv("stopColors", 2, stopColors);
      // ... 设置其他参数
    }

    // 渲染背景
    this->BackgroundRenderer->Render();

    // 清理纹理
    if (this->TexturedBackground && texture)
      texture->PostRender(this);
  }

  ostate->vtkglEnable(GL_DEPTH_TEST);
  vtkOpenGLCheckErrorMacro("failed after Clear");
}

背景渲染的三种模式:

cpp
// 模式1: 纯色背景
glClearColor(r, g, b, a);

// 模式2: 渐变背景
float value = length(texCoord - vec2(0.5)) * 2.0;  // 径向
float value = texCoord.s;  // 水平
float value = texCoord.t;  // 竖直

gl_FragColor = mix(color1, color2, value);

// 模式3: 纹理背景
gl_FragColor = texture(backgroundImage, texCoord);

灯光 Uniforms 更新 - UpdateLightingUniforms()

将灯光参数设置到 GLSL 着色器:

cpp
void vtkOpenGLRenderer::UpdateLightingUniforms(vtkShaderProgram* program)
{
  // ===== 检查是否需要更新 =====
  vtkMTimeType ptime = program->GetUniformGroupUpdateTime(vtkShaderProgram::LightingGroup);
  vtkMTimeType ltime = this->LightingUpdateTime;

  // 相机影响灯光(对于非头灯情况)
  if (this->LightingComplexity > 1)
  {
    vtkCamera* cam = this->GetActiveCamera();
    ltime = vtkMath::Max(ltime, cam->GetMTime());
  }

  if (ltime <= ptime)
    return;  // 无需更新

  // ===== 获取相机变换 =====
  vtkTransform* viewTF = this->GetActiveCamera()->GetModelViewTransformObject();

  // ===== 设置灯光参数到着色器 =====
  int numberOfLights = 0;
  vtkLightCollection* lc = this->GetLights();
  vtkLight* light;

  vtkCollectionSimpleIterator sit;
  float lightColor[3];
  float lightDirection[3];

  for (lc->InitTraversal(sit); (light = lc->GetNextLight(sit));)
  {
    if (light->GetSwitch() <= 0.0)
      continue;

    // 灯光颜色
    double* dColor = light->GetDiffuseColor();
    double intensity = light->GetIntensity();
    lightColor[0] = dColor[0] * intensity;
    lightColor[1] = dColor[1] * intensity;
    lightColor[2] = dColor[2] * intensity;

    std::ostringstream toString;
    toString << numberOfLights;
    std::string count = toString.str();

    program->SetUniform3f(("lightColor" + count).c_str(), lightColor);

    // ===== 只有非头灯才设置方向 =====
    if (this->LightingComplexity >= 2)
    {
      // 计算灯光方向(从焦点指向灯光)
      double* lfp = light->GetTransformedFocalPoint();
      double* lp = light->GetTransformedPosition();
      double lightDir[3];
      vtkMath::Subtract(lfp, lp, lightDir);
      vtkMath::Normalize(lightDir);

      // 转换到视图坐标系
      double tDirView[3];
      viewTF->TransformNormal(lightDir, tDirView);

      // 应用用户灯光变换(如果有)
      if (!light->LightTypeIsSceneLight() &&
          this->UserLightTransform.GetPointer() != nullptr)
      {
        double* tDir = this->UserLightTransform->TransformNormal(tDirView);
        lightDirection[0] = tDir[0];
        lightDirection[1] = tDir[1];
        lightDirection[2] = tDir[2];
      }
      else
      {
        lightDirection[0] = tDirView[0];
        lightDirection[1] = tDirView[1];
        lightDirection[2] = tDirView[2];
      }

      program->SetUniform3f(("lightDirectionVC" + count).c_str(), lightDirection);

      // ===== 位置灯光:设置位置、衰减、圆锥等 =====
      if (this->LightingComplexity >= 3)
      {
        float lightAttenuation[3];
        float lightPosition[3];

        // 衰减系数
        double* attn = light->GetAttenuationValues();
        lightAttenuation[0] = attn[0];
        lightAttenuation[1] = attn[1];
        lightAttenuation[2] = attn[2];

        // 位置(视图坐标)
        double tlpView[3];
        viewTF->TransformPoint(lp, tlpView);

        if (!light->LightTypeIsSceneLight() &&
            this->UserLightTransform.GetPointer() != nullptr)
        {
          double* tlp = this->UserLightTransform->TransformPoint(tlpView);
          lightPosition[0] = tlp[0];
          lightPosition[1] = tlp[1];
          lightPosition[2] = tlp[2];
        }
        else
        {
          lightPosition[0] = tlpView[0];
          lightPosition[1] = tlpView[1];
          lightPosition[2] = tlpView[2];
        }

        program->SetUniform3f(("lightAttenuation" + count).c_str(), lightAttenuation);
        program->SetUniformi(("lightPositional" + count).c_str(), light->GetPositional());
        program->SetUniform3f(("lightPositionVC" + count).c_str(), lightPosition);
        program->SetUniformf(("lightExponent" + count).c_str(), light->GetExponent());
        program->SetUniformf(("lightConeAngle" + count).c_str(), light->GetConeAngle());
      }
    }

    numberOfLights++;
  }

  // 标记为已更新
  program->SetUniformGroupUpdateTime(vtkShaderProgram::LightingGroup, ltime);
}

灯光参数流动图:


完整渲染流程总结


关键特性总结

特性实现作用
灯光复杂度分类0-3 级别自动生成最优着色器代码
Pass 系统vtkRenderPass 继承支持高级渲染效果
深度剥离单/双剥离算法正确处理透明度排序
IBL 支持PBR 纹理烘烤现实感光照
SSAO屏幕空间环境光遮蔽增强深度感
FXAA快速近似抗锯齿平衡质量和性能
背景渲染纯色/渐变/纹理灵活的背景配置
矩阵缓存MTime 检查避免重复计算

设计模式

python
// 1. 按需创建 Pass(延迟初始化)
if (!this->FXAAFilter)
  this->FXAAFilter = vtkOpenGLFXAAFilter::New();

// 2. 使用 MTime 缓存检查
if (ltime <= ptime)
  return;  // 数据未变化,跳过更新

// 3. RAII 错误检查
vtkOpenGLClearErrorMacro();
// ... 渲染代码
vtkOpenGLCheckErrorMacro("failed after render");

// 4. 状态隔离
vtkOpenGLState* ostate = this->GetState();
ostate->vtkglEnable(GL_DEPTH_TEST);
// ... 使用缓存机制

// 5. Pass 链式处理
pass->SetDelegatePass(another_pass);
pass->Render(&state);

这就是 vtkOpenGLRenderer 作为 VTK 渲染系统核心协调器的完整图景!

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