本篇文章你将会学到以下知识:
- vtk的体数据的渲染流程
- vtk的提数据的流程里面的各个类别含义
- 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 字(可选)
下一篇将会实现标尺和十字线功能