www.pudn.com > helloGPGPU_GLSL-1.0.2.zip > helloGPGPU_GLSL.cpp


//--------------------------------------------------------------------------- 
//                               www.GPGPU.org  
//                                Sample Code 
//--------------------------------------------------------------------------- 
// Copyright (c) 2004 Mark J. Harris and GPGPU.org 
// Copyright (c) 2004 3Dlabs Inc. Ltd. 
//--------------------------------------------------------------------------- 
// This software is provided 'as-is', without any express or implied 
// warranty. In no event will the authors be held liable for any 
// damages arising from the use of this software. 
// 
// Permission is granted to anyone to use this software for any 
// purpose, including commercial applications, and to alter it and 
// redistribute it freely, subject to the following restrictions: 
// 
// 1. The origin of this software must not be misrepresented; you 
//    must not claim that you wrote the original software. If you use 
//    this software in a product, an acknowledgment in the product 
//    documentation would be appreciated but is not required. 
// 
// 2. Altered source versions must be plainly marked as such, and 
//    must not be misrepresented as being the original software. 
// 
// 3. This notice may not be removed or altered from any source 
//    distribution. 
// 
//--------------------------------------------------------------------------- 
// Author: Mark Harris (harrism@gpgpu.org) - original helloGPGPU 
// Author: Mike Weiblen (mike.weiblen@3dlabs.com) - GLSL version 
//--------------------------------------------------------------------------- 
// GPGPU Lesson 0: "helloGPGPU_GLSL" (a GLSL version of "helloGPGPU") 
//--------------------------------------------------------------------------- 
// 
// GPGPU CONCEPTS Introduced:  
// 
//      1.) Texture = Array 
//      2.) Fragment Program = Computational Kernel. 
//      3.) One-to-one Pixel to Texel Mapping:  
//          a) Data-Dimensioned Viewport, and 
//          b) Orthographic Projection. 
//      4.) Viewport-Sized Quad = Data Stream Generator. 
//      5.) Copy To Texture = feedback. 
// 
//      For details of each of these concepts, see the explanations in the  
//      inline "GPGPU CONCEPT" comments in the code below. 
// 
// APPLICATION Demonstrated: A simple post-process edge detection filter.  
// 
//--------------------------------------------------------------------------- 
// Notes regarding this "helloGPGPU_GLSL" source code: 
// 
// This example was derived from the original "helloGPGPU.cpp" v1.0.1 
// by Mark J. Harris.  It demonstrates the same simple post-process edge 
// detection filter, but instead implemented using the OpenGL Shading Language 
// (also known as "GLSL"), an extension of the OpenGL v1.5 specification. 
// Because the GLSL compiler is an integral part of a vendor's OpenGL driver, 
// no additional GLSL development tools are required. 
// This example was developed/tested on 3Dlabs Wildcat Realizm. 
// 
// I intentionally minimized changes to the structure of the original code 
// to support a side-by-side comparison of the implementations. 
// 
// Thanks to Mark for making the original helloGPGPU example available! 
// 
// -- Mike Weiblen, May 2004 
// 
//  
// [MJH:] 
// This example has also been tested on NVIDIA GeForce FX and GeForce 6800 GPUs. 
// 
// Note that the example requires glew.h and glew32s.lib, available at  
// http://glew.sourceforge.net. 
// 
// Thanks to Mike for modifying the example.  I have actually changed my  
// original code to match; for example the inline Cg code instead of an  
// external file. 
// 
// -- Mark Harris, June 2004 
// 
// References: 
//    http://gpgpu.sourceforge.net/ 
//    http://glew.sourceforge.net/ 
//    http://www.xmission.com/~nate/glut.html 
//--------------------------------------------------------------------------- 
 
#include  
#include  
#include  
#define GLEW_STATIC 1 
#include  
#include  
 
// forward declarations 
class HelloGPGPU; 
void reshape(int w, int h); 
 
// globals 
HelloGPGPU  *g_pHello; 
 
 
// This shader performs a 9-tap Laplacian edge detection filter. 
// (converted from the separate "edges.cg" file to embedded GLSL string) 
static const char *edgeFragSource = { 
"uniform sampler2D texUnit;" 
"void main(void)" 
"{" 
"   const float offset = 1.0 / 512.0;" 
"   vec2 texCoord = gl_TexCoord[0].xy;" 
"   vec4 c  = texture2D(texUnit, texCoord);" 
"   vec4 bl = texture2D(texUnit, texCoord + vec2(-offset, -offset));" 
"   vec4 l  = texture2D(texUnit, texCoord + vec2(-offset,     0.0));" 
"   vec4 tl = texture2D(texUnit, texCoord + vec2(-offset,  offset));" 
"   vec4 t  = texture2D(texUnit, texCoord + vec2(    0.0,  offset));" 
"   vec4 ur = texture2D(texUnit, texCoord + vec2( offset,  offset));" 
"   vec4 r  = texture2D(texUnit, texCoord + vec2( offset,     0.0));" 
"   vec4 br = texture2D(texUnit, texCoord + vec2( offset,  offset));" 
"   vec4 b  = texture2D(texUnit, texCoord + vec2(    0.0, -offset));" 
"   gl_FragColor = 8.0 * (c + -0.125 * (bl + l + tl + t + ur + r + br + b));" 
"}" 
}; 
 
