码迷,mamicode.com
首页 > 其他好文 > 详细

第4章:缓冲区、着色器、GLSL

时间:2015-07-12 23:29:04      阅读:450      评论:0      收藏:0      [点我收藏+]

标签:opengl

原文链接:

Tutorial 4: Buffers, Shaders, and GLSL
第4章:缓冲区、着色器、GLSL

This tutorial will be the introduction to writing vertex and pixel shaders in OpenGL 4.0. It will also be the introduction to using vertex and index buffers in OpenGL 4.0. These are the most fundamental concepts that you need to understand and utilize to render 3D graphics.
本章将介绍如何使用和编写OpenGL 4.0中的顶点和像素着色器。这部分是理解和实现渲染3D图形的基本概念。

Vertex Buffers
顶点缓冲区

The first concept to understand is vertex buffers. To illustrate this concept let us take the example of a 3D model of a sphere:
第一个需要理解的概念是顶点缓冲区。让我们用一个3D球体来阐述这个概念:
技术分享
The 3D sphere model is actually composed of hundreds of triangles:
这个球体实际由数百个三角形组成:
技术分享
Each of the triangles in the sphere model has three points to it; we call each point a vertex. So for us to render the sphere model we need to put all the vertices that form the sphere into a special data array that we call a vertex buffer. Once all the points of the sphere model are in the vertex buffer we can then send the vertex buffer to the GPU so that it can render the model.
球体中的每个三角形都有3个点组成,每个点都称之为一个顶点。为了渲染这个球体我们需要将全部顶点放在一个特殊的数组里,这个数组就称之为顶点缓冲区。球体所有的点都放入顶点缓冲区后,我们可以将顶点缓冲区发送给GPU来渲染球体。

Index Buffers
索引缓冲区

Index buffers are related to vertex buffers. Their purpose is to record the location of each vertex that is in the vertex buffer. The GPU then uses the index buffer to quickly find specific vertices in the vertex buffer. The concept of an index buffer is similar to the concept using an index in a book; it helps find the topic you are looking for at a much higher speed. As well using index buffers can also increase the possibility of caching the vertex data in faster locations in video memory. So it is highly advised to use these for performance reasons as well.
索引缓冲区和顶点缓冲区相关。是用来记录每个顶点在顶点缓冲区中的位置。GPU通过索引缓冲区可以快速的从顶点缓冲区找到对应的顶点。这个概念有点类似于书的目录。同时,使用顶点缓冲区也增加了显存中缓冲区的顶点数据命中率。

Vertex Shaders
顶点着色器

Vertex shaders are small programs that are written mainly for transforming the vertices from the vertex buffer into 3D space. There are other calculations that can be done such as calculating normals for each vertex. The vertex shader program will be called by the GPU for each vertex it needs to process. For example a 5,000 polygon model will run your vertex shader program 15,000 times each frame just to draw that single model. So if you lock your graphics program to 60 fps it will call your vertex shader 900,000 times a second to draw just 5,000 triangles. As you can tell writing efficient vertex shaders is important.
顶点着色器是将顶点从顶点缓冲区转换到3D空间的小程序。也可以进行其他的运算,比如为每个顶点计算法线。GPU在需要时对每个订单都运行顶点着色程序。例如一个有5000个多边形(三角形)的模型将在每帧运行15000次顶点着色程序。如果你的程序锁定为每秒60帧,那么5000个三角形将会在一秒时间里运行900000次顶点着色程序。所以,编写高性能的顶点着色器十分重要。

Pixel Shaders
像素着色器

Pixel shaders are small programs that are written for doing the coloring of the polygons that we draw. They are run by the GPU for every visible pixel that will be drawn to the screen. Coloring, texturing, lighting, and most other effects you plan to do to your polygon faces are handled by the pixel shader program. Pixel shaders must be efficiently written due to the number of times they will be called by the GPU.
像素着色器是为我们绘制的多边形上色的小程序。GPU将会为每一个将要绘制到屏幕上的可见的像素运行像素着色器。像素着色器处理多边形面上的上色、贴图、光照和很多其他的效果。像素着色器也要编写为高效的代码。

GLSL

GLSL is the language we use in OpenGL 4.0 to code these small vertex and pixel shader programs. The syntax is pretty much identical to the C language with some pre-defined types. As this is the first GLSL tutorial we will do a very simple GLSL program using OpenGL 4.0 to get started.
GLSL是用来编写OpenGL 4.0顶点和像素着色器程序的语言。语法风格和C语言很接近。作为第一篇GLSL教程,我们将实现一个非常简单的GLSL程序。

Updated Framework
更新后的框架
技术分享
The framework has been updated for this tutorial. Under GraphicsClass we have added three new classes called CameraClass, ModelClass, and ColorShaderClass. CameraClass will take care of our view matrix we talked about previously. It will handle the location of the camera in the world and pass it to shaders when they need to draw and figure out where we are looking at the scene from. The ModelClass will handle the geometry of our 3D models, in this tutorial the 3D model will just be a single triangle for simplicity reasons. And finally ColorShaderClass will be responsible for rendering the model to the screen invoking our GLSL shader.
本章更新了框架。GraphicsClass下面增加了3个新的类CameraClass、ModelClass、ColorShaderClass。CameraClass来处理我们之前讨论的视口矩阵。当需要绘制从视角看到的场景时,这个类将摄像机的位置传递给着色器。ModelClass处理3D模型的几何变换,本章为了简单只使用了一个三角形。ColorShaderClass通过GLSL着色器将模型渲染到屏幕上。


We will begin the tutorial code by looking at the GLSL shader program first.
本教程先从GLSL着色器代码开始。


Color.vs


These will be our first shader programs. Shaders are small programs that do the actual rendering of models. These shaders are written in GLSL and stored in source files called color.vs and color.ps. I placed the files with the .cpp and .h files in the engine for now. The purpose of this shader is just to draw colored triangles as I am keeping things simple as possible in this first GLSL tutorial. Here is the code for the vertex shader first:
这是我们首次写着色器程序。着色器程序用来渲染模型。这些着色器使用GLSL编写并将代码保存到color.vs和color.ps文件。我将文件放在engine下和.cpp .h文件相同的目录。作为首个着色器程序,只负责绘制一个带颜色的三角形。下面是顶点着色器代码:
////////////////////////////////////////////////////////////////////////////////
// Filename: color.vs
////////////////////////////////////////////////////////////////////////////////

The vertex shader begins by defining the version of GLSL that we are working with. The version we are working with is 4.0. The higher the version the more features that can be unlocked and used in the shader code.
在顶点着色器的头部定义了GLSL的版本。我们使用的版本是4.0。更高的版本意味着可以在着色器的代码里使用更多的特性。
#version 400

