vtk.js

vtk.js


绘制3D云图(模型加载和着色)

<p>加载3D体数据并将其渲染到指定的 renderWindow 中。以下是详细的步骤解释:</p> <h3>数据解析:</h3> <pre><code>const renderData = getArrayBufferData(souces, data); const binary = renderData;</code></pre> <p>这部分从数据源中获取二进制数组(binary),准备传递给 VTK 读取器。</p> <h3>VTK 对象初始化:</h3> <pre><code>const vtkVolumeMapper = window.vtk.Rendering.Core.vtkVolumeMapper; const vtkVolume = window.vtk.Rendering.Core.vtkVolume; const vtkXMLImageDataReader = window.vtk.IO.XML.vtkXMLImageDataReader; const vtkBoundingBox = window.vtk.Common.DataModel.vtkBoundingBox; const vtkColorTransferFunction = window.vtk.Rendering.Core.vtkColorTransferFunction; const vtkPiecewiseFunction = window.vtk.Common.DataModel.vtkPiecewiseFunction;</code></pre> <p>引入 VTK 库中的各种对象,用于处理和渲染数据。</p> <h3>设置交互器更新率:</h3> <pre><code>renderWindow.getInteractor().setDesiredUpdateRate(30);</code></pre> <p>设置渲染窗口的交互器更新率为每秒 30 帧,以提高渲染性能。</p> <h3>数据读取:</h3> <pre><code>const vtiReader = vtkXMLImageDataReader.newInstance(); vtiReader.parseAsArrayBuffer(binary); const source = vtiReader.getOutputData(0);</code></pre> <p>使用 vtkXMLImageDataReader 读取 VTI 格式的体数据,得到 source 数据对象。</p> <h3>映射器和演员创建:</h3> <pre><code>const mapper = vtkVolumeMapper.newInstance(); const actor = vtkVolume.newInstance();</code></pre> <p>创建 mapper 和 actor 对象。mapper 将数据映射到 3D 空间,actor 用于显示该数据。</p> <h3>设置数据范围和颜色条:</h3> <pre><code>const dataArray = source.getPointData().getScalars() || source.getPointData().getArrays()[0]; const dataRange = dataArray.getRange(); global.RenderVtk.dataRange = dataRange; const dataExtent = source.getExtent(); global.RenderVtk.dataExtent = dataExtent;</code></pre> <p>获取数据的范围(dataRange)和数据扩展(dataExtent),并存储到 global.RenderVtk 对象中。</p> <h3>配置颜色传输函数:</h3> <pre><code>const lookupTable = vtkColorTransferFunction.newInstance(); const piecewiseFunction = vtkPiecewiseFunction.newInstance();</code></pre> <p>创建颜色传输函数(lookupTable)和分段函数(piecewiseFunction)。</p> <pre><code>const rangeBase = (dataRange[1] - dataRange[0]) / (config.color.length - 1); const colorMin = config.showRangeStart * (config.scaleBase || 1); const colorMax = config.showRangeEnd * (config.scaleBase || 1);</code></pre> <p>计算颜色范围基础、颜色最小值和最大值。</p> <h3>配置颜色条:</h3> <pre><code>for (let index = 0; index &amp;lt; config.color.length; index++) { let color = config.color[index]; const value = dataRange[0] + rangeBase * index; if (value &amp;gt; colorMax) { color = config.color[config.color.length - 1]; } if (value &amp;lt; colorMin) { color = config.color[0]; } const { r, g, b } = d3.rgb(color); colorsBar.push(color); lookupTable.addRGBPoint(dataRange[0] + rangeBase * index, r / 255, g / 255, b / 255); }</code></pre> <p>为颜色传输函数添加颜色点,根据数据范围和配置中的颜色数组进行设置。</p> <h3>映射器和演员设置:</h3> <pre><code>actor.setMapper(mapper); mapper.setInputData(source); const sampleDistance = 0.7 * Math.sqrt(source.getSpacing().map(v =&amp;gt; v * v).reduce((a, b) =&amp;gt; a + b, 0)); mapper.setSampleDistance(sampleDistance);</code></pre> <p>设置 mapper 输入数据并调整采样距离以提高渲染效果。</p> <h3>数据组件设置:</h3> <pre><code>let ComponentsNum = source.getPointData().getScalars().getNumberOfComponents(); if (ComponentsNum &amp;gt; 4) { ComponentsNum = 4; } source.getPointData().getScalars().setNumberOfComponents(ComponentsNum);</code></pre> <p>限制数据组件的数量,最大为 4。</p> <h3>配置演员属性:</h3> <pre><code>actor.getProperty().setRGBTransferFunction(0, lookupTable); for (let index = 1; index &amp;lt; ComponentsNum; index++) { actor.getProperty().setScalarOpacity(index, piecewiseFunction); } actor.getProperty().setInterpolationTypeToLinear(); actor.getProperty().setScalarOpacityUnitDistance( 0, vtkBoundingBox.getDiagonalLength(source.getBounds()) / Math.max(...source.getDimensions()) ); actor.getProperty().setUseGradientOpacity(0, true); actor.getProperty().setGradientOpacityMinimumOpacity(0, config.colorOpacity / 100); actor.getProperty().setGradientOpacityMaximumOpacity(0, config.colorOpacity / 100); actor.getProperty().setAmbient(0.2); actor.getProperty().setDiffuse(0.7); actor.getProperty().setSpecular(0.3); actor.getProperty().setSpecularPower(8.0);</code></pre> <p>配置演员的颜色传输函数、透明度、插值类型、渐变透明度和光照属性,以确保渲染效果符合要求。</p> <p>完整代码</p> <pre><code class="language-javascript">/** * * @param {*} data * @param {*} renderWindow * @param {*} container */ async vtkLoad3D3C(data, renderWindow, renderer, camera, options = {}) { const renderData = getArrayBufferData(souces, data); const binary = renderData; const vtkVolumeMapper = window.vtk.Rendering.Core.vtkVolumeMapper; const vtkVolume = window.vtk.Rendering.Core.vtkVolume; const vtkXMLImageDataReader = window.vtk.IO.XML.vtkXMLImageDataReader; const vtkBoundingBox = window.vtk.Common.DataModel.vtkBoundingBox; const vtkColorTransferFunction = window.vtk.Rendering.Core.vtkColorTransferFunction; const vtkPiecewiseFunction = window.vtk.Common.DataModel.vtkPiecewiseFunction; renderWindow.getInteractor().setDesiredUpdateRate(30); const vtiReader = vtkXMLImageDataReader.newInstance(); vtiReader.parseAsArrayBuffer(binary); const source = vtiReader.getOutputData(0); const mapper = vtkVolumeMapper.newInstance(); const actor = vtkVolume.newInstance(); const dataArray = source.getPointData().getScalars() || source.getPointData().getArrays()[0]; const dataRange = dataArray.getRange(); global.RenderVtk.dataRange = dataRange; const dataExtent = source.getExtent(); const colorsBar = []; global.RenderVtk.dataExtent = dataExtent; const lookupTable = vtkColorTransferFunction.newInstance(); const piecewiseFunction = vtkPiecewiseFunction.newInstance(); const rangeBase = (dataRange[1] - dataRange[0]) / (config.color.length - 1); const colorMin = config.showRangeStart * (config.scaleBase || 1); const colorMax = config.showRangeEnd * (config.scaleBase || 1); for (let index = 0; index &amp;lt; config.color.length; index++) { let color = config.color[index]; const value = dataRange[0] + rangeBase * index; if (value &amp;gt; colorMax) { color = config.color[config.color.length - 1]; } if (value &amp;lt; colorMin) { color = config.color[0]; } const { r, g, b } = d3.rgb(color); colorsBar.push(color); lookupTable.addRGBPoint(dataRange[0] + rangeBase * index, r / 255, g / 255, b / 255); } actor.setMapper(mapper); mapper.setInputData(source); const sampleDistance = 0.7 * Math.sqrt( source .getSpacing() .map(v =&amp;gt; v * v) .reduce((a, b) =&amp;gt; a + b, 0) ); mapper.setSampleDistance(sampleDistance); let ComponentsNum = source.getPointData().getScalars().getNumberOfComponents(); if (ComponentsNum &amp;gt; 4) { ComponentsNum = 4; } source.getPointData().getScalars().setNumberOfComponents(ComponentsNum); global.RenderVtk.source = source; actor.getProperty().setRGBTransferFunction(0, lookupTable); for (let index = 1; index &amp;lt; ComponentsNum; index++) { actor.getProperty().setScalarOpacity(index, piecewiseFunction); } actor.getProperty().setInterpolationTypeToLinear(); actor .getProperty() .setScalarOpacityUnitDistance( 0, vtkBoundingBox.getDiagonalLength(source.getBounds()) / Math.max(...source.getDimensions()) ); actor.getProperty().setUseGradientOpacity(0, true); actor.getProperty().setGradientOpacityMinimumOpacity(0, config.colorOpacity / 100); actor.getProperty().setGradientOpacityMaximumOpacity(0, config.colorOpacity / 100); actor.getProperty().setAmbient(0.2); actor.getProperty().setDiffuse(0.7); actor.getProperty().setSpecular(0.3); actor.getProperty().setSpecularPower(8.0); }</code></pre>

页面列表

ITEM_HTML