// This class encapsulates all of the GPGPU functionality of the example. 
class HelloGPGPU 
{ 
public: // methods 
    HelloGPGPU(int w, int h)  
    : _rAngle(0), 
      _iWidth(w), 
      _iHeight(h) 
    { 
        // Create a simple 2D texture.  This example does not use 
        // render to texture -- it just copies from the framebuffer to the 
        // texture. 
 
        // GPGPU CONCEPT 1: Texture = Array. 
        // Textures are the GPGPU equivalent of arrays in standard  
        // computation. Here we allocate a texture large enough to fit our 
        // data (which is arbitrary in this example). 
        glGenTextures(1, &_iTexture); 
        glBindTexture(GL_TEXTURE_2D, _iTexture); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _iWidth, _iHeight,  
                     0, GL_RGB, GL_FLOAT, 0); 
        
        // GPGPU CONCEPT 2: Fragment Program = Computational Kernel. 
        // A fragment program can be thought of as a small computational  
        // kernel that is applied in parallel to many fragments  
        // simultaneously.  Here we load a kernel that performs an edge  
        // detection filter on an image. 
 
        _programObject = glCreateProgramObjectARB(); 
 
        // Create the edge detection fragment program 
        _fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 
        glShaderSourceARB(_fragmentShader, 1, &edgeFragSource, NULL); 
        glCompileShaderARB(_fragmentShader); 
        glAttachObjectARB(_programObject, _fragmentShader); 
 
        // Link the shader into a complete GLSL program. 
        glLinkProgramARB(_programObject); 
        GLint progLinkSuccess; 
        glGetObjectParameterivARB(_programObject, GL_OBJECT_LINK_STATUS_ARB, 
                &progLinkSuccess); 
        if (!progLinkSuccess) 
        { 
            fprintf(stderr, "Filter shader could not be linked\n"); 
            exit(1); 
        } 
 
        // Get location of the sampler uniform 
        _texUnit = glGetUniformLocationARB(_programObject, "texUnit"); 
    } 
 
	~HelloGPGPU() 
    { 
    } 
 
    // This method updates the texture by rendering the geometry (a teapot  
    // and 3 rotating tori) and copying the image to a texture.   
    // It then renders a second pass using the texture as input to an edge  
    // detection filter.  It copies the results of the filter to the texture. 
    // The texture is used in HelloGPGPU::display() for displaying the  
    // results. 
    void update() 
    {        
        _rAngle += 0.5f; 
 
        // store the window viewport dimensions so we can reset them, 
        // and set the viewport to the dimensions of our texture 
        int vp[4]; 
        glGetIntegerv(GL_VIEWPORT, vp); 
 
        // GPGPU CONCEPT 3a: One-to-one Pixel to Texel Mapping: A Data- 
        //                   Dimensioned Viewport. 
        // We need a one-to-one mapping of pixels to texels in order to  
        // ensure every element of our texture is processed. By setting our  
        // viewport to the dimensions of our destination texture and drawing  
        // a screen-sized quad (see below), we ensure that every pixel of our  
        // texel is generated and processed in the fragment program. 
        glViewport(0, 0, _iWidth, _iHeight); 
         
        // Render a teapot and 3 tori 
        glClear(GL_COLOR_BUFFER_BIT); 
        glMatrixMode(GL_MODELVIEW); 
        glPushMatrix(); 
        glRotatef(-_rAngle, 0, 1, 0.25); 
        glutSolidTeapot(0.5); 
        glPopMatrix(); 
        glPushMatrix(); 
        glRotatef(2.1 * _rAngle, 1, 0.5, 0);           
        glutSolidTorus(0.05, 0.9, 64, 64); 
        glPopMatrix(); 
        glPushMatrix(); 
        glRotatef(-1.5 * _rAngle, 0, 1, 0.5); 
        glutSolidTorus(0.05, 0.9, 64, 64); 
        glPopMatrix(); 
        glPushMatrix(); 
        glRotatef(1.78 * _rAngle, 0.5, 0, 1); 
        glutSolidTorus(0.05, 0.9, 64, 64); 
        glPopMatrix(); 
         
        // copy the results to the texture 
        glBindTexture(GL_TEXTURE_2D, _iTexture); 
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _iWidth, _iHeight); 
         
 
        // run the edge detection filter over the geometry texture 
        // Activate the edge detection filter program 
        glUseProgramObjectARB(_programObject); 
             
        // identify the bound texture unit as input to the filter 
        glUniform1iARB(_texUnit, 0); 
             
        // GPGPU CONCEPT 4: Viewport-Sized Quad = Data Stream Generator. 
        // In order to execute fragment programs, we need to generate pixels. 
        // Drawing a quad the size of our viewport (see above) generates a  
        // fragment for every pixel of our destination texture. Each fragment 
        // is processed identically by the fragment program. Notice that in  
        // the reshape() function, below, we have set the frustum to  
        // orthographic, and the frustum dimensions to [-1,1].  Thus, our  
        // viewport-sized quad vertices are at [-1,-1], [1,-1], [1,1], and  
        // [-1,1]: the corners of the viewport. 
        glBegin(GL_QUADS); 
        {             
            glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.5f); 
            glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.5f); 
            glTexCoord2f(1, 1); glVertex3f( 1,  1, -0.5f); 
            glTexCoord2f(0, 1); glVertex3f(-1,  1, -0.5f); 
        } 
        glEnd(); 
         
        // disable the filter 
        glUseProgramObjectARB(0); 
         
        // GPGPU CONCEPT 5: Copy To Texture (CTT) = Feedback. 
        // We have just invoked our computation (edge detection) by applying  
        // a fragment program to a viewport-sized quad. The results are now  
        // in the frame buffer. To store them, we copy the data from the  
        // frame buffer to a texture.  This can then be fed back as input 
        // for display (in this case) or more computation (see  
        // more advanced samples.) 
 
        // update the texture again, this time with the filtered scene 
        glBindTexture(GL_TEXTURE_2D, _iTexture); 
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _iWidth, _iHeight); 
         
        // restore the stored viewport dimensions 
        glViewport(vp[0], vp[1], vp[2], vp[3]); 
    } 
 
    void display() 
    { 
        // Bind the filtered texture 
        glBindTexture(GL_TEXTURE_2D, _iTexture); 
        glEnable(GL_TEXTURE_2D); 
 
        // render a full-screen quad textured with the results of our  
        // computation.  Note that this is not part of the computation: this 
        // is only the visualization of the results. 
        glBegin(GL_QUADS); 
        { 
            glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.5f); 
            glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.5f); 
            glTexCoord2f(1, 1); glVertex3f( 1,  1, -0.5f); 
            glTexCoord2f(0, 1); glVertex3f(-1,  1, -0.5f); 
        } 
        glEnd(); 
 
        glDisable(GL_TEXTURE_2D); 
    } 
 
