ボリュームの表示Volume rendering

医療 MRI またはボリュームをエンジニア リングを参照してください。 Wikipedia でボリュームのレンダリングします。For medical MRI or engineering volumes, see Volume Rendering on Wikipedia. これらの '帯域幅消費型イメージ' には、不透明度と簡単に表現できないサーフェスとしてなど、ボリューム全体での色の豊富な情報が含まれて多角形のメッシュします。These 'volumetric images' contain rich information with opacity and color throughout the volume that cannot be easily expressed as surfaces such as polygonal meshes.

パフォーマンスを向上させる重要なソリューションKey solutions to improve performance

  1. 正しくありません。未熟なアプローチ。ボリューム全体を表示する、通常は遅く実行BAD: Naïve Approach: Show Whole Volume, generally runs too slowly
  2. よし:Cutting 平面。ボリュームの 1 つのスライスのみを表示します。GOOD: Cutting Plane: Show only a single slice of the volume
  3. よし:Cutting サブ ボリューム:ボリュームのいくつかのレイヤーのみを表示します。GOOD: Cutting Sub-Volume: Show only a few layers of the volume
  4. よし:ボリュームのレンダリングの解像度を下げる (' 混合解決シーンのレンダリング ' を参照してください)GOOD: Lower the resolution of the volume rendering (see 'Mixed Resolution Scene Rendering')

アプリケーションから、特定のフレームでは、画面に転送できる情報の特定の量だけが、これは、メモリの合計帯域幅。There is only a certain amount of information that can be transferred from the application to the screen in any particular frame, this is the total memory bandwidth. プレゼンテーションについては、そのデータを変換するために必要なすべての処理 (または '網掛け') も時間も必要です。Also any processing (or 'shading') required to transform that data for presentation also requires time. ボリュームのレンダリングを実施する際に、主な考慮がそのためには。The primary considerations when doing volume rendering are as such:

  • 画面幅 * 画面の高さ * 画面数 * ボリューム レイヤーで-こと-ピクセルの = 合計-ボリュームのサンプル-フレームごとScreen-Width * Screen-Height * Screen-Count * Volume-Layers-On-That-Pixel = Total-Volume-Samples-Per-Frame
  • 1028 * 720 * 2 * 256 = 378961920 (100%)(完全な res ボリューム: 多くのサンプル)1028 * 720 * 2 * 256 = 378961920 (100%) (full res volume: too many samples)
  • 1028 * 720 * 2 * 1 = 1480320 (完全 0.3%) (薄いスライス。1 ピクセルあたり 1 つのサンプルがスムーズに実行されます)1028 * 720 * 2 * 1 = 1480320 (0.3% of full) (thin slice: 1 sample per pixel, runs smoothly)
  • 1028 * 720 * 2 * 10 = 14803200 (完全 3.9%) (サブ ボリューム スライス。1 ピクセルあたり 10 個のサンプルでは、順調に実行、次の 3 d)1028 * 720 * 2 * 10 = 14803200 (3.9% of full) (sub-volume slice: 10 samples per pixel, runs fairly smoothly, looks 3d)
  • 200 * 200 * 2 * 256 = 20480000 (全 5%) (res の音量を下げる: ピクセルの数、フル ボリュームは、次の 3 d が少し blury)200 * 200 * 2 * 256 = 20480000 (5% of full) (lower res volume: fewer pixels, full volume, looks 3d but a bit blury)

3D テクスチャを表すRepresenting 3D Textures

CPU:On the CPU:

public struct Int3 { public int X, Y, Z; /* ... */ }
 public class VolumeHeader  {
   public readonly Int3 Size;
   public VolumeHeader(Int3 size) { this.Size = size;  }
   public int CubicToLinearIndex(Int3 index) {
     return index.X + (index.Y * (Size.X)) + (index.Z * (Size.X * Size.Y));
   }
   public Int3 LinearToCubicIndex(int linearIndex)
   {
     return new Int3((linearIndex / 1) % Size.X,
       (linearIndex / Size.X) % Size.Y,
       (linearIndex / (Size.X * Size.Y)) % Size.Z);
   }
   /* ... */
 }
 public class VolumeBuffer<T> {
   public readonly VolumeHeader Header;
   public readonly T[] DataArray;
   public T GetVoxel(Int3 pos)        {
     return this.DataArray[this.Header.CubicToLinearIndex(pos)];
   }
   public void SetVoxel(Int3 pos, T val)        {
     this.DataArray[this.Header.CubicToLinearIndex(pos)] = val;
   }
   public T this[Int3 pos] {
     get { return this.GetVoxel(pos); }
     set { this.SetVoxel(pos, value); }
   }
   /* ... */
 }

