|
Using Shaders
In "Modern" OpenGL |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
#ifndef FILE_UTILITIES__H
#define FILE_UTILITIES_H
#include <string>
using namespace std;
/**
* /file
* Functions for working with files.
*/
/**
* Read a file into a stringstream.
*
* @param fileName The name of the file to read
* @return A string containing the contents of the file
*/
string readFile(const char* fileName);
#endif
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include "fileUtilities.h"
string readFile(const char* fileName) {
string contents;
ifstream stream(fileName, ios::in);
if(stream.is_open()){
stringstream sstr;
sstr << stream.rdbuf();
contents = sstr.str();
stream.close();
}
return contents;
}
GLuint glCreateShader(GLenum shaderType)
void glShaderSource(GLuint shaderID,
GLsizei count,
const GLchar **source,
const GLint *length)
void glCompileShader(GLuint shaderID)
void glGetShaderiv(GLuint shaderID,
GLenum paramname,
GLint *params)
void glGetShaderInfoLog(GLuint shaderID,
GLsizei maxLength, GLsizei *actualLength,
GLchar *message)
GLuint buildShader(const char* fileName, GLenum shaderType) {
// Create the handle/name/ID
GLuint id = glCreateShader(shaderType);
// Read the shader code from the file
string code = readFile(fileName);
if (code.size() == 0) return 0;
// Compile the shader
char const* codePointer = code.c_str();
glShaderSource(id, 1, &codePointer , NULL);
glCompileShader(id);
// Check the shader
GLint ok = GL_FALSE;
GLsizei messageLength = 0;
glGetShaderiv(id, GL_COMPILE_STATUS, &ok);
if (!ok) {
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &messageLength);
GLchar errorMessage[messageLength + 1];
glGetShaderInfoLog(id, messageLength, NULL, &errorMessage[0]);
printf("%s\n", &errorMessage[0]);
}
return id;
}
GLuint glCreateProgram(void)
void glAttachShader(GLuint programID,
GLuint shaderID)
void glLinkProgram(GLuint programID)
void glGetProgramiv(GLuint programID,
GLenum paramname,
GLint *params)
void glGetProgramInfoLog(GLuint programID,
GLsizei maxLength, GLsizei *actualLength,
GLchar *message)
void glDetachShader(GLuint programID,
GLuint shaderID) and
void glDeleteShader(GLuint id)
/**
* Build a GLSL proram.
*
* @param vertexFileName The name of the file containg the vertex shader
* @param fragmentFileName The name of the file containg the fragment shader
*/
GLuint buildProgram(const char* vertexFileName, const char* fragmentFileName);
GLuint buildProgram(const char* vertexFile,
const char* fragmentFile){
// Read and compile the shaders
GLuint vertexShaderID = buildShader(vertexFile, GL_VERTEX_SHADER);
if (vertexShaderID == 0) return 0;
GLuint fragmentShaderID = buildShader(fragmentFile, GL_FRAGMENT_SHADER);
if (fragmentShaderID == 0) return 0;
// Link the program
GLuint programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
// Check the program
GLint ok = GL_FALSE;
GLsizei messageLength = 0;
glGetProgramiv(programID, GL_LINK_STATUS, &ok);
if (!ok) {
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &messageLength);
GLchar errorMessage[messageLength + 1];
glGetProgramInfoLog(programID, messageLength, NULL, &errorMessage[0]);
printf("%s\n", &errorMessage[0]);
}
// Cleanup
glDetachShader(programID, vertexShaderID);
glDetachShader(programID, fragmentShaderID);
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
return programID;
}
// Initialize GLUT
glutInit(&argc, argv);
// Create the GLUT window
width = 640;
height = 480;
window = createWindow("Using Shaders", width, height, 0, 0);
// Initialize GLEW
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
printf("Failed to initialize GLEW\n");
return -1;
}
// Register the callbacks
glutCloseFunc(onClose);
glutDisplayFunc(display);
// Process the command line arguments
if (argc <= 1) {
programID = buildProgram("lighting.vert", "lighting.frag");
} else {
programID = buildProgram(argv[1], argv[2]);
}
if (programID == 0) {
printf("Problem creating the shader program\n");
return -2;
}
/**
* Initilization tasks.
*/
void init() {
// Create a vertex array object (VAO)
glGenVertexArrays(1, &vertexArrayID);
glBindVertexArray(vertexArrayID);
// Set the clear color to JMU purple
glClearColor(0.2706f, 0.0000f, 0.5176f, 0.000f);
// Get a handle for the uniform named "MVP" for the given program ID
mvpMatrixID = glGetUniformLocation(programID, "MVP");
// Define the projection matrix
P = glm::ortho(-1.5f,1.5f,-1.5f,1.5f,0.0f,5.0f);
// Define the camera matrix
V = glm::lookAt(
glm::vec3(0,-1,-2), // Camera is at (0,1,-2), in world space
glm::vec3(0,0,0), // and looks at the origin
glm::vec3(0,-1,0) // Head is down
);
// Define the model matrix
M = glm::mat4(1.0f); // The identity matrix
MVP = P * V * M; // Remember: matrix multiplication is "backwards"
// Get a handle for the uniform named "V"
viewMatrixID = glGetUniformLocation(programID, "V");
// Get a handle for the uniform named "M"
modelMatrixID = glGetUniformLocation(programID, "M");
// Get a handle for the uniform named "LightPosition_worldspace"
lightID = glGetUniformLocation(programID, "lightPosition_worldspace");
// Define the position of the light
lightPos = glm::vec3(4.0, 4.0, 4.0);
// Enable depth testing
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
// Cull each triangle whose normal is not towards the camera
glEnable(GL_CULL_FACE);
}
#include <algorithm>
#include "trifileUtilities.h"
Vertex* read(const char* fileName, int& size)
{
char s[80];
double x, y, z, nx, ny, nz;
double minX, maxX, minY, maxY, minZ, maxZ;
FILE* in;
int br, bg, bb, fr, fg, fb, triangles;
minX = minY = minZ = 10000000.0;
maxX = maxY = maxZ = -10000000.0;
in = fopen(fileName, "r");
// Read the number of triangles
fscanf(in, "%d", &triangles);
// The number of vertices (3 per triangle)
size = triangles * 3;
Vertex* vertices = new Vertex[size];
// Read the triangles
for (int t=0; t<triangles; t++) {
// The word "Triangle"
fscanf(in, "%s",s);
// Front face R,G,B and back face R,G,B
fscanf(in, "%d %d %d %d %d %d",&fr,&fg,&fb,&br,&bg,&bb);
// The three vertices
for (int v=0; v<3; v++){
int index = t*3 + v;
fscanf(in, "%lf %lf %lf %lf %lf %lf", &x, &y, &z, &nx, &ny, &nz);
vertices[index].position[0] = x;
vertices[index].position[1] = y;
vertices[index].position[2] = z;
vertices[index].normal[0] = nx;
vertices[index].normal[1] = ny;
vertices[index].normal[2] = nz;
if (x < minX) minX = x;
else if (x > maxX) maxX = x;
if (y < minY) minY = y;
else if (y > maxY) maxY = y;
if (z < minZ) minZ = z;
else if (z > maxZ) maxZ = z;
vertices[index].color[0] = (GLfloat)fr / 255.;
vertices[index].color[1] = (GLfloat)fg / 255.;
vertices[index].color[2] = (GLfloat)fb / 255.;
}
}
fclose(in);
// Translate and scale
double scale = std::min(2.0 / (maxX - minX), 2.0 / (maxY - minY));
double tx = -(maxX + minX)/2.0;
double ty = -(maxY + minY)/2.0;
for (int t=0; t<triangles; t++) {
for (int v=0; v<3; v++){
int index = t*3 + v;
vertices[index].position[0] = (vertices[index].position[0] + tx) * scale;
vertices[index].position[1] = (vertices[index].position[1] + ty) * scale;
vertices[index].position[2] = vertices[index].position[2] * scale;
}
}
return &vertices[0];
}
/**
* The display callback
*/
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use the appropriate shaders
glUseProgram(programID);
// Define uniforms for the currently bound shader
glUniformMatrix4fv(mvpMatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(modelMatrixID, 1, GL_FALSE, &M[0][0]);
glUniformMatrix4fv(viewMatrixID, 1, GL_FALSE, &V[0][0]);
glUniform3f(lightID, lightPos.x, lightPos.y, lightPos.z);
// Describe the positions in the vertex array buffer
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glVertexAttribPointer(
0, // attribute identifier (used in the shader)
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized or not?
sizeof(Vertex), // stride
reinterpret_cast<void*>(offsetof(Vertex, position)) // offset
);
// Describe the colors in the vertex array buffer
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glVertexAttribPointer(
1,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, color))
);
// Describe the normals in the vertex array buffer
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glVertexAttribPointer(
2,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, normal))
);
// Draw the triangles
glDrawArrays(GL_TRIANGLES, 0, size);
// Move the off-screen buffer to the screen
glFlush();
}