/* * 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. * */ // OpenGL Graphics includes #include #if defined (__APPLE__) || defined(MACOSX) #pragma clang diagnostic ignored "-Wdeprecated-declarations" #include #else #include #endif // CUDA Library Headers #include #include // CUDA utilities and system includes #include #include // System includes #include #include #include #include // Includes #include "rng.h" // standard utility and system includes #include // SDK information static const char *printfFile = "randomFog.txt"; // RNG instance RNG *g_pRng = NULL; // CheckRender instance (for QA) CheckRender *g_pCheckRender = NULL; // Simple struct which contains the position and color of a vertex struct SVertex { GLfloat x,y,z; GLfloat r,g,b; }; // Data for the vertices SVertex *g_pVertices = NULL; int g_nVertices; // Size of the vertex array int g_nVerticesPopulated; // Number currently populated // Control the randomness int nSkip1 = 0; // Number of samples to discard between x,y int nSkip2 = 0; // Number of samples to discard between y,z int nSkip3 = 0; // Number of samples to discard between z,x // Control the display enum Shape_t {Sphere, SphericalShell, Cube, Plane}; Shape_t g_currentShape = Sphere; bool g_bShowAxes = true; bool g_bTenXZoom = false; bool g_bAutoRotate = true; int g_lastShapeX = 1024; int g_lastShapeY = 1024; float g_xRotated = 0.0f; float g_yRotated = 0.0f; const float PI = 3.14159265359f; void createCube(void) { int startVertex = 0; for (int i = startVertex ; i < g_nVerticesPopulated ; i++) { g_pVertices[i].x = (g_pRng->getNextU01() - .5f) * 2; for (int j = 0 ; j < nSkip1 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].y = (g_pRng->getNextU01() - .5f) * 2; for (int j = 0 ; j < nSkip2 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].z = (g_pRng->getNextU01() - .5f) * 2; for (int j = 0 ; j < nSkip3 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].r = 1.0f; g_pVertices[i].g = 1.0f; g_pVertices[i].b = 1.0f; } } void createPlane(void) { int startVertex = 0; for (int i = startVertex ; i < g_nVerticesPopulated ; i++) { g_pVertices[i].x = (g_pRng->getNextU01() - .5f) * 2; for (int j = 0 ; j < nSkip1 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].y = (g_pRng->getNextU01() - .5f) * 2; for (int j = 0 ; j < nSkip2 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].z = 0.0f; g_pVertices[i].r = 1.0f; g_pVertices[i].g = 1.0f; g_pVertices[i].b = 1.0f; } } void createSphere(void) { int startVertex = 0; for (int i = startVertex ; i < g_nVerticesPopulated ; i++) { float r; float rho; float theta; if (g_currentShape == Sphere) { r = g_pRng->getNextU01(); r = powf(r, 1.f/3.f); for (int j = 0 ; j < nSkip3 ; j++) { g_pRng->getNextU01(); } } else { r = 1.0f; } rho = g_pRng->getNextU01() * PI * 2.0f; for (int j= 0 ; j < nSkip1 ; j++) { g_pRng->getNextU01(); } theta = (g_pRng->getNextU01() * 2.0f) - 1.0f; theta = asin(theta); for (int j = 0 ; j < nSkip2 ; j++) { g_pRng->getNextU01(); } g_pVertices[i].x = r * fabs(cos(theta)) * cos(rho); g_pVertices[i].y = r * fabs(cos(theta)) * sin(rho); g_pVertices[i].z = r * sin(theta); g_pVertices[i].r = 1.0f; g_pVertices[i].g = 1.0f; g_pVertices[i].b = 1.0f; } } void createAxes(void) { // z axis: g_pVertices[200000].x = 0.0f; g_pVertices[200000].y = 0.0f; g_pVertices[200000].z = -1.5f; g_pVertices[200001].x = 0.0f; g_pVertices[200001].y = 0.0f; g_pVertices[200001].z = 1.5f; g_pVertices[200000].r = 1.0f; g_pVertices[200000].g = 0.0f; g_pVertices[200000].b = 0.0f; g_pVertices[200001].r = 0.0f; g_pVertices[200001].g = 1.0f; g_pVertices[200001].b = 1.0f; // y axis: g_pVertices[200002].x = 0.0f; g_pVertices[200002].y = -1.5f; g_pVertices[200002].z = 0.0f; g_pVertices[200003].x = 0.0f; g_pVertices[200003].y = 1.5f; g_pVertices[200003].z = 0.0f; g_pVertices[200002].r = 0.0f; g_pVertices[200002].g = 1.0f; g_pVertices[200002].b = 0.0f; g_pVertices[200003].r = 1.0f; g_pVertices[200003].g = 0.0f; g_pVertices[200003].b = 1.0f; // x axis: g_pVertices[200004].x = -1.5f; g_pVertices[200004].y = 0.0f; g_pVertices[200004].z = 0.0f; g_pVertices[200005].x = 1.5f; g_pVertices[200005].y = 0.0f; g_pVertices[200005].z = 0.0f; g_pVertices[200004].r = 0.0f; g_pVertices[200004].g = 0.0f; g_pVertices[200004].b = 1.0f; g_pVertices[200005].r = 1.0f; g_pVertices[200005].g = 1.0f; g_pVertices[200005].b = 0.0f; } void drawPoints(void) { if (g_bShowAxes) { glDrawArrays(GL_LINE_STRIP, 200000, 2); glDrawArrays(GL_LINE_STRIP, 200002, 2); glDrawArrays(GL_LINE_STRIP, 200004, 2); } glDrawArrays(GL_POINTS, 0, g_nVerticesPopulated); } void drawText(void) { using std::string; using std::stringstream; glPushMatrix(); glLoadIdentity(); glRasterPos2f(-1.2f, 1.2f); string infoString; stringstream ss; g_pRng->getInfoString(infoString); ss << " skip1=" << nSkip1; ss << " skip2=" << nSkip2; ss << " skip3=" << nSkip3; ss << " points=" << g_nVerticesPopulated; infoString.append(ss.str()); for (unsigned int i = 0 ; i < infoString.size() ; i++) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, infoString[i]); } glPopMatrix(); } void reshape(int x, int y) { float xScale; float yScale; g_lastShapeX = x; g_lastShapeY = y; // Check if shape is visible if (x == 0 || y == 0) { return; } // Set a new projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Adjust fit if (y > x) { xScale = 1.0f; yScale = (float)y / x; } else { xScale = (float)x / y; yScale = 1.0f; } // Angle of view:40 degrees // Near clipping plane distance: 10.0 (default) // Far clipping plane distance: 10.0 (default) if (g_bTenXZoom) { glOrtho(-.15f * xScale, .15f * xScale, -.15f * yScale, .15f * yScale, -5.0f, 5.0f); } else { glOrtho(-1.5f * xScale, 1.5f * xScale, -1.5f * yScale, 1.5f * yScale, -10.0f, 10.0f); } // Use the whole window for rendering glViewport(0, 0, x, y); glMatrixMode(GL_MODELVIEW); } void display(void) { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -4.0f); glRotatef(g_yRotated, 0.0f, 1.0f, 0.0f); glRotatef(g_xRotated, 1.0f, 0.0f, 0.0f); drawPoints(); drawText(); glFlush(); glutSwapBuffers(); } void idle(void) { if (g_bAutoRotate) { g_yRotated += 0.1f; if (g_yRotated >= 360.0f) { g_yRotated -= 360.0f; } g_xRotated += 0.05f; if (g_xRotated >= 360.0f) { g_xRotated -= 360.0f; } display(); } } void reCreate(void) { switch (g_currentShape) { case Sphere: case SphericalShell: createSphere(); break; case Cube: createCube(); break; default: createPlane(); } display(); } void cleanup(int code) { if (g_pRng) { delete g_pRng; g_pRng = NULL; } if (g_pVertices) { delete [] g_pVertices; g_pVertices = NULL; } if (g_pCheckRender) { delete g_pCheckRender; g_pCheckRender = NULL; } exit(code); } void glutClose() { cleanup(EXIT_SUCCESS); } void keyboard(unsigned char key, int x, int y) { switch (key) { // Select shape case 's': case 'S': g_currentShape = Sphere; createSphere(); display(); break; case 'e': case 'E': g_currentShape = SphericalShell; createSphere(); display(); break; case 'b': case 'B': g_currentShape = Cube; createCube(); display(); break; case 'p': case 'P': g_currentShape = Plane; createPlane(); display(); break; // Rotation case 'a': case 'A': g_bAutoRotate = !g_bAutoRotate; break; case 'i': case 'I': g_xRotated -= 1.0f; if (g_xRotated <= 0.0f) { g_xRotated += 360.0f; } display(); break; case ',': g_xRotated += 1.0f; if (g_xRotated >= 360.0f) { g_xRotated -= 360.0f; } display(); break; case 'j': case 'J': g_yRotated -= 1.0f; if (g_yRotated <= 0.0f) { g_yRotated += 360.0f; } display(); break; case 'l': case 'L': g_yRotated += 1.0f; if (g_yRotated >= 360.0f) { g_yRotated -= 360.0f; } display(); break; // Zoom case 't': case 'T': g_bTenXZoom = !g_bTenXZoom; reshape(g_lastShapeX, g_lastShapeY); reCreate(); break; // Axes case 'z': case 'Z': g_bShowAxes = !g_bShowAxes; reCreate(); break; // RNG case 'x': case 'X': g_pRng->selectRng(RNG::Pseudo); reCreate(); break; case 'c': case 'C': g_pRng->selectRng(RNG::Quasi); reCreate(); break; case 'v': case 'V': g_pRng->selectRng(RNG::ScrambledQuasi); reCreate(); break; case 'r': case 'R': g_pRng->resetSeed(); reCreate(); break; case ']': g_pRng->incrementDimensions(); reCreate(); break; case '[': g_pRng->resetDimensions(); reCreate(); break; case '1': nSkip1++; reCreate(); break; case '2': nSkip2++; reCreate(); break; case '3': nSkip3++; reCreate(); break; case '!': nSkip1 = 0; nSkip2 = 0; nSkip3 = 0; reCreate(); break; // Number of vertices case '+': g_nVerticesPopulated += 8000; if (g_nVerticesPopulated > g_nVertices) { g_nVerticesPopulated = g_nVertices; } reCreate(); break; case '-': g_nVerticesPopulated -= 8000; if (g_nVerticesPopulated < 8000) { g_nVerticesPopulated = 8000; } reCreate(); break; // Quit case 27: case 'q': case 'Q': #if defined(__APPLE__) || defined(MACOSX) exit(EXIT_SUCCESS); #else glutDestroyWindow(glutGetWindow()); return; #endif } } void showHelp(void) { using std::stringstream; using std::setw; using std::left; stringstream ss; ss << "\nRandom number visualization\n\n"; ss << "On creation, randomFog generates 200,000 random coordinates in spherical " << "coordinate space (radius, angle rho, angle theta) with curand's XORWOW algorithm. " << "The coordinates are normalized for a uniform distribution through the sphere.\n\n"; ss << "The X axis is drawn with blue in the negative direction and yellow positive.\n" << "The Y axis is drawn with green in the negative direction and magenta positive.\n" << "The Z axis is drawn with red in the negative direction and cyan positive.\n\n"; ss << "The following keys can be used to control the output:\n\n"; ss << left; ss << "\t" << setw(10) << "s" << "Generate a new set of random numbers and display as spherical coordinates (Sphere)\n"; ss << "\t" << setw(10) << "e" << "Generate a new set of random numbers and display on a spherical surface (shEll)\n"; ss << "\t" << setw(10) << "b" << "Generate a new set of random numbers and display as cartesian coordinates (cuBe/Box)\n"; ss << "\t" << setw(10) << "p" << "Generate a new set of random numbers and display on a cartesian plane (Plane)\n\n"; ss << "\t" << setw(10) << "i,l,j" << "Rotate the negative Z-axis up, right, down and left respectively\n"; ss << "\t" << setw(10) << "a" << "Toggle auto-rotation\n"; ss << "\t" << setw(10) << "t" << "Toggle 10x zoom\n"; ss << "\t" << setw(10) << "z" << "Toggle axes display\n\n"; ss << "\t" << setw(10) << "x" << "Select XORWOW generator (default)\n"; ss << "\t" << setw(10) << "c" << "Select Sobol' generator\n"; ss << "\t" << setw(10) << "v" << "Select scrambled Sobol' generator\n"; ss << "\t" << setw(10) << "r" << "Reset XORWOW (i.e. reset to initial seed) and regenerate\n"; ss << "\t" << setw(10) << "]" << "Increment the number of Sobol' dimensions and regenerate\n"; ss << "\t" << setw(10) << "[" << "Reset the number of Sobol' dimensions to 1 and regenerate\n\n"; ss << "\t" << setw(10) << "+" << "Increment the number of displayed points by 8,000 (up to maximum 200,000)\n"; ss << "\t" << setw(10) << "-" << "Decrement the number of displayed points by 8,000 (down to minimum 8,000)\n\n"; ss << "\t" << setw(10) << "q/[ESC]" << "Quit the application.\n\n"; puts(ss.str().c_str()); } int main(int argc, char **argv) { using std::runtime_error; try { bool bQA = false; // Open the log file printf("Random Fog\n"); printf("==========\n\n"); // Check QA mode if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) { bQA = true; findCudaDevice(argc, (const char **)argv); g_pCheckRender = new CheckBackBuffer(g_lastShapeX, g_lastShapeY, 4, false); } else { #if defined(__linux__) setenv ("DISPLAY", ":0", 0); #endif // Initialize GL glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //TODO use width/height? glutInitWindowSize(1000, 1000); // Create a window with rendering context and everything else we need glutCreateWindow("Random Fog"); if (!isGLVersionSupported(2, 0)) { fprintf(stderr, "This sample requires at least OpenGL 2.0\n"); exit(EXIT_WAIVED); } // Select CUDA device with OpenGL interoperability findCudaDevice(argc, (const char **)argv); } // Create vertices g_nVertices = 200000; g_nVerticesPopulated = 200000; g_pVertices = new SVertex[g_nVertices + 6]; // Setup the random number generators g_pRng = new RNG(12345, 1, 100000); printf("CURAND initialized\n"); // Compute the initial vertices and indices, starting in spherical mode createSphere(); createAxes(); showHelp(); if (bQA) { g_pCheckRender->setExecPath(argv[0]); g_pCheckRender->dumpBin(g_pVertices, g_nVerticesPopulated * sizeof(SVertex), "randomFog.bin"); if (g_pCheckRender->compareBin2BinFloat("randomFog.bin", "ref_randomFog.bin", g_nVerticesPopulated * sizeof(SVertex) / sizeof(float), 0.25f, 0.35f)) { cleanup(EXIT_SUCCESS); } else { cleanup(EXIT_FAILURE); } } else { // As we do not yet use a depth buffer, we cannot fill our sphere glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Enable the vertex array functionality: glEnableClientState(GL_VERTEX_ARRAY); // Enable the color array functionality (so we can specify a color for each vertex) glEnableClientState(GL_COLOR_ARRAY); // Pass the vertex pointer: glVertexPointer(3, //3 components per vertex (x,y,z) GL_FLOAT, sizeof(SVertex), g_pVertices); // Pass the color pointer glColorPointer(3, //3 components per vertex (r,g,b) GL_FLOAT, sizeof(SVertex), &g_pVertices[0].r); //Pointer to the first color // Point size for point mode glPointSize(1.0f); glLineWidth(2.0f); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Notify glut which messages we require: glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutIdleFunc(idle); #if defined (__APPLE__) || defined(MACOSX) atexit(glutClose); #else glutCloseFunc(glutClose); #endif // Let's get started! glutMainLoop(); } } catch (runtime_error &e) { printf("runtime error (%s)\n", e.what()); } exit(EXIT_SUCCESS); }