/** * Copyright 1993-2015 NVIDIA Corporation. All rights reserved. * * Please refer to the NVIDIA end user license agreement (EULA) associated * with this source code for terms and conditions that govern your use of * this software. Any use, reproduction, disclosure, or distribution of * this software and related documentation outside the terms of the EULA * is strictly prohibited. * */ /** * CUDA 3D Volume Filtering sample * * This sample loads a 3D volume from disk and displays it using * ray marching and 3D textures. * * Note - this is intended to be an example of using 3D textures * in CUDA, not an optimized volume renderer. * * Changes * sgg 22/3/2010 * - updated to use texture for display instead of glDrawPixels. * - changed to render from front-to-back rather than back-to-front. */ // OpenGL Graphics includes #include #if defined (__APPLE__) || defined(MACOSX) #pragma clang diagnostic ignored "-Wdeprecated-declarations" #include #ifndef glutCloseFunc #define glutCloseFunc glutWMCloseFunc #endif #else #include #endif // CUDA Runtime and Interop #include #include // Helper functions #include #include // CUDA utilities and system includes #include typedef unsigned int uint; typedef unsigned char uchar; #define MAX_EPSILON_ERROR 5.00f #define THRESHOLD 0.30f const char *sSDKsample = "CUDA 3D Volume Filtering"; #include "volume.h" #include "volumeFilter.h" #include "volumeRender.h" const char *volumeFilename = "Bucky.raw"; cudaExtent volumeSize = make_cudaExtent(32, 32, 32); uint width = 512, height = 512; dim3 blockSize(16, 16); dim3 gridSize; float3 viewRotation; float3 viewTranslation = make_float3(0.0, 0.0, -4.0f); float invViewMatrix[12]; float density = 0.05f; float brightness = 1.0f; float transferOffset = 0.0f; float transferScale = 1.0f; bool linearFiltering = true; bool preIntegrated = true; StopWatchInterface *animationTimer = NULL; float filterFactor = 0.0f; bool filterAnimation = true; int filterIterations = 2; float filterTimeScale = 0.001f; float filterBias = 0.0f; float4 filterWeights[3*3*3]; Volume volumeOriginal; Volume volumeFilter0; Volume volumeFilter1; GLuint pbo = 0; // OpenGL pixel buffer object GLuint volumeTex = 0; // OpenGL texture object struct cudaGraphicsResource *cuda_pbo_resource; // CUDA Graphics Resource (to transfer PBO) StopWatchInterface *timer = 0; // Auto-Verification Code const int frameCheckNumber = 2; int fpsCount = 0; // FPS count for averaging int fpsLimit = 1; // FPS limit for sampling int g_Index = 0; unsigned int frameCount = 0; unsigned int g_TotalErrors = 0; int *pArgc; char **pArgv; #define MAX(a,b) ((a > b) ? a : b) ////////////////////////////////////////////////////////////////////////// // QA RELATED void computeFPS() { frameCount++; fpsCount++; if (fpsCount == fpsLimit) { char fps[256]; float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f); sprintf(fps, "CUDA 3D Volume Filtering: %3.1f fps", ifps); glutSetWindowTitle(fps); fpsCount = 0; fpsLimit = ftoi(MAX(1.f, ifps)); sdkResetTimer(&timer); } } ////////////////////////////////////////////////////////////////////////// // 3D FILTER static float filteroffsets[3*3*3][3] = { {-1,-1,-1},{ 0,-1,-1},{ 1,-1,-1}, {-1, 0,-1},{ 0, 0,-1},{ 1, 0,-1}, {-1, 1,-1},{ 0, 1,-1},{ 1, 1,-1}, {-1,-1, 0},{ 0,-1, 0},{ 1,-1, 0}, {-1, 0, 0},{ 0, 0, 0},{ 1, 0, 0}, {-1, 1, 0},{ 0, 1, 0},{ 1, 1, 0}, {-1,-1, 1},{ 0,-1, 1},{ 1,-1, 1}, {-1, 0, 1},{ 0, 0, 1},{ 1, 0, 1}, {-1, 1, 1},{ 0, 1, 1},{ 1, 1, 1}, }; static float filterblur[3*3*3] = { 0,1,0, 1,2,1, 0,1,0, 1,2,1, 2,4,2, 1,2,1, 0,1,0, 1,2,1, 0,1,0, }; static float filtersharpen[3*3*3] = { 0,0,0, 0,-2,0, 0,0,0, 0,-2,0, -2,15,-2, 0,-2,0, 0,0,0, 0,-2,0, 0,0,0, }; static float filterpassthru[3*3*3] = { 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,1,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, }; void FilterKernel_init() { float sumblur = 0.0f; float sumsharpen = 0.0f; for (int i = 0; i < 3*3*3; i++) { sumblur += filterblur[i]; sumsharpen += filtersharpen[i]; } for (int i = 0; i < 3*3*3; i++) { filterblur[i] /= sumblur; filtersharpen[i] /= sumsharpen; filterWeights[i].x = filteroffsets[i][0]; filterWeights[i].y = filteroffsets[i][1]; filterWeights[i].z = filteroffsets[i][2]; } } void FilterKernel_update(float blurfactor) { if (blurfactor > 0.0f) { for (int i = 0; i < 3*3*3; i++) { filterWeights[i].w = filterblur[i] * blurfactor + filterpassthru[i] * (1.0f - blurfactor); } } else { blurfactor = -blurfactor; for (int i = 0; i < 3*3*3; i++) { filterWeights[i].w = filtersharpen[i] * blurfactor + filterpassthru[i] * (1.0f - blurfactor); } } } void filter() { if (filterAnimation) { filterFactor = cosf(sdkGetTimerValue(&animationTimer) * filterTimeScale); } FilterKernel_update(filterFactor); Volume *volumeRender = VolumeFilter_runFilter(&volumeOriginal,&volumeFilter0,&volumeFilter1, filterIterations, 3*3*3,filterWeights,filterBias); } ////////////////////////////////////////////////////////////////////////// // RENDERING // render image using CUDA void render() { VolumeRender_copyInvViewMatrix(invViewMatrix, sizeof(float4)*3); // map PBO to get CUDA device pointer uint *d_output; // map PBO to get CUDA device pointer checkCudaErrors(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0)); size_t num_bytes; checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&d_output, &num_bytes, cuda_pbo_resource)); //printf("CUDA mapped PBO: May access %ld bytes\n", num_bytes); // clear image checkCudaErrors(cudaMemset(d_output, 0, width*height*4)); // call CUDA kernel, writing results to PBO VolumeRender_render(gridSize, blockSize, d_output, width, height, density, brightness, transferOffset, transferScale, volumeOriginal.volumeTex); getLastCudaError("render kernel failed"); checkCudaErrors(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0)); } // display results using OpenGL (called by GLUT) void display() { sdkStartTimer(&timer); // use OpenGL to build view matrix GLfloat modelView[16]; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glRotatef(-viewRotation.x, 1.0, 0.0, 0.0); glRotatef(-viewRotation.y, 0.0, 1.0, 0.0); glTranslatef(-viewTranslation.x, -viewTranslation.y, -viewTranslation.z); glGetFloatv(GL_MODELVIEW_MATRIX, modelView); glPopMatrix(); invViewMatrix[0] = modelView[0]; invViewMatrix[1] = modelView[4]; invViewMatrix[2] = modelView[8]; invViewMatrix[3] = modelView[12]; invViewMatrix[4] = modelView[1]; invViewMatrix[5] = modelView[5]; invViewMatrix[6] = modelView[9]; invViewMatrix[7] = modelView[13]; invViewMatrix[8] = modelView[2]; invViewMatrix[9] = modelView[6]; invViewMatrix[10] = modelView[10]; invViewMatrix[11] = modelView[14]; filter(); render(); // display results glClear(GL_COLOR_BUFFER_BIT); // draw image from PBO glDisable(GL_DEPTH_TEST); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // draw using texture // copy from pbo to texture glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); glBindTexture(GL_TEXTURE_2D, volumeTex); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); // draw textured quad glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(1, 0); glVertex2f(1, 0); glTexCoord2f(1, 1); glVertex2f(1, 1); glTexCoord2f(0, 1); glVertex2f(0, 1); glEnd(); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); glutSwapBuffers(); glutReportErrors(); sdkStopTimer(&timer); computeFPS(); } void idle() { glutPostRedisplay(); } ////////////////////////////////////////////////////////////////////////// // LOGIC void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: #if defined (__APPLE__) || defined(MACOSX) exit(EXIT_SUCCESS); #else glutDestroyWindow(glutGetWindow()); return; #endif break; case ' ': filterAnimation = !filterAnimation; if (!filterAnimation) { sdkStopTimer(&animationTimer); } else { sdkStartTimer(&animationTimer); } break; case 'f': linearFiltering = !linearFiltering; VolumeRender_setTextureFilterMode(linearFiltering, &volumeOriginal); break; case 'p': preIntegrated = !preIntegrated; VolumeRender_setPreIntegrated(preIntegrated); break; case '+': density += 0.01f; break; case '-': density -= 0.01f; break; case ']': brightness += 0.1f; break; case '[': brightness -= 0.1f; break; case ';': transferOffset += 0.01f; break; case '\'': transferOffset -= 0.01f; break; case '.': transferScale += 0.01f; break; case ',': transferScale -= 0.01f; break; default: break; } printf("density = %.2f, brightness = %.2f, transferOffset = %.2f, transferScale = %.2f\n", density, brightness, transferOffset, transferScale); glutPostRedisplay(); } int ox, oy; int buttonState = 0; void mouse(int button, int state, int x, int y) { if (state == GLUT_DOWN) { buttonState |= 1<\n"); printf("\t\t-file = filename.raw (volume file for input)\n\n"); printf("\t\t-size = 64 (volume size, isotropic)\n\n"); printf("\t\t-xsize = 128 (volume size, anisotropic)\n\n"); printf("\t\t-ysize = 128 (volume size, anisotropic)\n\n"); printf("\t\t-zsize = 32 (volume size, anisotropic)\n\n"); } int main(int argc, char **argv) { pArgc = &argc; pArgv = argv; char *ref_file = NULL; #if defined(__linux__) setenv ("DISPLAY", ":0", 0); #endif printf("%s Starting...\n\n", sSDKsample); //start logs if (checkCmdLineFlag(argc, (const char **)argv, "help")) { printHelp(); exit(EXIT_SUCCESS); } if (checkCmdLineFlag(argc, (const char **)argv, "file")) { fpsLimit = frameCheckNumber; getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file); } int device = findCudaDevice(argc, (const char **)argv); if (!ref_file) { initGL(&argc, argv); } // load volume data initData(argc, argv); printf( "Press \n" " 'SPACE' to toggle animation\n" " 'p' to toggle pre-integrated transfer function\n" " '+' and '-' to change density (0.01 increments)\n" " ']' and '[' to change brightness\n" " ';' and ''' to modify transfer function offset\n" " '.' and ',' to modify transfer function scale\n\n"); if (ref_file) { runSingleTest(ref_file, argv[0]); } else { // This is the normal rendering path for VolumeRender glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(motion); glutReshapeFunc(reshape); glutIdleFunc(idle); initPixelBuffer(); #if defined (__APPLE__) || defined(MACOSX) atexit(cleanup); #else glutCloseFunc(cleanup); #endif glutMainLoop(); } }