vtk.js

vtk.js


绘制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 &amp;gt; maxPoints) { const ratio = numPoints / maxPoints; let downsampledArray = []; const orrginData = data.data; for (let i = 0; i &amp;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 &amp;gt; maxPoints) { const ratio = numPoints / maxPoints; let downsampledArray = []; const orrginData = data.data; for (let i = 0; i &amp;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>

页面列表

ITEM_HTML