The next section in the vertex shader is the input vertex format. In this tutorial each vertex will be composed of an x, y, and z position as well as an r, g, and b color. These are floating point values and both of the input values have three floats each. You will notice we use a special type called vec3 instead of an array of floats. GLSL has useful types such as vec3, vec4, mat3, mat4 to make programming shaders easier and readable.
然后是顶点着色器的输入顶点格式。本教程中每个顶点包含了x、y、z坐标和r、g、b颜色。每个变量都包含了3个浮点类型的值。你会发现这里使用vec3类型来代替浮点数组。GLSL有很多有用的类型,例如vec3、vec4、mat3、mat4,这些类型可以使代码更容易编写和理解。
/////////////////////
// INPUT VARIABLES //
/////////////////////
in vec3 inputPosition;
in vec3 inputColor;

This next section in the vertex shader is the output variables that will be sent into the pixel shader. The only output variable that is defined is the color since we will be sending the color into the pixel shader. Note that the transformed input vertex position will also be sent into the pixel shader, but it is sent inside a special predefined variable called gl_Position.
下面是顶点着色器的输出变量,是用来将值传递给像素着色器的。这里只定义了将颜色传递到像素着色器的color变量。注意,输入的顶点位置也会被发送到像素着色器,但是通过着色器的内置变量gl_Position发送的。
//////////////////////
// OUTPUT VARIABLES //
//////////////////////
out vec3 color;

The next section in the vertex shader is the uniform variables. These are variables that we set once and do not change for each vertex. For this tutorial we will just set the three important matrices which are world, view, and projection.
下面是顶点着色器的一致变量。这些变量只设置一次并且不会因顶点的不同而改变。本教程我们设置了3个重要的矩阵,世界矩阵吗、视口矩阵、投影矩阵。
///////////////////////
// UNIFORM VARIABLES //
///////////////////////
uniform mat4 worldMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

The final section in the vertex shader is the code body. The code starts by multiplying the input vertex by the world, view, and then projection matrices. This will place the vertex in the correct location for rendering in 3D space according to our view and then onto the 2D screen. We store the result in the special gl_Position vector which will automatically get passed into the pixel shader. Also note that I do set the W value of the input position to 1.0 so we can do proper calculations using the 4x4 matrices. After that we set the output color from this vertex to be a copy of the input color. This will allow the pixel shader to access the color.
下面是顶点着色器的代码部分。代码开始部分是将输入的顶点分别乘以世界矩阵、视口矩阵、投影矩阵。这样做可以将在3D空间里的物体放在2D屏幕正确的位置上。我们将计算结果保存在gl_Position里,值会自动发送到像素着色器。注意,我将输入的位置的w值设置为1.0,这样做的目的是可以和4x4的矩阵进行运算。然后我们设置输出颜色为当前顶点的颜色。这样像素着色器就可以访问颜色。
////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
void main(void)
{
 // Calculate the position of the vertex against the world, view, and projection matrices.
 gl_Position = worldMatrix * vec4(inputPosition, 1.0f);
 gl_Position = viewMatrix * gl_Position;
 gl_Position = projectionMatrix * gl_Position;

 // Store the input color for the pixel shader to use.
 color = inputColor;
}

Color.ps

The pixel shader draws each pixel on the polygons that will be rendered to the screen. This pixel shader program is very simple as we just tell it to color the pixel the same as the input value of the color. The pixel shader receives color as an input vector and sets the outputColor output variable which represents the final pixel color. We need to convert the input color from vec3 to vec4 so that it has an alpha component to match our pixel format. Also remember that the pixel shader gets its input from the vertex shader output so naming variables correctly is important.
像素着色器是用来绘制多边形渲染到屏幕上每个像素的。这个像素着色器程序非常简单,只是将输入的颜色值直接输出到像素上。像素着色器接收到输入的颜色并设置输出颜色outputColor变量的值。我们需要将输入的vec3转换为包含alpha的vec4格式。必须要记住像素着色器从顶点着色器输入的,所以变量的名字必须要写正确。
////////////////////////////////////////////////////////////////////////////////
// Filename: color.ps
////////////////////////////////////////////////////////////////////////////////
#version 400

/////////////////////
// INPUT VARIABLES //
/////////////////////
in vec3 color;

//////////////////////
// OUTPUT VARIABLES //
//////////////////////
out vec4 outputColor;

////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
void main(void)
{
 outputColor = vec4(color, 1.0f);
}

Colorshaderclass.h

The ColorShaderClass is the class that we will use to compile and execute our color GLSL shaders so that we can render the 3D models that are on the GPU.
ColorShaderClass是负责编译和运行GLSL着色器的类,这个类可以让GPU渲染我们的3D模型。
////////////////////////////////////////////////////////////////////////////////
// Filename: colorshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _COLORSHADERCLASS_H_
#define _COLORSHADERCLASS_H_

//////////////
// INCLUDES //
//////////////
#include <fstream>
using namespace std;

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "openglclass.h"

////////////////////////////////////////////////////////////////////////////////
// Class name: ColorShaderClass
////////////////////////////////////////////////////////////////////////////////
class ColorShaderClass
{
public:
 ColorShaderClass();
 ColorShaderClass(const ColorShaderClass&);
 ~ColorShaderClass();

The functions here handle initializing and shutdown of the shader. The SetShaderParameters function sets the shader uniform variables and the SetShader function sets the shader code as the current rendering system.
下面的方法处理初始化和关闭着色器。SetShaderParamemters方法设置着色器的一直变量。SetShader方法设置着色器的代码。
bool Initialize(OpenGLClass*, HWND);
 void Shutdown(OpenGLClass*);
 void SetShader(OpenGLClass*);
 bool SetShaderParameters(OpenGLClass*, float*, float*, float*);

private:
 bool InitializeShader(char*, char*, OpenGLClass*, HWND);
 char* LoadShaderSourceFile(char*);
 void OutputShaderErrorMessage(OpenGLClass*, HWND, unsigned int, char*);
 void OutputLinkerErrorMessage(OpenGLClass*, HWND, unsigned int);
 void ShutdownShader(OpenGLClass*);

private:
 unsigned int m_vertexShader;
 unsigned int m_fragmentShader;
 unsigned int m_shaderProgram;
};

#endif

Colorshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: colorshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "colorshaderclass.h"

ColorShaderClass::ColorShaderClass()
{
}

ColorShaderClass::ColorShaderClass(const ColorShaderClass& other)
{
}

ColorShaderClass::~ColorShaderClass()
{
}

The Initialize function will call the initialization function for the shaders. We pass in the name of the GLSL shader files, in this tutorial they are named color.vs and color.ps.
Initialize方法是调用着色器的初始化方法。我们将GLSL着色器的文件名传递进来。本教程用到的文件是color.vs和color.ps。
bool ColorShaderClass::Initialize(OpenGLClass* OpenGL, HWND hwnd)
{
 bool result;

 // Initialize the vertex and pixel shaders.
 result = InitializeShader("../Engine/color.vs", "../Engine/color.ps", OpenGL, hwnd);
 if(!result)
 {
  return false;
 }

 return true;
}

