绘制3D点阵云
<p>在VTK(Visualization Toolkit)中加载和渲染点云数据的主体逻辑。它首先对点云数据进行采样处理,然后将处理后的数据可视化为3D图形。具体步骤如下:</p>
<h3>1. 创建点数据对象</h3>
<pre><code>const vtkPointsData = vtkPoints.newInstance();</code></pre>
<p>创建一个新的 vtkPoints 实例,用于存储点云数据。</p>
<h3>2. 点云数据采样</h3>
<pre><code>const maxPoints = 500000;
let numPoints = data.data.length / 3;
console.log('要渲染的点云的数量', numPoints);</code></pre>
<p>定义最大点数 maxPoints 为 500,000。获取点云数据的数量 numPoints。</p>
<pre><code>if (numPoints &gt; maxPoints) {
const ratio = numPoints / maxPoints;
let downsampledArray = [];
const orrginData = data.data;
for (let i = 0; i &lt; maxPoints; i++) {
const index = Math.floor(i * ratio) * 3;
downsampledArray.push(orrginData[index], orrginData[index + 1], orrginData[index + 2]);
}
console.log('降低采样后绘制的点云的数量', maxPoints);
vtkPointsData.setNumberOfPoints(maxPoints);
vtkPointsData.setData(new Float32Array(downsampledArray));
numPoints = maxPoints;
downsampledArray = null;
} else {
console.log('绘制的点云的数量', data.data.length / 3);
vtkPointsData.setNumberOfPoints(numPoints);
vtkPointsData.setData(data.data);
}
delete data.data;</code></pre>
<ul>
<li>如果点数超过 maxPoints,则通过降低采样率来减少点的数量。每个点的数据(x, y, z)从原始数据中按照比例抽取。然后将降采样后的数据设置到 vtkPointsData 中。</li>
<li>如果点数没有超过 maxPoints,则直接使用原始数据。</li>
</ul>
<h3>3. 创建 vtkPolyData 对象</h3>
<pre><code>const polydata = vtkPolyData.newInstance();
polydata.setPoints(vtkPointsData);</code></pre>
<p>创建 vtkPolyData 对象,并将处理后的点数据设置进去。</p>
<h3>4. 创建和设置点的表示形式</h3>
<pre><code>const sphereSource = vtkSphereSource.newInstance({
radius: this.getRadius(numPoints),
thetaResolution: 8,
phiResolution: 3
});</code></pre>
<ul>
<li>使用 vtkSphereSource 创建一个小球体,用作每个点的可视化表示。radius 由 getRadius 方法确定,thetaResolution 和 phiResolution 设置球体的分辨率。</li>
</ul>
<pre><code>const mapper = vtkGlyph3DMapper.newInstance({
scaleFactor: 1.0
});
mapper.setInputData(polydata);
mapper.setSourceConnection(sphereSource.getOutputPort());</code></pre>
<p>创建 vtkGlyph3DMapper 实例,将点云数据映射到球体上。</p>
<h3>5. 创建和添加 vtkActor</h3>
<pre><code>const actor = vtkActor.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
actor.getProperty().setColor(0, 1.0, 0);
actor.getProperty().setDiffuseColor(0, 1.0, 0);
actor.getProperty().setAmbientColor(0, 1.0, 0);</code></pre>
<p>创建 vtkActor 实例,并将其与 mapper 关联,然后将其添加到渲染器中。设置其颜色为绿色。</p>
<h3>6. 创建点云的边界框(Outline)</h3>
<pre><code>const outline = vtkOutlineFilter.newInstance();
outline.setInputData(polydata);
const mapperOut = vtkMapper.newInstance();
mapperOut.setInputConnection(outline.getOutputPort());
const actorOut = vtkActor.newInstance();
actorOut.setMapper(mapperOut);
actorOut.getProperty().set({
lineWidth: 3,
color: [1, 1, 1]
});
renderer.addActor(actorOut);</code></pre>
<p>使用 vtkOutlineFilter 创建点云数据的边界框,并将其添加到渲染器中,设置边框的线宽和颜色为白色。</p>
<h3>7. 创建和添加坐标轴</h3>
<pre><code>const { axis } = this.vm.$refs.canvasHeader.getConfig('Moudle');
const cubeAxes = vtkCubeAxesActor.newInstance();
cubeAxes.setAxisLabels(['X', 'Y', 'Z']);
cubeAxes.getProperty().setColor(0.3, 0.3, 0.3);
cubeAxes.setCamera(renderer.getActiveCamera());
cubeAxes.setDataBounds(actor.getBounds());
if (axis) {
renderer.addActor(cubeAxes);
}</code></pre>
<p>创建 vtkCubeAxesActor 实例用于显示坐标轴。设置坐标轴标签和颜色,并根据点云的边界设置坐标轴范围。如果配置中要求显示坐标轴,则将其添加到渲染器中。</p>
<pre><code>const axesActor = vtkAxesActor.newInstance();
const configAxis = axesActor.getConfig();
axesActor.setDragable(true);
configAxis.recenter = false;
axesActor.setConfig(configAxis);
axesActor.update();
if (axis) {
renderer.addActor(axesActor);
}</code></pre>
<p>创建 vtkAxesActor 实例来显示坐标轴,并设置其可拖动属性。根据配置更新坐标轴并添加到渲染器中。</p>
<p>这段代码综合了点云数据的处理、可视化表示以及坐标轴的添加,展示了如何在VTK中处理和渲染大规模点云数据。</p>
<p>完整源码</p>
<pre><code class="language-javascript"> /**
* 点云加载主体逻辑 抽离
* @param {*} data
* @param {*} renderer
*/
vtkLoadPointsContent(data, renderer) {
const vtkPointsData = vtkPoints.newInstance();
const maxPoints = 500000;
let numPoints = data.data.length / 3;
console.log('要渲染的点云的数量', numPoints);
// 如果绘制的点大于100万 开始降低采样绘制
if (numPoints &gt; maxPoints) {
const ratio = numPoints / maxPoints;
let downsampledArray = [];
const orrginData = data.data;
for (let i = 0; i &lt; maxPoints; i++) {
const index = Math.floor(i * ratio) * 3; // 每个坐标对有三个元素
downsampledArray.push(orrginData[index], orrginData[index + 1], orrginData[index + 2]);
}
console.log('降低采样后绘制的点云的数量', maxPoints);
vtkPointsData.setNumberOfPoints(maxPoints);
vtkPointsData.setData(new Float32Array(downsampledArray));
numPoints = maxPoints;
downsampledArray = null;
} else {
console.log('绘制的点云的数量', data.data.length / 3);
vtkPointsData.setNumberOfPoints(numPoints);
vtkPointsData.setData(data.data);
}
delete data.data;
// 创建 vtkPolyData 对象
const polydata = vtkPolyData.newInstance();
polydata.setPoints(vtkPointsData);
// 使用 vtkSphereSource 创建一个小球体,作为每个点的表示形式
const sphereSource = vtkSphereSource.newInstance({
radius: this.getRadius(numPoints), // 球体半径
thetaResolution: 8,
phiResolution: 3
});
// 使用 vtkGlyph3DMapper 将点云数据映射到小球体上
const mapper = vtkGlyph3DMapper.newInstance({
scaleFactor: 1.0
});
mapper.setInputData(polydata);
mapper.setSourceConnection(sphereSource.getOutputPort());
// 创建 Actor 并设置 Mapper
const actor = vtkActor.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
actor.getProperty().setColor(0, 1.0, 0);
actor.getProperty().setDiffuseColor(0, 1.0, 0);
actor.getProperty().setAmbientColor(0, 1.0, 0);
const outline = vtkOutlineFilter.newInstance();
outline.setInputData(polydata);
const mapperOut = vtkMapper.newInstance();
mapperOut.setInputConnection(outline.getOutputPort());
const actorOut = vtkActor.newInstance();
actorOut.setMapper(mapperOut);
actorOut.getProperty().set({
lineWidth: 3,
color: [1, 1, 1]
});
renderer.addActor(actorOut);
const { axis } = this.vm.$refs.canvasHeader.getConfig('Moudle');
// todd 坐标系绘制
const cubeAxes = vtkCubeAxesActor.newInstance();
cubeAxes.setAxisLabels(['X', 'Y', 'Z']);
cubeAxes.getProperty().setColor(0.3, 0.3, 0.3);
cubeAxes.setCamera(renderer.getActiveCamera());
cubeAxes.setDataBounds(actor.getBounds());
if (axis) {
renderer.addActor(cubeAxes);
}
const axesActor = vtkAxesActor.newInstance();
const configAxis = axesActor.getConfig();
axesActor.setDragable(true);
configAxis.recenter = false;
axesActor.setConfig(configAxis);
axesActor.update();
if (axis) {
renderer.addActor(axesActor);
}
}</code></pre>