Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Jika Anda baru menggunakan penyajian volume, kami sarankan Anda membaca ringkasan kami.
Mewakili Tekstur 3D
Pada 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); }
}
/* ... */
}
Pada 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];
}
Bayangan dan Gradien
Cara menaungi volume, seperti MRI, untuk visualisasi yang berguna. Metode utamanya adalah memiliki 'jendela intensitas' (min dan maks) yang ingin Anda lihat intensitasnya, dan cukup skalakan ke ruang tersebut untuk melihat intensitas hitam dan putih. 'tanjakan warna' kemudian dapat diterapkan ke nilai dalam rentang tersebut, dan disimpan sebagai tekstur, sehingga berbagai bagian spektrum intensitas dapat dibayangi warna yang berbeda:
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 ) );
}
Dalam banyak aplikasi kami, kami menyimpan dalam volume kami baik nilai intensitas mentah maupun 'indeks segmentasi' (untuk segmentasi bagian yang berbeda seperti kulit dan tulang; segmen ini dibuat oleh para ahli dalam alat khusus). Ini dapat dikombinasikan dengan pendekatan di atas untuk menempatkan warna yang berbeda, atau bahkan ramp warna yang berbeda untuk setiap indeks segmen:
// Change color to match segment index (fade each segment towards black):
color.rgb = SegmentColors[ segment_index ] * color.a; // brighter alpha gives brighter color
Pemotongan Volume dalam Shader
Langkah pertama yang bagus adalah membuat "bidang pemotongan" yang dapat bergerak melalui volume, 'mengirisnya', dan bagaimana nilai pemindaian di setiap titik. Ini mengasumsikan bahwa ada kubus 'VolumeSpace', yang mewakili di mana volume berada di ruang dunia, yang dapat digunakan sebagai referensi untuk menempatkan poin:
// 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 ) );
Pelacakan Volume dalam Shader
Cara menggunakan GPU untuk melakukan pelacakan subvolume (berjalan beberapa voxel dalam, kemudian lapisan pada data dari belakang ke depan):
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 );
Penyajian Seluruh Volume
Memodifikasi kode subvolume di atas, kita mendapatkan:
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
Rendering Adegan Resolusi Campuran
Cara merender bagian dari adegan dengan resolusi rendah dan menempatkannya kembali:
- Siapkan dua kamera di luar layar, satu untuk mengikuti setiap mata yang memperbarui setiap bingkai
- Siapkan dua target render resolusi rendah (yaitu, masing-masing 200x200) yang dirender kamera
- Menyiapkan quad yang bergerak di depan pengguna
Setiap Bingkai:
- Gambar target render untuk setiap mata pada resolusi rendah (data volume, shader mahal, dan sebagainya)
- Gambar adegan secara normal sebagai resolusi penuh (jala, UI, dan sebagainya)
- Gambar quad di depan pengguna, di atas adegan, dan proyeksikan render rendah ke dalamnya
- Hasil: kombinasi visual elemen resolusi penuh dengan data volume dengan resolusi rendah tetapi kepadatan tinggi