The Shutdown function will call the shutdown of the shader.
Shutdown方法关闭着色器。
void ColorShaderClass::Shutdown(OpenGLClass* OpenGL)
{
 // Shutdown the vertex and pixel shaders as well as the related objects.
 ShutdownShader(OpenGL);

 return;
}

SetShader will set the color GLSL vertex and pixel shaders as the current rendering programs for drawing all 3D geometry.
SetShader函数用来设置当前渲染的3D几何图形所使用的GLSL顶点和像素着色器。
void ColorShaderClass::SetShader(OpenGLClass* OpenGL)
{
 // Install the shader program as part of the current rendering state.
 OpenGL->glUseProgram(m_shaderProgram);
 
 return;
}

Now we will start with one of the more important functions in this tutorial which is called InitializeShader. This function is what actually loads the shader files and makes them useable to OpenGL and the GPU. You will also see the setup of how the vertex buffer data is going to look on the graphics pipeline in the GPU. The input variables will need to match the VertexType in the modelclass.h file (which will be covered shortly) as well as the inputs variables defined in the color.vs file.
下面我们开始本章非常重要的方法InitializeShader。此方法加载着色器文件并使其在OpenGL和GPU起效。你还能看到在GPU图形管线上顶点缓冲区数据是如何传递的。在color.vs文件里定义的输入量需要匹配modelclass.h文件(稍后介绍)的VertexType。
bool ColorShaderClass::InitializeShader(char* vsFilename, char* fsFilename, OpenGLClass* OpenGL, HWND hwnd)
{
 const char* vertexShaderBuffer;
 const char* fragmentShaderBuffer;
 int status;

The first section of this function is where we load the vertex and pixel shader source files and compile them.
本方法的第一部分是加载和编译着色器源代码文件。
// Load the vertex shader source file into a text buffer.
 vertexShaderBuffer = LoadShaderSourceFile(vsFilename);
 if(!vertexShaderBuffer)
 {
  return false;
 }

 // Load the fragment shader source file into a text buffer.
 fragmentShaderBuffer = LoadShaderSourceFile(fsFilename);
 if(!fragmentShaderBuffer)
 {
  return false;
 }

 // Create a vertex and fragment shader object.
 m_vertexShader = OpenGL->glCreateShader(GL_VERTEX_SHADER);
 m_fragmentShader = OpenGL->glCreateShader(GL_FRAGMENT_SHADER);

 // Copy the shader source code strings into the vertex and fragment shader objects.
 OpenGL->glShaderSource(m_vertexShader, 1, &vertexShaderBuffer, NULL);
 OpenGL->glShaderSource(m_fragmentShader, 1, &fragmentShaderBuffer, NULL);

 // Release the vertex and fragment shader buffers.
 delete [] vertexShaderBuffer;
 vertexShaderBuffer = 0;
 
 delete [] fragmentShaderBuffer;
 fragmentShaderBuffer = 0;

 // Compile the shaders.
 OpenGL->glCompileShader(m_vertexShader);
 OpenGL->glCompileShader(m_fragmentShader);

 // Check to see if the vertex shader compiled successfully.
 OpenGL->glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, &status);
 if(status != 1)
 {
  // If it did not compile then write the syntax error message out to a text file for review.
  OutputShaderErrorMessage(OpenGL, hwnd, m_vertexShader, vsFilename);
  return false;
 }

 // Check to see if the fragment shader compiled successfully.
 OpenGL->glGetShaderiv(m_fragmentShader, GL_COMPILE_STATUS, &status);
 if(status != 1)
 {
  // If it did not compile then write the syntax error message out to a text file for review.
  OutputShaderErrorMessage(OpenGL, hwnd, m_fragmentShader, fsFilename);
  return false;
 }

Once the GLSL programs have compiled successfully we can create a shader program object and then attach the vertex and pixel shaders to it. We also bind the input variables and then finally link the program.
GLSL程序编译成功后,我们可以创建着色器对象并绑定顶点和像素着色器。然后也绑定输入变量,最后链接程序。
// Create a shader program object.
 m_shaderProgram = OpenGL->glCreateProgram();

 // Attach the vertex and fragment shader to the program object.
 OpenGL->glAttachShader(m_shaderProgram, m_vertexShader);
 OpenGL->glAttachShader(m_shaderProgram, m_fragmentShader);

 // Bind the shader input variables.
 OpenGL->glBindAttribLocation(m_shaderProgram, 0, "inputPosition");
 OpenGL->glBindAttribLocation(m_shaderProgram, 1, "inputColor");

 // Link the shader program.
 OpenGL->glLinkProgram(m_shaderProgram);

 // Check the status of the link.
 OpenGL->glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &status);
 if(status != 1)
 {
  // If it did not link then write the syntax error message out to a text file for review.
  OutputLinkerErrorMessage(OpenGL, hwnd, m_shaderProgram);
  return false;
 }

 return true;
}

The LoadShaderSourceFile function loads the shader code into a buffer that can be compiled.
LoadShaderSourceFile方法加载着色器代码到缓冲区,这样我们可以进行编译。
char* ColorShaderClass::LoadShaderSourceFile(char* filename)
{
 ifstream fin;
 int fileSize;
 char input;
 char* buffer;

 // Open the shader source file.
 fin.open(filename);

 // If it could not open the file then exit.
 if(fin.fail())
 {
  return 0;
 }

 // Initialize the size of the file.
 fileSize = 0;

 // Read the first element of the file.
 fin.get(input);

 // Count the number of elements in the text file.
 while(!fin.eof())
 {
  fileSize++;
  fin.get(input);
 }

 // Close the file for now.
 fin.close();

 // Initialize the buffer to read the shader source file into.
 buffer = new char[fileSize+1];
 if(!buffer)
 {
  return 0;
 }

 // Open the shader source file again.
 fin.open(filename);

 // Read the shader text file into the buffer as a block.
 fin.read(buffer, fileSize);

 // Close the file.
 fin.close();

 // Null terminate the buffer.
 buffer[fileSize] = '\0';

 return buffer;
}

The OutputShaderErrorMessage function writes out errors to a text file in case the GLSL shaders could not compile. The text file will contain the information needed to debug and correct the shader code.
OutputShaderErrorMessage方法将不能编译的GLSL着色器错误输出到文本文件。可以根据文件提供的信息对着色器代码进行调试和修正。
void ColorShaderClass::OutputShaderErrorMessage(OpenGLClass* OpenGL, HWND hwnd, unsigned int shaderId, char* shaderFilename)
{
 int logSize, i;
 char* infoLog;
 ofstream fout;
 wchar_t newString[128];
 unsigned int error, convertedChars;

 // Get the size of the string containing the information log for the failed shader compilation message.
 OpenGL->glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logSize);

 // Increment the size by one to handle also the null terminator.
 logSize++;

 // Create a char buffer to hold the info log.
 infoLog = new char[logSize];
 if(!infoLog)
 {
  return;
 }

 // Now retrieve the info log.
 OpenGL->glGetShaderInfoLog(shaderId, logSize, NULL, infoLog);

 // Open a file to write the error message to.
 fout.open("shader-error.txt");

 // Write out the error message.
 for(i=0; i<logSize; i++)
 {
  fout << infoLog[i];
 }

 // Close the file.
 fout.close();

 // Convert the shader filename to a wide character string.
 error = mbstowcs_s(&convertedChars, newString, 128, shaderFilename, 128);
 if(error != 0)
 {
  return;
 }

 // Pop a message up on the screen to notify the user to check the text file for compile errors.
 MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", newString, MB_OK);

 return;
}