GPU:On the GPU:

float3 _VolBufferSize;
 int3 UnitVolumeToIntVolume(float3 coord) {
   return (int3)( coord * _VolBufferSize.xyz );
 }
 int IntVolumeToLinearIndex(int3 coord, int3 size) {
   return coord.x + ( coord.y * size.x ) + ( coord.z * ( size.x * size.y ) );
 }
 uniform StructuredBuffer<float> _VolBuffer;
 float SampleVol(float3 coord3 ) {
   int3 intIndex3 = UnitVolumeToIntVolume( coord3 );
   int index1D = IntVolumeToLinearIndex( intIndex3, _VolBufferSize.xyz);
   return __VolBuffer[index1D];
 }

網掛けとグラデーションShading and Gradients

わかりやすく視覚化の MRI などのボリュームの網掛けを表示する方法。How to shade an volume, such as MRI, for useful visualization. プライマリのメソッドが '強度ウィンドウ' (min と max)、強度を参照してくださいし、黒と白の濃さを表示するには、その領域に単に拡張することです。The primary method is to have an 'intensity window' (a min and max) that you want to see intensities within, and simply scale into that space to see the black and white intensity. 色パレットをその範囲内の値に適用し、および、テクスチャとして格納されるため、輝度スペクトルのさまざまな部分は網掛けのさまざまな色を指定できます。A 'color ramp' can then be applied to the values within that range, and stored as a texture, so that different parts of the intensity spectrum can be shaded different colors:

float4 ShadeVol( float intensity ) {
   float unitIntensity = saturate( intensity - IntensityMin / ( IntensityMax - IntensityMin ) );
   // Simple two point black and white intensity:
   color.rgba = unitIntensity;
   // Color ramp method:
   color.rgba = tex2d( ColorRampTexture, float2( unitIntensity, 0 ) );

多くの生の輝度値とセグメント化インデックスの両方に、ボリュームの保存、アプリケーション (など、さまざまな部分を分割するスキンとボーン、これらのセグメント通常によって作成されますの専用ツールの専門家)。In many our applications we store in our volume both a raw intensity value and a 'segmentation index' (to segment different parts such as skin and bone, these segments are generally created by experts in dedicated tools). これは、別の色、またはセグメントのインデックスごとに異なる色ごとの傾斜増加を配置する前に示した方法で結合できます。This can be combined with the approach above to put a different color, or even different color ramp for each segment index:

// Change color to match segment index (fade each segment towards black):
 color.rgb = SegmentColors[ segment_index ] * color.a; // brighter alpha gives brighter color

シェーダーでスライス ボリュームVolume Slicing in a Shader

優れた最初の手順が「スライス プレーン」を作成するには、ボリュームを移動することのできる 'スライス'、および各ポイントで、スキャンの値します。A great first step is to create a "slicing plane" that can move through the volume, 'slicing it', and how the scan values at each point. これにより、ボリュームが、ポイントを配置するための参照として使用できる、ワールド空間内にある表す 'VolumeSpace' キューブがあることを前提としています。This assumes that there is a 'VolumeSpace' cube, which represents where the volume is in world space, that can be used as a reference for placing the points:

// In the vertex shader:
 float4 worldPos = mul(_Object2World, float4(input.vertex.xyz, 1));
 float4 volSpace = mul(_WorldToVolume, float4(worldPos, 1));
// In the pixel shader:
 float4 color = ShadeVol( SampleVol( volSpace ) );

シェーダーでボリュームのトレースVolume Tracing in Shaders

GPU を使用して、サブのボリュームのトレース (歩くが、いくつか voxels ディープしレイヤー データ バックアップから最前面へ移動) を実行する方法。How to use the GPU to do sub-volume tracing (walks a few voxels deep then layers on the data from back to front):

float4 AlphaBlend(float4 dst, float4 src) {
   float4 res = (src * src.a) + (dst - dst * src.a);
   res.a = src.a + (dst.a - dst.a*src.a);
   return res;
 }
 float4 volTraceSubVolume(float3 objPosStart, float3 cameraPosVolSpace) {
   float maxDepth = 0.15; // depth in volume space, customize!!!
   float numLoops = 10; // can be 400 on nice PC
   float4 curColor = float4(0, 0, 0, 0);
   // Figure out front and back volume coords to walk through:
   float3 frontCoord = objPosStart;
   float3 backCoord = frontPos + (normalize(cameraPosVolSpace - objPosStart) * maxDepth);
   float3 stepCoord = (frontCoord - backCoord) / numLoops;
   float3 curCoord = backCoord;
   // Add per-pixel random offset, avoids layer aliasing:
   curCoord += stepCoord * RandomFromPositionFast(objPosStart);
   // Walk from back to front (to make front appear in-front of back):
   for (float i = 0; i < numLoops; i++) {
     float intensity = SampleVol(curCoord);
     float4 shaded = ShadeVol(intensity);
     curColor = AlphaBlend(curColor, shaded);
     curCoord += stepCoord;
   }
   return curColor;
 }
// In the vertex shader:
 float4 worldPos = mul(_Object2World, float4(input.vertex.xyz, 1));
 float4 volSpace = mul(_WorldToVolume, float4(worldPos.xyz, 1));
 float4 cameraInVolSpace = mul(_WorldToVolume, float4(_WorldSpaceCameraPos.xyz, 1));
// In the pixel shader:
 float4 color = volTraceSubVolume( volSpace, cameraInVolSpace );

ボリューム全体のレンダリングWhole Volume Rendering

上記のコードでサブ ボリュームの変更が表示されます。Modifying the sub-volume code above we get:

float4 volTraceSubVolume(float3 objPosStart, float3 cameraPosVolSpace) {
   float maxDepth = 1.73; // sqrt(3), max distance from point on cube to any other point on cube
   int maxSamples = 400; // just in case, keep this value within bounds
   // not shown: trim front and back positions to both be within the cube
   int distanceInVoxels = length(UnitVolumeToIntVolume(frontPos - backPos)); // measure distance in voxels
   int numLoops = min( distanceInVoxels, maxSamples ); // put a min on the voxels to sample

混合解決シーンのレンダリングMixed Resolution Scene Rendering

低解像度のシーンの一部をレンダリングし、元の場所に配置する方法。How to render a part of the scene with a low resolution and put it back in place:

  1. 次の各フレームを更新する目ごとに 1 つ、2 つのオフスクリーン カメラをセットアップします。Setup two off-screen cameras, one to follow each eye that update each frame
  2. 2 つの低解像度の設定ではレンダー ターゲット (たとえば 200 x 200 ごと) にカメラをレンダリングします。Setup two low-resolution render targets (say 200x200 each), that the cameras render into
  3. ユーザーの前に移動するクアッドをセットアップします。Setup a quad that moves in front of the user

各フレームには:Each Frame:

  1. 低解像度で目ごとにレンダー ターゲットの描画 (データのボリューム、高価なシェーダーなど)Draw the render targets for each eye at low-resolution (volume data, expensive shaders, etc.)
  2. フル解像度として通常シーンを描画 (メッシュ、UI など)。Draw the scene normally as full resolution (meshes, UI, etc.)
  3. シーンの上に、ユーザーの前に、クアッドを描画し、低解像度のレンダリングに投影します。Draw a quad in front of the user, over the scene, and project the low-res renders onto that.
  4. 低解像度が高密度のボリューム データを持つフル解像度の要素の結果: ビジュアルの組み合わせ。Result: visual combination of full-resolution elements with low-resolution but high-density volume data.