vtk.js之RenderWindow渲染图像展示

1,269 阅读3分钟

本篇文章你将会学到以下知识:

  1. vtk的体数据的渲染流程
  2. vtk的提数据的流程里面的各个类别含义
  3. vtk 的示例应用(需要结合上一篇的文章itkDICOMReader读取dicom格式的文件当作数据源)

vtk的体数据的渲染流程如下:

编辑切换为居中

vtk 渲染流程图

vtk的提数据的流程里面的各个类别含义:

RenderWindow: 窗口

Render:渲染器

Actor : 执行渲染mapper的对象

Mapper:把不同的数据类型,转换为图形数据

Filter: 对原始数据做一些操作,列如三角化,提取轮廓等(本节没有用到所以就没有写入了)

Source: 其实就是ImageData的数据(上一节通过dicomReader读取后的数据然后需要vtkHelper 转换)

本节实战是渲染体数据,所以用了Actor为vtkVolume这个类。

vtk 的示例应用:

引入vtk 的相关类

// 导入vtk 需要用的包
import "@kitware/vtk.js/favicon";
import "@kitware/vtk.js/Rendering/Profiles/Volume";
// 引入mapper
import vtkVolumeMapper from "@kitware/vtk.js/Rendering/Core/VolumeMapper";
// 引入actor
import vtkVolume from "@kitware/vtk.js/Rendering/Core/Volume";
// 引入 renderWindow
import vtkFullScreenRenderWindow from "@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow";

开始初始化我们需要的实例,这个vtkFullScreenRenderWindow 这个类比较特殊,因为通过源码其实里面以及帮我们实例化了Renderer 这个渲染器:

编辑切换为居中

index.js

所以不需要我们再实例化Renderer类即可

const fullScreenRenderWindow = vtkFullScreenRenderWindow.newInstance();
  const renderer = fullScreenRenderWindow.getRenderer();
  const renderWindow = fullScreenRenderWindow.getRenderWindow();
  const mapper = vtkVolumeMapper.newInstance();
  mapper.setInputData(source);
  const actor = vtkVolume.newInstance();
  actor.setMapper(mapper);
  const sampleDistance =
    0.7 *
    Math.sqrt(
      source
        .getSpacing()
        .map((v) => v * v)
        .reduce((a, b) => a + b, 0)
    );
  mapper.setSampleDistance(sampleDistance);

  const activeCamera = renderer.getActiveCamera();
  activeCamera.setPosition(0, -1, 0);
  activeCamera.setFocalPoint(0, 0, 0);
  activeCamera.setViewUp(0, 0, 1);
  renderer.addActor(actor);
  renderer.resetCamera();
  renderWindow.render();

每次在渲染窗口render之前需要将渲染器的camera 重新设置一下。

其中setPosition 和setFocalPoint setViewUp 是设置当前的UI图像的正面照(通过调节光源),其中setSampleDistance 是为了设置光源和相机的距离是为了后续的操作做一些准备(可以先不关它)

效果如下:

编辑切换为全宽

添加图片注释,不超过 140 字(可选)

但是这个图明显感觉是切片数据,无法看清当前的3D模型,所以需要设置一些数据的透明度和颜色,这个时候需要设置actor的Property。

其中有2个函数需要使用:

PiecewiseFunction ColorTransferFunction (相关作用去看API文档,不想做解释)

import vtkColorTransferFunction from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction";
import vtkPiecewiseFunction from "@kitware/vtk.js/Common/DataModel/PiecewiseFunction";


function updateEdgeGradient(renderWindow, actor, value) {
  const sourceDS = actor.getMapper().getInputData();
  const dataArray =
    sourceDS.getPointData().getScalars() ||
    sourceDS.getPointData().getArrays()[0];
  const dataRange = dataArray.getRange();
  const lookupTable = vtkColorTransferFunction.newInstance();
  lookupTable.addRGBPoint(-512, 0.0, 0.0, 0.0);
  lookupTable.addRGBPoint(670, 0.6, 0.0, 0.0);
  lookupTable.addRGBPoint(1961, 0.86, 0.67, 0.3);
  lookupTable.addRGBPoint(2893, 1.0, 1.0, 1.0);
  lookupTable.addRGBPoint(3072, 1.0, 1.0, 1.0);

  const piecewiseFunction = vtkPiecewiseFunction.newInstance();
  piecewiseFunction.addPoint(886, 0.0);
  piecewiseFunction.addPoint(1853, 0.28);
  piecewiseFunction.addPoint(3072, 0.5);
  if (value === 0) {
    actor.getProperty().setUseGradientOpacity(0, false);
  } else {
    actor.getProperty().setUseGradientOpacity(0, true);
    const minV = Math.max(0.0, value - 0.3) / 0.7;
    actor
      .getProperty()
      .setGradientOpacityMinimumValue(
        0,
        (dataRange[1] - dataRange[0]) * 0.2 * minV * minV
      );
    actor
      .getProperty()
      .setGradientOpacityMaximumValue(
        0,
        (dataRange[1] - dataRange[0]) * 1.0 * value * value
      );
    console.log("-----updateEdgeGradient", dataRange);
  }
  actor.getProperty().setRGBTransferFunction(0, lookupTable);
  actor.getProperty().setScalarOpacity(0, piecewiseFunction);
  renderWindow.render();
}

以上的参数为什么要这样设置,我这边也是拿前人的c++ 代码来做相应的设置。每个模型的数据可能对应的参数是不一样的。

将以上的代码中的函数放入

renderer.addActor(actor);
renderer.resetCamera();
updateEdgeGradient(renderWindow, actor, 0.45);

0.45这个参数也是我这边根据当前的3D调试出来的。

具体效果如下:

编辑切换为全宽

添加图片注释,不超过 140 字(可选)

下一篇将会实现标尺和十字线功能