The OutputLinkerErrorMessage function writes out the linker errors to a text file in the event that the linking in the InitializeShader function was not successful.
OutputLinkerErrorMessage方法将InitializeShader方法中的链接错误输出到文本文件。
void ColorShaderClass::OutputLinkerErrorMessage(OpenGLClass* OpenGL, HWND hwnd, unsigned int programId)
{
 int logSize, i;
 char* infoLog;
 ofstream fout;

 // Get the size of the string containing the information log for the failed shader compilation message.
 OpenGL->glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logSize);

 // Increment the size by one to handle also the null terminator.
 logSize++;

 // Create a char buffer to hold the info log.
 infoLog = new char[logSize];
 if(!infoLog)
 {
  return;
 }

 // Now retrieve the info log.
 OpenGL->glGetProgramInfoLog(programId, logSize, NULL, infoLog);

 // Open a file to write the error message to.
 fout.open("linker-error.txt");

 // Write out the error message.
 for(i=0; i<logSize; i++)
 {
  fout << infoLog[i];
 }

 // Close the file.
 fout.close();

 // Pop a message up on the screen to notify the user to check the text file for linker errors.
 MessageBox(hwnd, L"Error compiling linker.  Check linker-error.txt for message.", L"Linker Error", MB_OK);

 return;
}

ShutdownShader releases the shaders and the shader program.
ShutdownShader方法用来释放着色器资源。
void ColorShaderClass::ShutdownShader(OpenGLClass* OpenGL)
{
 // Detach the vertex and fragment shaders from the program.
 OpenGL->glDetachShader(m_shaderProgram, m_vertexShader);
 OpenGL->glDetachShader(m_shaderProgram, m_fragmentShader);

 // Delete the vertex and fragment shaders.
 OpenGL->glDeleteShader(m_vertexShader);
 OpenGL->glDeleteShader(m_fragmentShader);

 // Delete the shader program.
 OpenGL->glDeleteProgram(m_shaderProgram);

 return;
}

The SetShaderVariables function exists to make setting the uniform variables in the shader easier. The matrices used in this function are created inside the GraphicsClass, after which this function is called to send them from there into the vertex shader during the Render function call.
SetShaderVariables方法为了方便设置着色器的一致变量。GraphicsClass创建了将要使用的矩阵,在渲染方法调用时,本方法将这些矩阵传递到顶点着色器。
bool ColorShaderClass::SetShaderParameters(OpenGLClass* OpenGL, float* worldMatrix, float* viewMatrix, float* projectionMatrix)
{
 unsigned int location;

 // Set the world matrix in the vertex shader.
 location = OpenGL->glGetUniformLocation(m_shaderProgram, "worldMatrix");
 if(location == -1)
 {
  return false;
 }
 OpenGL->glUniformMatrix4fv(location, 1, false, worldMatrix);

 // Set the view matrix in the vertex shader.
 location = OpenGL->glGetUniformLocation(m_shaderProgram, "viewMatrix");
 if(location == -1)
 {
  return false;
 }
 OpenGL->glUniformMatrix4fv(location, 1, false, viewMatrix);

 // Set the projection matrix in the vertex shader.
 location = OpenGL->glGetUniformLocation(m_shaderProgram, "projectionMatrix");
 if(location == -1)
 {
  return false;
 }
 OpenGL->glUniformMatrix4fv(location, 1, false, projectionMatrix);

 return true;
}

Modelclass.h

As stated previously the ModelClass is responsible for encapsulating the geometry for 3D models. In this tutorial we will manually setup the data for a single green triangle. We will also create a vertex and index buffer for the triangle so that it can be rendered.
前面描述过ModelClass是用来封装3D几何模型的。本教程我们将手动设置一个绿色的三角形数据。同时我们也将创建渲染需要的顶点和索引缓冲区数据。
////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "openglclass.h"

////////////////////////////////////////////////////////////////////////////////
// Class name: ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:

Here is the definition of our vertex type that will be used with the vertex buffer in this ModelClass. Also take note that this typedef must match the input variables layout in the ColorShaderClass as well as the GLSL vertex shader.
下面是ModelClass中顶点缓冲区用的VertexType的定义。注意,类型定义必须与ColorShaderClass和GLSL顶点着色器中的输入类型对应。
struct VertexType
 {
  float x, y, z;
  float r, g, b;
 };

public:
 ModelClass();
 ModelClass(const ModelClass&);
 ~ModelClass();

The functions here handle initializing and shutdown of the model‘s vertex and index buffers. The Render function puts the model geometry on the video card and draws it using the GLSL color shader.
下面两个方法处理初始化和关闭模型的顶点和索引缓冲区。Render方法将几何模型发送给显卡并通过GLSL着色器进行渲染。
bool Initialize(OpenGLClass*);
 void Shutdown(OpenGLClass*);
 void Render(OpenGLClass*);

private:
 bool InitializeBuffers(OpenGLClass*);
 void ShutdownBuffers(OpenGLClass*);
 void RenderBuffers(OpenGLClass*);

The private variables in the ModelClass are the vertex array object, vertex buffer, and index buffer IDs. Also there are two integers to keep track of the size of the vertex and index buffers.
ModelClass定义的私有变量是顶点数组对象、顶点缓冲区和索引缓冲区ID。还定义了两个整形变量来保存顶点的和索引的数量。

private:
 int m_vertexCount, m_indexCount;
 unsigned int m_vertexArrayId, m_vertexBufferId, m_indexBufferId;
};

#endif

Modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"

ModelClass::ModelClass()
{
}

ModelClass::ModelClass(const ModelClass& other)
{
}

ModelClass::~ModelClass()
{
}

The Initialize function will call the initialization functions for the vertex and index buffers.
Initialize方法调用顶点和索引缓冲区的初始化方法。
bool ModelClass::Initialize(OpenGLClass* OpenGL)
{
 bool result;

 // Initialize the vertex and index buffer that hold the geometry for the triangle.
 result = InitializeBuffers(OpenGL);
 if(!result)
 {
  return false;
 }

 return true;
}

The Shutdown function will call the shutdown functions for the buffers and related data.
Shutdown方法调用缓冲区及相关数据的关闭方法。
void ModelClass::Shutdown(OpenGLClass* OpenGL)
{
 // Release the vertex and index buffers.
 ShutdownBuffers(OpenGL);

 return;
}

