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 <stdio.h>
#include <assert.h>
#include <stdlib.h>
#define GLEW_STATIC 1
#include <GL/glew.h>
#include <GL/glut.h>
// 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, &amt;_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, &amt;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,
&amt;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;
}