ضمیمه A: برنامههای نمونه 1 تا 7 و شیدرها
ضمیمه A: برنامههای نمونهٔ ۱ تا ۷ و Shaderها
ضمیمه A — برنامههای نمونه
این ضمیمه کد منبع بسیاری از برنامههای نمونهای را دربر دارد که در متن توسعه داده شدند. نسخههای همراه کتاب همچنین فایلهای include مانند Angel.h، matrix.h و vector.h و یک Makefile برای مدیریت تفاوت معماریها را شامل میشوند. نمونههای افزوده و یادداشتهای پیادهسازی نیز همراه مجموعه ارائه شدهاند.
driverهای کارت گرافیک، OpenGL را با ترکیبی از سختافزار و نرمافزار پیادهسازی میکنند. بنابراین اگر driverها درست نصب شده باشند، استفاده از OpenGL در سامانههای مختلف از دید برنامه یکسان است؛ تفاوت کارتها عمدتاً در کارایی، extensionهای پشتیبانیشده و نسخهٔ OpenGL است.
OpenGL روی بیشتر workstationها استاندارد است. در Windows کتابخانهٔ پویا و فایلهای .lib/include همراه محیطهای توسعه فراهم میشوند و فایلهای GLUT یا freeglut نیز قابل استفادهاند. freeglut امکان بررسی سازگاری کد با نسخهٔ مشخصی از OpenGL را نیز میدهد. بیشتر کاربران میتوانند از GLEW برای مدیریت version و extension استفاده کنند؛ در Mac معمولاً نیازی به GLEW نیست. در Linux نیز Mesa و driverهای سازندگان کارتها گزینههای متداولاند.
برنامههای بعدی برای ارتباط با window system از GLUT استفاده میکنند و نامگذاری تابعها مطابق OpenGL Programming Guide و GLUT Users Guide است. بخش بزرگی از کد میان نمونهها مشترک است؛ بنابراین تابعهایی مانند reshape callback، تابع initialization و main بسیار شبیهاند و توضیحهای مفصل فقط در اولین نمونهها آمدهاند.
هدف اصلی این نمونهها نمایش اصول گرافیکی است، نه بهینهسازی. در بسیاری از موارد میتوان برنامهها را توسعه داد، کاراتر کرد یا همان نتیجهٔ بصری را با قابلیت دیگری از OpenGL ساخت.
برنامههای این ضمیمه عبارتاند از:
- تابع
InitShader؛
- برنامهٔ تولید ۵۰۰۰ نقطه روی Sierpinski gasket؛
- نسخهٔ بازگشتی gasket؛
- مکعب چرخان با ارسال زاویههای rotation به GPU؛
- مشاهدهٔ مکعب با perspective؛
- مکعب چرخانِ shaded؛
- کرهٔ بازگشتی shaded با per-fragment lighting؛
- مکعب چرخان با texture؛
- برنامهٔ شکل مبتنی بر tree؛
- renderer قوری.
A.1 تابع مقداردهی اولیهٔ Shader
A.1.1 کد برنامه
کد زیر عیناً حفظ شده است:
#include "Angel.h" // Book header file
namespace Angel {
// Create a NULL-terminated string by reading the provided file
static char*
readShaderSource(const char* shaderFile)
{
FILE* fp = fopen(shaderFile, "r");
if ( fp == NULL ) { return NULL; }
fseek(fp, 0L, SEEK_END);
long size = ftell(fp);
A.1 Shader Initialization Function 609
fseek(fp, 0L, SEEK_SET);
char* buf = new char[size + 1];
fread(buf, 1, size, fp);
buf[size] = ’ ’;
fclose(fp);
return buf;
}
// Create a GLSL program object from vertex and fragment shader files
GLuint
InitShader(const char* vShaderFile, const char* fShaderFile)
{
struct Shader {
const char* filename;
GLenum type;
GLchar* source;
} shaders[2] = {
{ vShaderFile, GL_VERTEX_SHADER, NULL },
{ fShaderFile, GL_FRAGMENT_SHADER, NULL }
};
GLuint program = glCreateProgram( void );
for ( int i = 0; i < 2; ++i ) {
Shader& s = shaders[i];
s.source = readShaderSource( s.filename );
if ( shaders[i].source == NULL ) {
std::cerr << "Failed to read " << s.filename << std::endl;
exit( EXIT_FAILURE );
}
GLuint shader = glCreateShader( s.type );
glShaderSource( shader, 1, (const GLchar**) &s.source, NULL );
glCompileShader( shader );
GLint compiled;
glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
std::cerr << s.filename << " failed to compile:" << std::endl;
GLint logSize;
glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logSize );
char* logMsg = new char[logSize];
glGetShaderInfoLog( shader, logSize, NULL, logMsg );
std::cerr << logMsg << std::endl;
delete [] logMsg;
A.2 برنامهٔ Sierpinski Gasket
A.2.1 کد برنامه
610 Appendix A Sample Programs
exit( EXIT_FAILURE );
}
delete [] s.source;
glAttachShader( program, shader );
}
// link and error check
glLinkProgram(program);
GLint linked;
glGetProgramiv( program, GL_LINK_STATUS, &linked );
if ( !linked ) {
std::cerr << "Shader program failed to link" << std::endl;
GLint logSize;
glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logSize);
char* logMsg = new char[logSize];
glGetProgramInfoLog( program, logSize, NULL, logMsg );
std::cerr << logMsg << std::endl;
delete [] logMsg;
exit( EXIT_FAILURE );
}
// use program object
glUseProgram(program);
return program;
}
} // Close namespace Angel block
A.2 SIERPINSKI GASKET PROGRAM
A.2.1 Application Code
// Two-Dimensional Sierpinski Gasket
// Generated using randomly selected vertices and bisection
#include "Angel.h"
const int NumPoints = 5000;
void
init( void )
{
vec2 points[NumPoints];
A.2 Sierpinski Gasket Program 611
// Specify the vertices for a triangle
vec2 vertices[3] = {
vec2( -1.0, -1.0 ), vec2( 0.0, 1.0 ), vec2( 1.0, -1.0 )
};
// Select an arbitrary initial point inside of the triangle
points[0] = vec2( 0.25, 0.50 );
// compute and store N-1 new points
for ( int i = 1; i < NumPoints; ++i ) {
int j = rand( void ) % 3; // pick a vertex at random
// Compute the point halfway between the selected vertex
// and the previous point
points[i] = ( points[i - 1] + vertices[j] ) / 2.0;
}
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader21.glsl", "fshader21.glsl" );
glUseProgram( program );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW );
// Initialize the vertex position attribute from the vertex shader
GLuint loc = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( loc );
glVertexAttribPointer( loc, 2, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
glClearColor( 1.0, 1.0, 1.0, 1.0 ); // white background
}
//----------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT ); // clear the window
glDrawArrays( GL_POINTS, 0, NumPoints ); // draw the points
A.2.2 Vertex Shader و A.2.3 Fragment Shader
612 Appendix A Sample Programs
glFlush( void );
}
//----------------------------------------------------------------------
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA );
glutInitWindowSize( 512, 512 );
// If you are using freeglut, the next two lines will check if
// the code is truly 3.2. Otherwise, comment them out
glutInitContextVersion( 3, 2 );
glutInitContextProfile( GLUT_CORE_PROFILE );
glutCreateWindow( "Sierpinski Gasket" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutMainLoop( void );
return 0;
}
A.2.2 Vertex Shader
#version 150 //GLSL Version 1.5
in vec4 vPosition;
void main()
{
gl_Position = vPosition;
}
A.2.3 Fragment Shader
#version 150
out vec4 fColor;
void main()
{
fColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
A.3 تولید بازگشتی Sierpinski Gasket
A.3.1 کد برنامه
A.3 Recursive Generation of Sierpinski Gasket 613
A.3 RECURSIVE GENERATION OF SIERPINSKI GASKET
A.3.1 Application Code
// Recursive subdivision of triangle to form Sierpinski gasket
// Number of recursive steps given on command line
#include "Angel.h"
using namespace Angel;
const int NumTimesToSubdivide = 5;
const int NumTriangles = 729; // 3^5 triangles generated
const int NumVertices = 3 * NumTriangles;
vec2 points[NumVertices];
int Index = 0;
//----------------------------------------------------------------------
void
triangle( const vec2& a, const vec2& b, const vec2& c )
{
points[Index++] = a;
points[Index++] = b;
points[Index++] = c;
}
//----------------------------------------------------------------------
void
divide_triangle( const vec2& a, const vec2& b, const vec2& c, int count )
{
if ( count > 0 ) {
//compute midpoints of sides
vec2 v0 = ( a + b ) / 2.0;
vec2 v1 = ( a + c ) / 2.0;
vec2 v2 = ( b + c ) / 2.0;
//subdivide all but middle triangle
divide_triangle( a, v0, v1, count - 1 );
divide_triangle( c, v1, v2, count - 1 );
divide_triangle( b, v2, v0, count - 1 );
}
else {
triangle( a, b, c ); // draw triangle at end of recursion
}
}
614 Appendix A Sample Programs
//----------------------------------------------------------------------
void
init( void )
{
vec2 vertices[3] = {
vec2( -1.0, -1.0 ), vec2( 0.0, 1.0 ), vec2( 1.0, -1.0 )
};
// Subdivide the original triangle
divide_triangle( vertices[0], vertices[1], vertices[2],
NumTimesToSubdivide );
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader22.glsl", "fshader22.glsl" );
glUseProgram( program );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points), points,
GL_STATIC_DRAW );
// Initialize the vertex position attribute from the vertex shader
GLuint loc = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( loc );
glVertexAttribPointer( loc, 2, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
glClearColor( 1.0, 1.0, 1.0, 1.0 ); // white background
}
//----------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT );
glDrawArrays( GL_TRIANGLES, 0, NumTriangles );
glFlush( void );
}
//----------------------------------------------------------------------
A.3.2 Vertex Shader، A.3.3 Fragment Shader و آغاز A.4 مکعب چرخان با rotation در shader
A.4 Rotating Cube with Rotation in Shader 615
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA );
glutInitWindowSize( 512, 512 );
glutInitContextVersion( 3, 2 );
glutInitContextProfile( GLUT_CORE_PROFILE );
glutCreateWindow( "Sierpinski Gasket" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutMainLoop( void );
return 0;
}
A.3.2 Vertex Shader
#version 150
in vec4 vPosition;
void main()
{
gl_Position = vPosition;
}
A.3.3 Fragment Shader
#version 150
out vec4 fColor;
void main()
{
fColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
A.4 ROTATING CUBE WITH ROTATION IN SHADER
A.4.1 Application Code
//
// Display a rotating color cube
616 Appendix A Sample Programs
// In this version, idle function increments angles
// which are sent to vertex shader where rotation takes place
#include "Angel.h"
typedef Angel::vec4 color4;
typedef Angel::vec4 point4;
const int NumVertices = 36; //(6 faces)(2 triangles/face)
(3 vertices/triangle)
point4 points[NumVertices];
color4 colors[NumVertices];
// Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8] = {
point4( -0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, -0.5, -0.5, 1.0 ),
point4( -0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, -0.5, -0.5, 1.0 )
};
// RGBA colors
color4 vertex_colors[8] = {
color4( 0.0, 0.0, 0.0, 1.0 ), // black
color4( 1.0, 0.0, 0.0, 1.0 ), // red
color4( 1.0, 1.0, 0.0, 1.0 ), // yellow
color4( 0.0, 1.0, 0.0, 1.0 ), // green
color4( 0.0, 0.0, 1.0, 1.0 ), // blue
color4( 1.0, 0.0, 1.0, 1.0 ), // magenta
color4( 1.0, 1.0, 1.0, 1.0 ), // white
color4( 0.0, 1.0, 1.0, 1.0 ) // cyan
};
// Array of rotation angles (in degrees) for each coordinate axis
enum { Xaxis = 0, Yaxis = 1, Zaxis = 2, NumAxes = 3 };
int Axis = Xaxis;
GLfloat Theta[NumAxes] = { 0.0, 0.0, 0.0 };
GLuint theta; // The location of the "theta" shader uniform variable
//----------------------------------------------------------------------
// quad generates two triangles for each face and assigns colors
// to the vertices
A.4 Rotating Cube with Rotation in Shader 617
int Index = 0;
void
quad( int a, int b, int c, int d )
{
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[b]; points[Index] = vertices[b]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[d]; points[Index] = vertices[d]; Index++;
}
//----------------------------------------------------------------------
// generate 12 triangles: 36 vertices and 36 colors
void
colorcube( void )
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
//----------------------------------------------------------------------
// OpenGL initialization
void
init( void )
{
colorcube( void );
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader36.glsl", "fshader36.glsl" );
glUseProgram( program );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
NULL, GL_STATIC_DRAW );
618 Appendix A Sample Programs
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(points),
sizeof(colors), colors );
// set up vertex arrays
GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
GLuint vColor = glGetAttribLocation( program, "vColor" );
glEnableVertexAttribArray( vColor );
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(points)) );
theta = glGetUniformLocation( program, "theta" );
glEnable( GL_DEPTH_TEST );
glClearColor( 1.0, 1.0, 1.0, 1.0 );
}
//----------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUniform3fv( theta, 1, Theta );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers( void );
}
//----------------------------------------------------------------------
void
keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 033: // Escape Key
case ’q’: case ’Q’:
exit( EXIT_SUCCESS );
break;
}
}
//----------------------------------------------------------------------
A.4 Rotating Cube with Rotation in Shader 619
void
mouse( int button, int state, int x, int y )
{
if ( state == GLUT_DOWN ) {
switch( button ) {
case GLUT_LEFT_BUTTON: Axis = Xaxis; break;
case GLUT_MIDDLE_BUTTON: Axis = Yaxis; break;
case GLUT_RIGHT_BUTTON: Axis = Zaxis; break;
}
}
}
//----------------------------------------------------------------------
void
idle( void )
{
Theta[Axis] += 0.01;
if ( Theta[Axis] > 360.0 ) {
Theta[Axis] -= 360.0;
}
glutPostRedisplay( void );
}
//----------------------------------------------------------------------
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutInitWindowSize( 512, 512 );
glutCreateWindow( "Color Cube" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutMouseFunc( mouse );
glutIdleFunc( idle );
glutMainLoop( void );
return 0;
}
A.4.2 Vertex Shader و A.4.3 Fragment Shader
620 Appendix A Sample Programs
A.4.2 Vertex Shader
#version 150
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
uniform vec3 theta;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remember: these matrices are column-major
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, -s.x, 0.0,
0.0, s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
-s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, -s.z, 0.0, 0.0,
s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
color = vColor;
gl_Position = rx * ry * rz * vPosition;
}
A.4.3 Fragment Shader
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
A.5 Perspective Projection
A.5.1 کد برنامه
A.5 Perspective Projection 621
A.5 PERSPECTIVE PROJECTION
A.5.1 Application Code
// Perspective view of a color cube using LookAt( void ) and Frustum( void )
#include "Angel.h"
typedef Angel::vec4 color4;
typedef Angel::vec4 point4;
const int NumVertices = 36; //(6 faces)(2 triangles/face)(3 vertices/triangle)
point4 points[NumVertices];
color4 colors[NumVertices];
// Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8] ={
point4( -0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, -0.5, -0.5, 1.0 ),
point4( -0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, -0.5, -0.5, 1.0 )
};
// RGBA colors
color4 vertex_colors[8] ={
color4( 0.0, 0.0, 0.0, 1.0 ), // black
color4( 1.0, 0.0, 0.0, 1.0 ), // red
color4( 1.0, 1.0, 0.0, 1.0 ), // yellow
color4( 0.0, 1.0, 0.0, 1.0 ), // green
color4( 0.0, 0.0, 1.0, 1.0 ), // blue
color4( 1.0, 0.0, 1.0, 1.0 ), // magenta
color4( 1.0, 1.0, 1.0, 1.0 ), // white
color4( 0.0, 1.0, 1.0, 1.0 ) // cyan
};
// Viewing transformation parameters
GLfloat radius = 1.0;
GLfloat theta = 0.0;
GLfloat phi = 0.0;
const GLfloat dr = 5.0 * DegreesToRadians;
GLuint model_view; // model-view matrix uniform shader variable location
622 Appendix A Sample Programs
// Projection transformation parameters
GLfloat left = -1.0, right = 1.0;
GLfloat bottom = -1.0, top = 1.0;
GLfloat zNear = 0.5, zFar = 3.0;
GLuint projection; // projection matrix uniform shader variable location
//----------------------------------------------------------------------
// quad generates two triangles for each face and assigns colors
// to the vertices
int Index = 0;
void
quad( int a, int b, int c, int d )
{
colors[Index] = vertex_colors[a]; points[Index] = vertices[a];
Index++;
colors[Index] = vertex_colors[b]; points[Index] = vertices[b];
Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c];
Index++;
colors[Index] = vertex_colors[a]; points[Index] = vertices[a];
Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c];
Index++;
colors[Index] = vertex_colors[d]; points[Index] = vertices[d];
Index++;
}
//----------------------------------------------------------------------
// generate 12 triangles: 36 vertices and 36 colors
void
colorcube( void )
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
//----------------------------------------------------------------------
A.5 Perspective Projection 623
// OpenGL initialization
void
init( void )
{
colorcube( void );
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader42.glsl", "fshader42.glsl" );
glUseProgram( program );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
NULL, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );
// set up vertex arrays
GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
GLuint vColor = glGetAttribLocation( program, "vColor" );
glEnableVertexAttribArray( vColor );
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(points)) );
model_view = glGetUniformLocation( program, "model_view" );
projection = glGetUniformLocation( program, "projection" );
glEnable( GL_DEPTH_TEST );
glClearColor( 1.0, 1.0, 1.0, 1.0 );
}
//----------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
624 Appendix A Sample Programs
point4 eye( radius*sin(theta)*cos(phi),
radius*sin(theta)*sin(phi),
radius*cos(theta),
1.0 );
point4 at( 0.0, 0.0, 0.0, 1.0 );
vec4 up( 0.0, 1.0, 0.0, 0.0 );
mat4 mv = LookAt( eye, at, up );
glUniformMatrix4fv( model_view, 1, GL_TRUE, mv );
mat4 p = Frustum( left, right, bottom, top, zNear, zFar );
glUniformMatrix4fv( projection, 1, GL_TRUE, p );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers( void );
}
//----------------------------------------------------------------------
void
keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 033: // Escape Key
case ’q’: case ’Q’:
exit( EXIT_SUCCESS );
break;
case ’x’: left *= 1.1; right *= 1.1; break;
case ’X’: left *= 0.9; right *= 0.9; break;
case ’y’: bottom *= 1.1; top *= 1.1; break;
case ’Y’: bottom *= 0.9; top *= 0.9; break;
case ’z’: zNear *= 1.1; zFar *= 1.1; break;
case ’Z’: zNear *= 0.9; zFar *= 0.9; break;
case ’r’: radius *= 2.0; break;
case ’R’: radius *= 0.5; break;
case ’o’: theta += dr; break;
case ’O’: theta -= dr; break;
case ’p’: phi += dr; break;
case ’P’: phi -= dr; break;
case ’ ’: // reset values to their defaults
left = -1.0;
right = 1.0;
bottom = -1.0;
top = 1.0;
zNear = 0.5;
zFar = 3.0;
A.5.2 Vertex Shader
A.5 Perspective Projection 625
radius = 1.0;
theta = 0.0;
phi = 0.0;
break;
}
glutPostRedisplay( void );
}
//----------------------------------------------------------------------
void
reshape( int width, int height )
{
glViewport( 0, 0, width, height );
}
//----------------------------------------------------------------------
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutInitWindowSize( 512, 512 );
glutCreateWindow( "Color Cube" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutReshapeFunc( reshape );
glutMainLoop( void );
return 0;
}
A.5.2 Vertex Shader
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
uniform mat4 model_view;
uniform mat4 projection;
A.5.3 Fragment Shader
A.6 مکعب چرخانِ Shaded
A.6.1 کد برنامه
626 Appendix A Sample Programs
void main()
{
gl_Position = projection*model_view*vPosition/vPosition.w;
color = vColor;
}
A.5.3 Fragment Shader
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
A.6 ROTATING SHADED CUBE
A.6.1 Application Code
// Display a rotating cube with lighting
//
// Light and material properties are sent to the shader as uniform
// variables. Vertex positions and normals are sent after each
// rotation.
#include "Angel.h"
typedef Angel::vec4 color4;
typedef Angel::vec4 point4;
const int NumVertices = 36; //(6 faces)(2 triangles/face)
(3 vertices/triangle)
point4 points[NumVertices];
vec3 normals[NumVertices];
// Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8] = {
point4( -0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, 0.5, 0.5, 1.0 ),
point4( 0.5, -0.5, 0.5, 1.0 ),
point4( -0.5, -0.5, -0.5, 1.0 ),
point4( -0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, 0.5, -0.5, 1.0 ),
point4( 0.5, -0.5, -0.5, 1.0 )
};
A.6 Rotating Shaded Cube 627
// Array of rotation angles (in degrees) for each coordinate axis
enum { Xaxis = 0, Yaxis = 1, Zaxis = 2, NumAxes = 3 };
int Axis = Xaxis;
GLfloat Theta[NumAxes] = { 0.0, 0.0, 0.0 };
// Model-view and projection matrices uniform location
GLuint ModelView, Projection;
//----------------------------------------------------------------------
// quad generates two triangles for each face and assigns colors
// to the vertices
int Index = 0;
void
quad( int a, int b, int c, int d )
{
// Initialize temporary vectors along the quad’s edge to
// compute its face normal
vec4 u = vertices[b] - vertices[a];
vec4 v = vertices[c] - vertices[b];
vec3 normal = normalize( cross(u, v) );
normals[Index] = normal; points[Index] = vertices[a]; Index++;
normals[Index] = normal; points[Index] = vertices[b]; Index++;
normals[Index] = normal; points[Index] = vertices[c]; Index++;
normals[Index] = normal; points[Index] = vertices[a]; Index++;
normals[Index] = normal; points[Index] = vertices[c]; Index++;
normals[Index] = normal; points[Index] = vertices[d]; Index++;
}
//----------------------------------------------------------------------
// generate 12 triangles: 36 vertices and 36 colors
void
colorcube( void )
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
//----------------------------------------------------------------------
// OpenGL initialization
628 Appendix A Sample Programs
void
init( void )
{
colorcube( void );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(normals),
NULL, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(points),
sizeof(normals), normals );
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader53.glsl", "fshader53.glsl" );
glUseProgram( program );
// set up vertex arrays
GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
GLuint vNormal = glGetAttribLocation( program, "vNormal" );
glEnableVertexAttribArray( vNormal );
glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(points)) );
// Initialize shader lighting parameters
point4 light_position( 0.0, 0.0, -1.0, 0.0 );
color4 light_ambient( 0.2, 0.2, 0.2, 1.0 );
color4 light_diffuse( 1.0, 1.0, 1.0, 1.0 );
color4 light_specular( 1.0, 1.0, 1.0, 1.0 );
color4 material_ambient( 1.0, 0.0, 1.0, 1.0 );
color4 material_diffuse( 1.0, 0.8, 0.0, 1.0 );
color4 material_specular( 1.0, 0.8, 0.0, 1.0 );
float material_shininess = 100.0;
color4 ambient_product = light_ambient * material_ambient;
color4 diffuse_product = light_diffuse * material_diffuse;
color4 specular_product = light_specular * material_specular;
A.6 Rotating Shaded Cube 629
glUniform4fv( glGetUniformLocation(program, "AmbientProduct"),
1, ambient_product );
glUniform4fv( glGetUniformLocation(program, "DiffuseProduct"),
1, diffuse_product );
glUniform4fv( glGetUniformLocation(program, "SpecularProduct"),
1, specular_product );
glUniform4fv( glGetUniformLocation(program, "LightPosition"),
1, light_position );
glUniform1f( glGetUniformLocation(program, "Shininess"),
material_shininess );
// Retrieve transformation uniform variable locations
ModelView = glGetUniformLocation( program, "ModelView" );
Projection = glGetUniformLocation( program, "Projection" );
glEnable( GL_DEPTH_TEST );
glShadeModel(GL_FLAT);
glClearColor( 1.0, 1.0, 1.0, 1.0 );
}
//----------------------------------------------------------------------
void
display( void )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Generate the model-view matrix
const vec3 viewer_pos( 0.0, 0.0, 2.0 );
mat4 model_view = ( Translate( -viewer_pos ) *
RotateX( Theta[Xaxis] ) *
RotateY( Theta[Yaxis] ) *
RotateZ( Theta[Zaxis] ) );
glUniformMatrix4fv( ModelView, 1, GL_TRUE, model_view );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers( void );
}
//----------------------------------------------------------------------
void
mouse( int button, int state, int x, int y )
630 Appendix A Sample Programs
{
if ( state == GLUT_DOWN ) {
switch( button ) {
case GLUT_LEFT_BUTTON: Axis = Xaxis; break;
case GLUT_MIDDLE_BUTTON: Axis = Yaxis; break;
case GLUT_RIGHT_BUTTON: Axis = Zaxis; break;
}
}
}
//----------------------------------------------------------------------
void
idle( void )
{
Theta[Axis] += 0.01;
if ( Theta[Axis] > 360.0 ) {
Theta[Axis] -= 360.0;
}
glutPostRedisplay( void );
}
//----------------------------------------------------------------------
void
keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 033: // Escape Key
case ’q’: case ’Q’:
exit( EXIT_SUCCESS );
break;
}
}
//----------------------------------------------------------------------
void
reshape( int width, int height )
{
glViewport( 0, 0, width, height );
GLfloat aspect = GLfloat(width)/height;
mat4 projection = Perspective( 45.0, aspect, 0.5, 3.0 );
glUniformMatrix4fv( Projection, 1, GL_TRUE, projection );
}
A.6.2 Vertex Shader
A.6 Rotating Shaded Cube 631
//----------------------------------------------------------------------
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutInitWindowSize( 512, 512 );
glutCreateWindow( "Color Cube" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutReshapeFunc( reshape );
glutMouseFunc( mouse );
glutIdleFunc( idle );
glutMainLoop( void );
return 0;
}
A.6.2 Vertex Shader
#version 150
in vec4 vPosition;
in vec3 vNormal;
out vec4 color;
uniform vec4 AmbientProduct, DiffuseProduct, SpecularProduct;
uniform mat4 ModelView;
uniform mat4 Projection;
uniform vec4 LightPosition;
uniform float Shininess;
void main()
{
// Transform vertex position into eye coordinates
vec3 pos = (ModelView * vPosition).xyz;
vec3 L = normalize( LightPosition.xyz - pos );
vec3 E = normalize( -pos );
vec3 H = normalize( L + E );
// Transform vertex normal into eye coordinates
vec3 N = normalize( ModelView*vec4(vNormal, 0.0) ).xyz;
A.6.3 Fragment Shader
A.7 نورپردازی Per-Fragment مدل کره
A.7.1 کد برنامه
632 Appendix A Sample Programs
// Compute terms in the illumination equation
vec4 ambient = AmbientProduct;
float Kd = max( dot(L, N), 0.0 );
vec4 diffuse = Kd*DiffuseProduct;
float Ks = pow( max(dot(N, H), 0.0), Shininess );
vec4 specular = Ks * SpecularProduct;
if( dot(L, N) < 0.0 ) specular = vec4(0.0, 0.0, 0.0, 1.0);
gl_Position = Projection * ModelView * vPosition;
color = ambient + diffuse + specular;
color.a = 1.0;
}
A.6.3 Fragment Shader
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
A.7 PER-FRAGMENT LIGHTING OF SPHERE MODEL
A.7.1 Application Code
// fragment shading of sphere model
#include "Angel.h"
const int NumTimesToSubdivide = 5;
const int NumTriangles = 4096;
// (4 faces)^(NumTimesToSubdivide + 1)
const int NumVertices = 3 * NumTriangles;
typedef Angel::vec4 point4;
typedef Angel::vec4 color4;
point4 points[NumVertices];
vec3 normals[NumVertices];
// Model-view and projection matrices uniform location
A.7 Per-Fragment Lighting of Sphere Model 633
GLuint ModelView, Projection;
//----------------------------------------------------------------------
int Index = 0;
void
triangle( const point4& a, const point4& b, const point4& c )
{
vec3 normal = normalize( cross(b - a, c - b) );
normals[Index] = normal; points[Index] = a; Index++;
normals[Index] = normal; points[Index] = b; Index++;
normals[Index] = normal; points[Index] = c; Index++;
}
//----------------------------------------------------------------------
point4
unit( const point4& p )
{
float len = p.x*p.x + p.y*p.y + p.z*p.z;
point4 t;
if ( len > DivideByZeroTolerance ) {
t = p / sqrt(len);
t.w = 1.0;
}
return t;
}
void
divide_triangle( const point4& a, const point4& b,
const point4& c, int count )
{
if ( count > 0 ) {
point4 v1 = unit( a + b );
point4 v2 = unit( a + c );
point4 v3 = unit( b + c );
divide_triangle( a, v1, v2, count - 1 );
divide_triangle( c, v2, v3, count - 1 );
divide_triangle( b, v3, v1, count - 1 );
divide_triangle( v1, v3, v2, count - 1 );
}
else {
triangle( a, b, c );
}
}
634 Appendix A Sample Programs
void
tetrahedron( int count )
{
point4 v[4] = {
vec4( 0.0, 0.0, 1.0, 1.0 ),
vec4( 0.0, 0.942809, -0.333333, 1.0 ),
vec4( -0.816497, -0.471405, -0.333333, 1.0 ),
vec4( 0.816497, -0.471405, -0.333333, 1.0 )
};
divide_triangle( v[0], v[1], v[2], count );
divide_triangle( v[3], v[2], v[1], count );
divide_triangle( v[0], v[3], v[1], count );
divide_triangle( v[0], v[2], v[3], count );
}
//----------------------------------------------------------------------
// OpenGL initialization
void
init( void )
{
// Subdivide a tetrahedron into a sphere
tetrahedron( NumTimesToSubdivide );
// Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(normals),
NULL, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(points),
sizeof(normals), normals );
// Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader56.glsl", "fshader56.glsl" );
glUseProgram( program );
// set up vertex arrays
GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0) );
A.7 Per-Fragment Lighting of Sphere Model 635
GLuint vNormal = glGetAttribLocation( program, "vNormal" );
glEnableVertexAttribArray( vNormal );
glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(points)) );
// Initialize shader lighting parameters
point4 light_position( 0.0, 0.0, 2.0, 0.0 );
color4 light_ambient( 0.2, 0.2, 0.2, 1.0 );
color4 light_diffuse( 1.0, 1.0, 1.0, 1.0 );
color4 light_specular( 1.0, 1.0, 1.0, 1.0 );
color4 material_ambient( 1.0, 0.0, 1.0, 1.0 );
color4 material_diffuse( 1.0, 0.8, 0.0, 1.0 );
color4 material_specular( 1.0, 0.0, 1.0, 1.0 );
float material_shininess = 5.0;
color4 ambient_product = light_ambient * material_ambient;
color4 diffuse_product = light_diffuse * material_diffuse;
color4 specular_product = light_specular * material_specular;
glUniform4fv( glGetUniformLocation(program, "AmbientProduct"),
1, ambient_product );
glUniform4fv( glGetUniformLocation(program, "DiffuseProduct"),
1, diffuse_product );
glUniform4fv( glGetUniformLocation(program, "SpecularProduct"),
1, specular_product );
glUniform4fv( glGetUniformLocation(program, "LightPosition"),
1, light_position );
glUniform1f( glGetUniformLocation(program, "Shininess"),
material_shininess );
// Retrieve transformation uniform variable locations
ModelView = glGetUniformLocation( program, "ModelView" );
Projection = glGetUniformLocation( program, "Projection" );
glEnable( GL_DEPTH_TEST );
glClearColor( 1.0, 1.0, 1.0, 1.0 ); // white background
}
//----------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
point4 at( 0.0, 0.0, 0.0, 1.0 );
636 Appendix A Sample Programs
point4 eye( 0.0, 0.0, 2.0, 1.0 );
vec4 up( 0.0, 1.0, 0.0, 0.0 );
mat4 model_view = LookAt( eye, at, up );
glUniformMatrix4fv( ModelView, 16, GL_TRUE, model_view );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers( void );
}
//----------------------------------------------------------------------
void
keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 033: // Escape Key
case ’q’: case ’Q’:
exit( EXIT_SUCCESS );
break;
}
}
//----------------------------------------------------------------------
void
reshape( int width, int height )
{
glViewport( 0, 0, width, height );
GLfloat left = -2.0, right = 2.0;
GLfloat top = 2.0, bottom = -2.0;
GLfloat zNear = -20.0, zFar = 20.0;
GLfloat aspect = GLfloat(width)/height;
if ( aspect > 1.0 ) {
left *= aspect;
right *= aspect;
}
else {
top /= aspect;
bottom /= aspect;
}
mat4 projection = Ortho( left, right, bottom, top, zNear, zFar );
glUniformMatrix4fv( Projection, 1, GL_TRUE, projection );
}
//----------------------------------------------------------------------
A.7.2 Vertex Shader
A.7 Per-Fragment Lighting of Sphere Model 637
int
main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH );
glutInitWindowSize( 512, 512 );
glutCreateWindow( "Sphere" );
glewInit( void );
init( void );
glutDisplayFunc( display );
glutReshapeFunc( reshape );
glutKeyboardFunc( keyboard );
glutMainLoop( void );
return 0;
}
A.7.2 Vertex Shader
#version 150
in vec4 vPosition;
in vec3 vNormal;
// output values that will be interpolated per-fragment
out vec3 fN;
out vec3 fE;
out vec3 fL;
uniform mat4 ModelView;
uniform vec4 LightPosition;
uniform mat4 Projection;
void main()
{
fN = vNormal;
fE = vPosition.xyz;
fL = LightPosition.xyz;
if( LightPosition.w != 0.0 ) {
fL = LightPosition.xyz - vPosition.xyz;
}
gl_Position = Projection*ModelView*vPosition;
}
A.7.3 Fragment Shader
A.8 مکعب چرخان با Texture
A.8.1 کد برنامه (آغاز)
638 Appendix A Sample Programs
A.7.3 Fragment Shader
#version 150
// per-fragment interpolated values from the vertex shader
in vec3 fN;
in vec3 fL;
in vec3 fE;
out vec4 fColor;
uniform vec4 AmbientProduct, DiffuseProduct, SpecularProduct;
uniform mat4 ModelView;
uniform vec4 LightPosition;
uniform float Shininess;
void main()
{
// Normalize the input lighting vectors
vec3 N = normalize(fN);
vec3 E = normalize(fE);
vec3 L = normalize(fL);
vec3 H = normalize( L + E );
vec4 ambient = AmbientProduct;
float Kd = max(dot(L, N), 0.0);
vec4 diffuse = Kd*DiffuseProduct;
float Ks = pow(max(dot(N, H), 0.0), Shininess);
vec4 specular = Ks*SpecularProduct;
// discard the specular highlight if the light’s behind the vertex
if( dot(L, N) < 0.0 ) {
specular = vec4(0.0, 0.0, 0.0, 1.0);
}
fColor = ambient + diffuse + specular;
fColor.a = 1.0;
}
A.8 ROTATING CUBE WITH TEXTURE
A.8.1 Application Code
// rotating cube with two texture objects
// change textures with 1 and 2 keys