Render is called from the GraphicsClass::Render function. This function calls RenderBuffers to put the vertex and index buffers on the graphics pipeline and uses the color shader to render them.
GraphicsClass::Render方法会调用下面的Render方法。此方法调用RenderBuffers方法将顶点和索引缓冲区放入图形管线并使用着色器渲染他们。
void ModelClass::Render(OpenGLClass* OpenGL)
{
 // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
 RenderBuffers(OpenGL);

 return;
}

The InitializeBuffers function is where we handle creating the vertex and index buffers. Usually you would read in a model and create the buffers from that data file. For this tutorial we will just set the points in the vertex and index buffer manually since it is only a single triangle.
InitializeBuffers方法用来创建顶点和索引缓冲区。一般情况下这些数据是可以从模型的数据文件获取的。本章通过手动方式来设置一个三角形用的顶点和索引缓冲区数据。
bool ModelClass::InitializeBuffers(OpenGLClass* OpenGL)
{
 VertexType* vertices;
 unsigned int* indices;

First create two temporary arrays to hold the vertex and index data that we will use later to populate the final buffers with.
首先创建两个临时数组来保存顶点和索引数据。
// Set the number of vertices in the vertex array.
 m_vertexCount = 3;

 // Set the number of indices in the index array.
 m_indexCount = 3;

 // Create the vertex array.
 vertices = new VertexType[m_vertexCount];
 if(!vertices)
 {
  return false;
 }

 // Create the index array.
 indices = new unsigned int[m_indexCount];
 if(!indices)
 {
  return false;
 }

Now fill both the vertex and index array with the three points of the triangle as well as the index to each of the points. Please note that I create the points in the clockwise order of drawing them. If you do this counter clockwise it will think the triangle is facing the opposite direction and not draw it due to back face culling. Always remember that the order in which you send your vertices to the GPU is very important. The color is set here as well since it is part of the vertex description. I set the color to green.
下面来填充顶点和索引数组。注意,我使用顺时针方向创建点。如果使用逆时针方向,OpenGL会认为三角形是背向读者的,如果开启背向剔除就不会进行渲染。记住,顶点的顺序对于GPU很重要。颜色也是顶点信息的一部分。我设置了绿色。
// Load the vertex array with data.

 // Bottom left.
 vertices[0].x = -1.0f;  // Position.
 vertices[0].y = -1.0f;
 vertices[0].z =  0.0f;

 vertices[0].r = 0.0f;  // Color.
 vertices[0].g = 1.0f;
 vertices[0].b = 0.0f;

 // Top middle.
 vertices[1].x = 0.0f;  // Position.
 vertices[1].y = 1.0f;
 vertices[1].z = 0.0f;

 vertices[1].r = 0.0f;  // Color.
 vertices[1].g = 1.0f;
 vertices[1].b = 0.0f;

 // Bottom right.
 vertices[2].x =  1.0f;  // Position.
 vertices[2].y = -1.0f;
 vertices[2].z =  0.0f;

 vertices[2].r = 0.0f;  // Color.
 vertices[2].g = 1.0f;
 vertices[2].b = 0.0f;

 // Load the index array with data.
 indices[0] = 0;  // Bottom left.
 indices[1] = 1;  // Top middle.
 indices[2] = 2;  // Bottom right.

With the vertex array and index array filled out we can now use those to create the vertex buffer and index buffer. But first we must create a vertex array object which will store all the information about the buffers and attributes so that we can make a single call to the vertex array object to handle all the rendering for us. Once the vertex array object is bound we can create the vertex and index buffers and load the data into them from the temporary arrays that we created above. We also bind the vertex array attributes so that it knows the format of the data inside the vertex buffer. Setting the last parameter in the glVertexAttribPointer function is important so that it knows the offset of where each vertex buffer element begins.
顶点和索引数组填充好后我们就可以使用他们创建顶点和索引缓冲区了。但首先要创建一个顶点数组对象用来保存缓冲区和属性信息,这样我们通过一次调用就可以使用顶点数组数据来进行渲染。顶点数组对象绑定后,就可以使用之前的临时数组创建顶点和索引缓冲区了。我们也绑定顶点数组属性,这样就知道顶点缓冲区内的数据格式。使用glVertexAttribPointer方法使着色器知道在顶点缓冲区里的起始位置和偏移量。
 // Allocate an OpenGL vertex array object.
 OpenGL->glGenVertexArrays(1, &m_vertexArrayId);

 // Bind the vertex array object to store all the buffers and vertex attributes we create here.
 OpenGL->glBindVertexArray(m_vertexArrayId);

 // Generate an ID for the vertex buffer.
 OpenGL->glGenBuffers(1, &m_vertexBufferId);

 // Bind the vertex buffer and load the vertex (position and color) data into the vertex buffer.
 OpenGL->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
 OpenGL->glBufferData(GL_ARRAY_BUFFER, m_vertexCount * sizeof(VertexType), vertices, GL_STATIC_DRAW);

 // Enable the two vertex array attributes.
 OpenGL->glEnableVertexAttribArray(0);  // Vertex position.
 OpenGL->glEnableVertexAttribArray(1);  // Vertex color.

 // Specify the location and format of the position portion of the vertex buffer.
 OpenGL->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
 OpenGL->glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexType), 0);

 // Specify the location and format of the color portion of the vertex buffer.
 OpenGL->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
 OpenGL->glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (3 * sizeof(float)));

 // Generate an ID for the index buffer.
 OpenGL->glGenBuffers(1, &m_indexBufferId);

 // Bind the index buffer and load the index data into it.
 OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
 OpenGL->glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount* sizeof(unsigned int), indices, GL_STATIC_DRAW);

After the vertex buffer and index buffer have been created you can delete the vertex and index arrays as they are no longer needed since the data was copied into the buffers.
顶点和索引缓冲区创建好后,删除我们不再使用的数据。
// Now that the buffers have been loaded we can release the array data.
 delete [] vertices;
 vertices = 0;

 delete [] indices;
 indices = 0;

 return true;
}

The ShutdownBuffers function releases the buffers and attributes that were created in the InitializeBuffers function.
ShutdownBuffers方法释放我们在InitializeBuffers方法里创建的缓冲区和属性。
void ModelClass::ShutdownBuffers(OpenGLClass* OpenGL)
{
 // Disable the two vertex array attributes.
 OpenGL->glDisableVertexAttribArray(0);
 OpenGL->glDisableVertexAttribArray(1);
 
 // Release the vertex buffer.
 OpenGL->glBindBuffer(GL_ARRAY_BUFFER, 0);
 OpenGL->glDeleteBuffers(1, &m_vertexBufferId);

 // Release the index buffer.
 OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 OpenGL->glDeleteBuffers(1, &m_indexBufferId);

 // Release the vertex array object.
 OpenGL->glBindVertexArray(0);
 OpenGL->glDeleteVertexArrays(1, &m_vertexArrayId);

 return;
}

