April 2013

Real time visualization of 3D vector field with CUDA

2 Prerequisites

2.1 Dataset

2.2 Loading input

Code listing 1: NRRD header from the Large input file.
1
2
3
4
5
6
7
8
9
NRRD0001
type: float
dimension: 4
sizes: 3 800 400 300
spacings: 1 1.0012515783 1.0025062561 0.50167226791
axis mins: 0 -150 -200 -50
labels: "Vx;Vy;Vz" "x" "y" "z"
endian: little
encoding: raw
Code listing 2: Reading raw float vectors from binary stream
1
2
3
4
5
6
7
8
9
10
11
12
13
std::ifstream inputStream(filePath, std::ios::binary);
// Reading of header information skipped.
// ... float3 size initialized with size of VF
size_t totalSize = size.x * size.y * size.z;
float4* data = new float4[totalSize];
 
for (size_t i = 0; i < totalSize; ++i) {
float4* f4Ptr = &(data[i]);
// Read x, y, z.
inputStream.read((char*)f4Ptr, sizeof(float3));
// Compute magnitude as w.
curr->w = std::sqrtf(f4Ptr->x * f4Ptr->x + f4Ptr->y * f4Ptr->y + f4Ptr->z * f4Ptr->z);
}

2.3 Fast reading of volumetric data using CUDA

Code listing 3: Initialization of vector field in CUDA as 3D texture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
texture<float4, cudaTextureType3D, cudaReadModeElementType> vectorFieldTex;
cudaArray* d_volumeArray = nullptr;
 
void initCuda(const float4* h_volume, cudaExtent volumeSize) {
// Allocate 3D array.
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float4>();
cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize);
 
// Copy data to 3D array using pitched ptr.
cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr = make_cudaPitchedPtr((void*)h_volume,
volumeSize.width * sizeof(float4), volumeSize.width, volumeSize.height);
copyParams.dstArray = d_volumeArray;
copyParams.extent = volumeSize;
copyParams.kind = cudaMemcpyHostToDevice;
 
// Set texture parameters.
vectorFieldTex.normalized = false;
vectorFieldTex.filterMode = cudaFilterModeLinear;
vectorFieldTex.addressMode[0] = cudaAddressModeClamp;
vectorFieldTex.addressMode[1] = cudaAddressModeClamp;
vectorFieldTex.addressMode[2] = cudaAddressModeClamp;
 
// Bind 3D array to 3D texture.
cudaBindTextureToArray(vectorFieldTex, d_volumeArray, channelDesc);
}

2.4 Color gradient for magnitude visualization

  • Color gradient used for visualization of vector magnitudes
    Color gradient used for visualization of vector magnitudes

2.5 OpenGL CUDA interoperability

Code listing 4: Initialization of shared VBO between CUDA and OpenGL.
1
2
3
4
5
6
7
8
9
cudaError_t createCudaSharedVbo(GLuint* vbo, GLenum target, uint size,
cudaGraphicsResource** cudaResource) {
 
glGenBuffers(1, vbo);
glBindBuffer(target, *vbo);
glBufferData(target, size, 0, GL_DYNAMIC_DRAW);
glBindBuffer(target, 0);
return cudaGraphicsGLRegisterBuffer(cudaResource, *vbo, cudaGraphicsMapFlagsNone);
}
Code listing 5: Simplified code of usage of shared VBOs to generate and render glyph lines without GPU→CPU memory transfer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void allocate() {
createCudaSharedVbo(&m_verticesVbo, GL_ARRAY_BUFFER,
verticesCount * sizeof(float3), &m_cudaVerticesVboResource);
createCudaSharedVbo(&m_colorsVbo, GL_ARRAY_BUFFER,
verticesCount * sizeof(float3), &m_cudaColorsVboResource);
}
 
void recompute() {
size_t bytesCount;
float3* d_glyphs;
cudaGraphicsMapResources(1, &m_cudaVerticesVboResource);
cudaGraphicsResourceGetMappedPointer((void**)&d_glyphs, &bytesCount,
m_cudaVerticesVboResource);
 
float3* d_colors;
cudaGraphicsMapResources(1, &m_cudaColorsVboResource);
cudaGraphicsResourceGetMappedPointer((void**)&d_colors, &bytesCount,
m_cudaColorsVboResource);
 
runCudaKernel(..., d_glyphs, d_colors, m_glyphsCount, ...);
 
cudaGraphicsUnmapResources(1, &m_cudaColorsVboResource);
cudaGraphicsUnmapResources(1, &m_cudaVerticesVboResource);
}
 
void displayCallback() {
glBindBuffer(GL_ARRAY_BUFFER, m_verticesVbo);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, NULL);
 
glBindBuffer(GL_ARRAY_BUFFER, m_colorsVbo);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3, GL_FLOAT, 0, NULL);
 
glDrawArrays(GL_LINES, 0, 2 * m_glyphsCount.x * m_glyphsCount.y);
}