protected: // data 
    int           _iWidth, _iHeight; // The dimensions of our array 
    float         _rAngle;           // used for animation 
     
    unsigned int  _iTexture;         // The texture used as a data array 
 
    GLhandleARB   _programObject;    // the program used to update 
    GLhandleARB   _fragmentShader; 
 
    GLint         _texUnit;          // a parameter to the fragment program 
}; 
 
// GLUT idle function 
void idle() 
{ 
    glutPostRedisplay(); 
} 
 
// GLUT display function 
void display() 
{ 
    g_pHello->update();  // update the scene and run the edge detect filter 
    g_pHello->display(); // display the results 
    glutSwapBuffers(); 
} 
 
// GLUT reshape function 
void reshape(int w, int h) 
{ 
    if (h == 0) h = 1; 
     
    glViewport(0, 0, w, h); 
     
    // GPGPU CONCEPT 3b: One-to-one Pixel to Texel Mapping: An Orthographic  
    //                   Projection. 
    // This code sets the projection matrix to orthographic with a range of  
    // [-1,1] in the X and Y dimensions. This allows a trivial mapping of  
    // pixels to texels. 
    glMatrixMode(GL_PROJECTION);     
    glLoadIdentity();                
    gluOrtho2D(-1, 1, -1, 1);        
    glMatrixMode(GL_MODELVIEW);      
    glLoadIdentity();                
} 
 
// Called at startup 
void initialize() 
{ 
    // Initialize the "OpenGL Extension Wrangler" library 
    glewInit(); 
 
    // Ensure we have the necessary OpenGL Shading Language extensions. 
    if (glewGetExtension("GL_ARB_fragment_shader")      != GL_TRUE || 
        glewGetExtension("GL_ARB_vertex_shader")        != GL_TRUE || 
        glewGetExtension("GL_ARB_shader_objects")       != GL_TRUE || 
        glewGetExtension("GL_ARB_shading_language_100") != GL_TRUE) 
    { 
        fprintf(stderr, "Driver does not support OpenGL Shading Language\n"); 
        exit(1); 
    } 
 
    // Create the example object 
    g_pHello = new HelloGPGPU(512, 512); 
} 
 
// The main function 
int main() 
{ 
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); 
    glutInitWindowSize(512, 512); 
    glutCreateWindow("Hello, GPGPU! (GLSL version)"); 
 
    glutIdleFunc(idle); 
    glutDisplayFunc(display); 
    glutReshapeFunc(reshape); 
 
    initialize(); 
 
    glutMainLoop(); 
    return 0; 
}