RenderBuffers is called from the Render function. The purpose of this function is to set the vertex buffer and index buffer as active on the input assembler in the GPU by binding the OpenGL vertex array object. Once the GPU has an active vertex buffer it can then use the currently set shader to render that buffer. This function also defines how those buffers should be drawn such as triangles, lines, fans, and so forth. In this tutorial we set the vertex buffer and index buffer as active on the input assembler and tell the GPU that the buffers should be drawn as triangles using the glDrawElements OpenGL function. The glDrawElements function indicates also that we will be drawing using an index buffer.
RenderBuffers方法被Render方法调用。本函数的目的是通过绑定OpenGL顶点数组对象(VAO)激活GPU顶点和索引缓冲区。GPU激活顶点缓冲区后可使用着色器渲染缓冲区。次方法也定义了缓冲区的绘制方式,比如三角形、线、扇形等等。本教程我们使用OpenGL的glDrawElements方法让GPU将缓冲区数据绘制为三角形。glDrawElements方法也表示使用索引缓冲区进行绘制。
void ModelClass::RenderBuffers(OpenGLClass* OpenGL)
{
 // Bind the vertex array object that stored all the information about the vertex and index buffers.
 OpenGL->glBindVertexArray(m_vertexArrayId);

 // Render the vertex buffer using the index buffer.
 glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, 0);

 return;
}

Cameraclass.h

We have examined how to code GLSL shaders, how to setup vertex and index buffers, and how to invoke the GLSL shaders to draw those buffers using the ColorShaderClass. The one thing we are missing however is the view point to draw them from. For this we will require a camera class to let OpenGL 4.0 know from where and also how we are viewing the scene. The camera class will keep track of where the camera is and its current rotation. It will use the position and rotation information to generate a view matrix which will be passed into the GLSL shaders for rendering.
我们已经解释了如何编写GLSL代码、如何设置顶点和索引缓冲区、如何使用ColorShaderClass调用GLSL着色器绘制缓冲区数据。还差一件事没说就是从怎样的观察点来绘制物体。为达到这个目的,我们需要一个摄像机类让OpenGL 4.0知道我们是如何观察场景的。摄像机类记录摄像机的位置和旋转。它用位置和旋转信息来产生GLSL着色器使用的视口矩阵。
////////////////////////////////////////////////////////////////////////////////
// Filename: cameraclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _CAMERACLASS_H_
#define _CAMERACLASS_H_

//////////////
// INCLUDES //
//////////////
#include <math.h>

////////////////////////////////////////////////////////////////////////////////
// Class name: CameraClass
////////////////////////////////////////////////////////////////////////////////
class CameraClass
{
private:
 struct VectorType
 {
  float x, y, z;
 };

public:
 CameraClass();
 CameraClass(const CameraClass&);
 ~CameraClass();

 void SetPosition(float, float, float);
 void SetRotation(float, float, float);

 void Render();
 void GetViewMatrix(float*);

private:
 void MatrixRotationYawPitchRoll(float*, float, float, float);
 void TransformCoord(VectorType&, float*);
 void BuildViewMatrix(VectorType, VectorType, VectorType);

private:
 float m_positionX, m_positionY, m_positionZ;
 float m_rotationX, m_rotationY, m_rotationZ;
 float m_viewMatrix[16];
};

#endif

The CameraClass header is quite simple with just four functions that will be used. The SetPosition and SetRotation functions will be used to set the position and rotation of the camera object. Render will be used to create the view matrix based on the position and rotation of the camera. And finally GetViewMatrix will be used to retrieve the view matrix from the camera object so that the shaders can use it for rendering.
CameraClass的头文件非常简单只包含了几个方法。SetPosition和SetRotation方法用来设置摄像机的位置和旋转。Render方法使用位置和旋转创建视口矩阵。最后GetViewMatrix方法返回着色器渲染用的视口矩阵。

Cameraclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: cameraclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "cameraclass.h"
The class constructor will initialize the position and rotation of the camera to be at the origin of the scene.

CameraClass::CameraClass()
{
 m_positionX = 0.0f;
 m_positionY = 0.0f;
 m_positionZ = 0.0f;

 m_rotationX = 0.0f;
 m_rotationY = 0.0f;
 m_rotationZ = 0.0f;
}

CameraClass::CameraClass(const CameraClass& other)
{
}

CameraClass::~CameraClass()
{
}

The SetPosition and SetRotation functions are used for setting up the position and rotation of the camera.

void CameraClass::SetPosition(float x, float y, float z)
{
 m_positionX = x;
 m_positionY = y;
 m_positionZ = z;
 return;
}

void CameraClass::SetRotation(float x, float y, float z)
{
 m_rotationX = x;
 m_rotationY = y;
 m_rotationZ = z;
 return;
}

The Render function uses the position and rotation of the camera to build and update the view matrix. We first setup our variables for up, position, rotation, and so forth. Then at the origin of the scene we first rotate the camera based on the x, y, and z rotation of the camera. Once it is properly rotated we then translate the camera to the position in 3D space. With the correct values in the position, lookAt, and up we can then use the BuildViewMatrix function to create the view matrix to represent the current camera rotation and translation.
Render方法使用摄像机的位置和旋转构建和更新视口矩阵。首先设置摄像机的正方向、位置、旋转等等。然后从场景的起始位置旋转摄像机。接着将摄像机在3D空间进行位移。通过位置、观察点和正方向的值调用BuildViewMatrix方法创建视口矩阵。
void CameraClass::Render()
{
 VectorType up, position, lookAt;
 float yaw, pitch, roll;
 float rotationMatrix[9];

 // Setup the vector that points upwards.
// 设置摄像机的正方向
 up.x = 0.0f;
 up.y = 1.0f;
 up.z = 0.0f;

 // Setup the position of the camera in the world.
 position.x = m_positionX;
 position.y = m_positionY;
 position.z = m_positionZ;

 // Setup where the camera is looking by default.
 lookAt.x = 0.0f;
 lookAt.y = 0.0f;
 lookAt.z = 1.0f;

 // Set the yaw (Y axis), pitch (X axis), and roll (Z axis) rotations in radians.
 pitch = m_rotationX * 0.0174532925f;
 yaw   = m_rotationY * 0.0174532925f;
 roll  = m_rotationZ * 0.0174532925f;

 // Create the rotation matrix from the yaw, pitch, and roll values.
 MatrixRotationYawPitchRoll(rotationMatrix, yaw, pitch, roll);

 // Transform the lookAt and up vector by the rotation matrix so the view is correctly rotated at the origin.
 TransformCoord(lookAt, rotationMatrix);
 TransformCoord(up, rotationMatrix);
 
 // Translate the rotated camera position to the location of the viewer.
 lookAt.x = position.x + lookAt.x;
 lookAt.y = position.y + lookAt.y;
 lookAt.z = position.z + lookAt.z;

 // Finally create the view matrix from the three updated vectors.
 BuildViewMatrix(position, lookAt, up);

 return;
}

The following function creates a left handed rotation matrix from the yaw, pitch, and roll values.
yaw、pitch、roll分别对应y、x、z轴。
void CameraClass::MatrixRotationYawPitchRoll(float* matrix, float yaw, float pitch, float roll)
{
 float cYaw, cPitch, cRoll, sYaw, sPitch, sRoll;

 // Get the cosine and sin of the yaw, pitch, and roll.
 cYaw = cosf(yaw);
 cPitch = cosf(pitch);
 cRoll = cosf(roll);

 sYaw = sinf(yaw);
 sPitch = sinf(pitch);
 sRoll = sinf(roll);

 // Calculate the yaw, pitch, roll rotation matrix.
 matrix[0] = (cRoll * cYaw) + (sRoll * sPitch * sYaw);
 matrix[1] = (sRoll * cPitch);
 matrix[2] = (cRoll * -sYaw) + (sRoll * sPitch * cYaw);
 
 matrix[3] = (-sRoll * cYaw) + (cRoll * sPitch * sYaw);
 matrix[4] = (cRoll * cPitch);
 matrix[5] = (sRoll * sYaw) + (cRoll * sPitch * cYaw);
 
 matrix[6] = (cPitch * sYaw);
 matrix[7] = -sPitch;
 matrix[8] = (cPitch * cYaw);

 return;
}

The following function multiplies a 3 float vector by a 3x3 matrice and returns the result back in the input vector.
下面的方法将一个由3个float类型的向量与3x3的矩阵相乘,并将返回的结果保存到输入的向量里。
void CameraClass::TransformCoord(VectorType& vector, float* matrix)
{
 float x, y, z;

 // Transform the vector by the 3x3 matrix.
 x = (vector.x * matrix[0]) + (vector.y * matrix[3]) + (vector.z * matrix[6]);
 y = (vector.x * matrix[1]) + (vector.y * matrix[4]) + (vector.z * matrix[7]);
 z = (vector.x * matrix[2]) + (vector.y * matrix[5]) + (vector.z * matrix[8]);

 // Store the result in the reference.
 vector.x = x;
 vector.y = y;
 vector.z = z;

 return;
}

The following function builds a left handed view matrix.
下面的方法构建了一个左手坐标系视口矩阵。
void CameraClass::BuildViewMatrix(VectorType position, VectorType lookAt, VectorType up)
{
 VectorType zAxis, xAxis, yAxis;
 float length, result1, result2, result3;

 // zAxis = normal(lookAt - position)
 zAxis.x = lookAt.x - position.x;
 zAxis.y = lookAt.y - position.y;
 zAxis.z = lookAt.z - position.z;
 length = sqrt((zAxis.x * zAxis.x) + (zAxis.y * zAxis.y) + (zAxis.z * zAxis.z));
 zAxis.x = zAxis.x / length;
 zAxis.y = zAxis.y / length;
 zAxis.z = zAxis.z / length;

 // xAxis = normal(cross(up, zAxis))
 xAxis.x = (up.y * zAxis.z) - (up.z * zAxis.y);
 xAxis.y = (up.z * zAxis.x) - (up.x * zAxis.z);
 xAxis.z = (up.x * zAxis.y) - (up.y * zAxis.x);
 length = sqrt((xAxis.x * xAxis.x) + (xAxis.y * xAxis.y) + (xAxis.z * xAxis.z));
 xAxis.x = xAxis.x / length;
 xAxis.y = xAxis.y / length;
 xAxis.z = xAxis.z / length;

 // yAxis = cross(zAxis, xAxis)
 yAxis.x = (zAxis.y * xAxis.z) - (zAxis.z * xAxis.y);
 yAxis.y = (zAxis.z * xAxis.x) - (zAxis.x * xAxis.z);
 yAxis.z = (zAxis.x * xAxis.y) - (zAxis.y * xAxis.x);

 // -dot(xAxis, position)
 result1 = ((xAxis.x * position.x) + (xAxis.y * position.y) + (xAxis.z * position.z)) * -1.0f;

 // -dot(yaxis, eye)
 result2 = ((yAxis.x * position.x) + (yAxis.y * position.y) + (yAxis.z * position.z)) * -1.0f;

 // -dot(zaxis, eye)
 result3 = ((zAxis.x * position.x) + (zAxis.y * position.y) + (zAxis.z * position.z)) * -1.0f;

 // Set the computed values in the view matrix.
 m_viewMatrix[0]  = xAxis.x;
 m_viewMatrix[1]  = yAxis.x;
 m_viewMatrix[2]  = zAxis.x;
 m_viewMatrix[3]  = 0.0f;

 m_viewMatrix[4]  = xAxis.y;
 m_viewMatrix[5]  = yAxis.y;
 m_viewMatrix[6]  = zAxis.y;
 m_viewMatrix[7]  = 0.0f;

 m_viewMatrix[8]  = xAxis.z;
 m_viewMatrix[9]  = yAxis.z;
 m_viewMatrix[10] = zAxis.z;
 m_viewMatrix[11] = 0.0f;

 m_viewMatrix[12] = result1;
 m_viewMatrix[13] = result2;
 m_viewMatrix[14] = result3;
 m_viewMatrix[15] = 1.0f;

 return;
}

After the Render function has been called to create the view matrix we can provide the updated view matrix to calling functions using this GetViewMatrix function. The view matrix will be one of the three main matrices used in the GLSL vertex shader.
调用Render方法创建视口矩阵后我们可以通过调用GetViewMatrix方法更新矩阵。视口矩阵式GLSL顶点着色器使用的3个重要矩阵之一。
void CameraClass::GetViewMatrix(float* matrix)
{
 matrix[0]  = m_viewMatrix[0];
 matrix[1]  = m_viewMatrix[1];
 matrix[2]  = m_viewMatrix[2];
 matrix[3]  = m_viewMatrix[3];

 matrix[4]  = m_viewMatrix[4];
 matrix[5]  = m_viewMatrix[5];
 matrix[6]  = m_viewMatrix[6];
 matrix[7]  = m_viewMatrix[7];

 matrix[8]  = m_viewMatrix[8];
 matrix[9]  = m_viewMatrix[9];
 matrix[10] = m_viewMatrix[10];
 matrix[11] = m_viewMatrix[11];

 matrix[12] = m_viewMatrix[12];
 matrix[13] = m_viewMatrix[13];
 matrix[14] = m_viewMatrix[14];
 matrix[15] = m_viewMatrix[15];

 return;
}

Graphicsclass.h

GraphicsClass now has the three new classes added to it. CameraClass, ModelClass, and ColorShaderClass have headers added here as well as private member variables. Remember that GraphicsClass is the main class that is used to render the scene by invoking all the needed class objects for the project.
GraphicsClass增加了3个类的引用。分别增加CameraClass, ModelClass, ColorShaderClass的头文件和私有成员变量。GraphicsClass是渲染场景的主要类,它来负责在需要的时候调用其他的对象。
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "openglclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "colorshaderclass.h"

/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = true;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;

////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
 GraphicsClass();
 GraphicsClass(const GraphicsClass&);
 ~GraphicsClass();

 bool Initialize(OpenGLClass*, HWND);
 void Shutdown();
 bool Frame();

private:
 bool Render();

private:
 OpenGLClass* m_OpenGL;
 CameraClass* m_Camera;
 ModelClass* m_Model;
 ColorShaderClass* m_ColorShader;
};

#endif

Graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"

The first change to GraphicsClass is initializing the camera, model, and color shader objects in the class constructor to null.
GraphicsClass::GraphicsClass()
{
 m_OpenGL = 0;
 m_Camera = 0;
 m_Model = 0;
 m_ColorShader = 0;
}

GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}

GraphicsClass::~GraphicsClass()
{
}

The Initialize function has also been updated to create and initialize the three new objects.
Initialize方法增加了新的内容。
bool GraphicsClass::Initialize(OpenGLClass* OpenGL, HWND hwnd)
{
 bool result;

 // Store a pointer to the OpenGL class object.
 m_OpenGL = OpenGL;

 // Create the camera object.
 m_Camera = new CameraClass;
 if(!m_Camera)
 {
  return false;
 }

 // Set the initial position of the camera.
 m_Camera->SetPosition(0.0f, 0.0f, -10.0f);

 // Create the model object.
 m_Model = new ModelClass;
 if(!m_Model)
 {
  return false;
 }

 // Initialize the model object.
 result = m_Model->Initialize(m_OpenGL);
 if(!result)
 {
  MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
  return false;
 }
 
 // Create the color shader object.
 m_ColorShader = new ColorShaderClass;
 if(!m_ColorShader)
 {
  return false;
 }

 // Initialize the color shader object.
 result = m_ColorShader->Initialize(m_OpenGL, hwnd);
 if(!result)
 {
  MessageBox(hwnd, L"Could not initialize the color shader object.", L"Error", MB_OK);
  return false;
 }

 return true;
}

Shutdown is also updated to shut down and release the three new objects.
Shutdown方法同样增加了内容。
void GraphicsClass::Shutdown()
{
 // Release the color shader object.
 if(m_ColorShader)
 {
  m_ColorShader->Shutdown(m_OpenGL);
  delete m_ColorShader;
  m_ColorShader = 0;
 }

 // Release the model object.
 if(m_Model)
 {
  m_Model->Shutdown(m_OpenGL);
  delete m_Model;
  m_Model = 0;
 }

 // Release the camera object.
 if(m_Camera)
 {
  delete m_Camera;
  m_Camera = 0;
 }

 // Release the pointer to the OpenGL class object.
 m_OpenGL = 0;

 return;
}

The Frame function has remained the same as the previous tutorial.
Frame方法和前面教程的一样。
bool GraphicsClass::Frame()
{
 bool result;

 // Render the graphics scene.
 result = Render();
 if(!result)
 {
  return false;
 }

 return true;
}

As you would expect the Render function had the most changes to it. It still begins with clearing the scene except that it is cleared to black. After that it calls the Render function for the camera object to create a view matrix based on the camera‘s location that was set in the Initialize function. Once the view matrix is created we get a copy of it from the camera class. We also get copies of the world and projection matrix from the OpenGLClass object. Next we set the color GLSL shaders as the current rendering program so that anything drawn will use those vertex and pixel shaders. Then we call the ModelClass::Render function to draw the green triangle model geometry. The green triangle is now drawn to the back buffer. With that the scene is complete and we call EndScene to display it to the screen.
本章对Render方法的改动较大。它还是从清除场景开始,只是修改了清除颜色为黑色。然后调用了摄像机对象的Render方法创建基于摄像机位置的视口矩阵。视口矩阵创建好后我们从摄像机类复制一份。同时从OpenGLClass对象复制世界和投影矩阵。然后设置GLSL着色器为当前渲染程序,这样所有的绘制都会使用顶点和像素着色器。然后调用ModelClass::Render方法绘制绿色的三角形。现在,绿色三角形绘制在后台缓冲区里。场景准备好后,我们调用EndScene方法把场景显示到屏幕上。
bool GraphicsClass::Render()
{
 float worldMatrix[16];
 float viewMatrix[16];
 float projectionMatrix[16];

 // Clear the buffers to begin the scene.
 m_OpenGL->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

 // Generate the view matrix based on the camera's position.
 m_Camera->Render();

 // Get the world, view, and projection matrices from the opengl and camera objects.
 m_OpenGL->GetWorldMatrix(worldMatrix);
 m_Camera->GetViewMatrix(viewMatrix);
 m_OpenGL->GetProjectionMatrix(projectionMatrix);

 // Set the color shader as the current shader program and set the matrices that it will use for rendering.
 m_ColorShader->SetShader(m_OpenGL);
 m_ColorShader->SetShaderParameters(m_OpenGL, worldMatrix, viewMatrix, projectionMatrix);

 // Render the model using the color shader.
 m_Model->Render(m_OpenGL);
 
 // Present the rendered scene to the screen.
 m_OpenGL->EndScene();

 return true;
}

Summary
总结
技术分享

So in summary you should have learned the basics about how vertex and index buffers work and how the vertex array object encapsulates them. You should have also learned the basics of vertex and pixel shaders and how to write them using GLSL. And finally you should understand how we‘ve incorporated these new concepts into our frame work to produce a green triangle that renders to the screen. I also want to mention that I realize the code is fairly long just to draw a single triangle and it could have all been stuck inside a single main() function. However I did it this way with a proper frame work so that the coming tutorials require very few changes in the code to do far more complex graphics.
本章介绍了顶点和索引缓冲区的工作原理,及顶点数组对象(VAO)是怎样封装他们的。本章还介绍了顶点和像素着色器及如何使用GLSL来编写他们。最后介绍了如何将这些新的内容合并到框架里并在屏幕上绘制一个绿色的三角形。有人可能会问用了这么多的代码来画一个简单的三角形,为何不全部放在main()方法中来实现。这样做的目的是构造一个通用的框架,这样后面的教程只需要修改很少的代码就能绘制更加复杂的图形。

To Do Exercises
练习

1. Compile and run the tutorial. Ensure it draws a green triangle to the screen. Press escape to quit once it does.
1. 编译并运行教程代码。确保在屏幕上绘制了一个绿色三角形。按ESC键退出程序。

2. Change the color of the triangle to red.
2. 将三角形的颜色改为红色。

3. Change the triangle to a square.
3. 将三角形修改为方形。

4. Move the camera back 10 more units.
4. 将摄像机向后移动10个单位。

5. Change the pixel shader to output the color half as bright. (huge hint: multiply something in pixel shader by 0.5f)
5. 修改像素着色器输出颜色的亮度减半。(提示:输出颜色值乘以0.5f)


Source Code
源代码

http://www.rastertek.com/gl40src04.zip

第4章:缓冲区、着色器、GLSL

标签:opengl

原文地址:http://blog.csdn.net/weyson/article/details/46853327

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!