Qt和OpenGL都是是跨平台的库、因此在学习OpenGL的时候,如果熟悉Qt库,在Qt中使用OpenGL将是一件非常愉快的事情、由于Qt对平台GUI的封装,使得我们不用关心平台窗口的事情,同时,Qt的QGLWidget对OpenGL本事也做了一些封装,为在学习使用OpenGL的时候,提供了很大的便利,不用一开始就过于考虑细节的事情。
本文以OpenGL4.0为基础、介绍一个简单的顶点数组对象的(VAO)使用方法。为了验证怎么使用VAO,创建了一个平面网格类,将一个平面矩形按三角形序列渲染出来。首先看看平面网格类的定义:
class CVxPlaneMesh: public Drawable
{
public:
CVxPlaneMesh(int iRow = 10, int iCol = 10 , float fWidth = 10, float fHeight = 10);
virtual ~CVxPlaneMesh(void);
void render() const;
private:
GLuint m_iVAO; //顶点数组对象。
int m_iFaces; //多少个三角形面片
};
{
int iVertexCnt = ( iRow + 1) * ( iCol + 1);
float* pVertex = new float[ iVertexCnt * 3];
int iIndexCnt = 2 * 3 * iRow * iCol;
unsigned int* pIndex = new unsigned int[ iIndexCnt ];
m_iFaces = iRow * iCol;
float fDeltaX = fWidth / iCol;
float fDeltaY = fHeight / iRow;
for ( int i = 0; i <= iRow ; i ++)
{
for (int j = 0; j <= iCol; j ++ )
{
float* pPos = pVertex + ( i * ( iCol + 1 ) + j ) * 3; //第多少个顶点,再*3,
* pPos = j * fDeltaX - 0.5 * fWidth; //X
*(pPos + 1) = 0; //Y
*(pPos + 2) = i * fDeltaY - 0.5 * fHeight; //Z
}
}
//顶点搞定,现在搞定索引。
for ( int i = 0; i < iRow ; i ++)
{
for (int j = 0; j < iCol; j ++ )
{
unsigned int* p = pIndex + 2 * 3 * ( i * iCol + j ); //每个方格有两个三角形组成,
*p++ = (i + 1) * (iCol + 1) + j;
*p++ = i * (iCol + 1) + j;
*p++ = i * (iCol + 1) + j + 1;
*p++ = (i + 1) * (iCol + 1) + j;
*p++ = (i * (iCol + 1)) + j + 1;
*p = (i + 1) * (iCol + 1) + j + 1;
}
}
//生成VAO
glGenVertexArrays( 1, &m_iVAO );
glBindVertexArray(m_iVAO);
GLuint iHandle[2];
glGenBuffers(2, iHandle);
glBindBuffer(GL_ARRAY_BUFFER,iHandle[0]);
glBufferData(GL_ARRAY_BUFFER,3 * (iRow+1) * (iCol+1) * sizeof(float),(void*) pVertex, GL_STATIC_DRAW);
glVertexAttribPointer( (GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, ((GLubyte *)NULL + (0)) );
glEnableVertexAttribArray(0); // Vertex position
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iHandle[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * iRow * iCol * sizeof(unsigned int),(void*)pIndex, GL_STATIC_DRAW );
glBindVertexArray(0);
delete[] pVertex;
delete[] pIndex;
}
CVxPlaneMesh::~CVxPlaneMesh(void)
{
}
void CVxPlaneMesh::render() const
{
glBindVertexArray(m_iVAO);
glDrawElements(GL_TRIANGLES, 6 * m_iFaces, GL_UNSIGNED_INT, ((GLubyte *)NULL + (0)));
}
以上是平面网格类的定义,现在将这个网格放到一个场景中去,场景类的定义如下:
class CVxPlaneScene : public Scene{
private:
GLSLProgram prog;
int width, height;
CVxPlaneMesh *plane;
float angle;
mat4 model;
mat4 view;
mat4 projection;
void setMatrices();
void compileAndLinkShader();
public:
CVxPlaneScene();
void initScene();
void update( float t );
void render();
void resize(int, int);
};
CVxPlaneScene::CVxPlaneScene() : angle(0.0f)
{
}
void CVxPlaneScene::initScene()
{
compileAndLinkShader();
glClearColor(0.0,0.0,0.0,1.0);
glEnable(GL_DEPTH_TEST);
plane = new CVxPlaneMesh();
view = glm::lookAt(vec3(0.0f,10.0f,0.0f), vec3(0.0f,0.0f,0.0f), vec3(0.0f,0.0f,-1.0f));
projection = glm::ortho(-10.0, 10.0, -10.0, 10.0, 0.0, 100.0);/*mat4(1.0f);*/
}
void CVxPlaneScene::update( float t )
{
//angle += 1.0f;
//if( angle > 360.0 ) angle -= 360.0;
}
void CVxPlaneScene::render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE );
model = mat4(1.0f);
setMatrices();
plane->render();
}
void CVxPlaneScene::setMatrices()
{
mat4 mv = view * model;
prog.setUniform("MVP", projection * mv);
}
void CVxPlaneScene::resize(int w, int h)
{
glViewport(0,0,w,h);
width = w;
height = h;
projection = glm::perspective(70.0f, (float)w/h, 0.3f, 100.0f);
}
void CVxPlaneScene::compileAndLinkShader()
{
if( ! prog.compileShaderFromFile("shader/PlaneScene.vert",GLSLShader::VERTEX) )
{
printf("Vertex shader failed to compile!\n%s",
prog.log().c_str());
exit(1);
}
if( ! prog.compileShaderFromFile("shader/PlaneScene.frag",GLSLShader::FRAGMENT))
{
printf("Fragment shader failed to compile!\n%s",
prog.log().c_str());
exit(1);
}
if( ! prog.link() )
{
printf("Shader program failed to link!\n%s",
prog.log().c_str());
exit(1);
}
if( ! prog.validate() )
{
printf("Program failed to validate!\n%s",
prog.log().c_str());
exit(1);
}
prog.use();
}
这里的Shader代码很简单,只是简单的做个变换。
#version 400
layout (location = 0) in vec3 VertexPosition;
uniform mat4 MVP;
void main()
{
gl_Position = MVP * vec4(VertexPosition,1.0);
}
#version 400
layout( location = 0 ) out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
将场景渲染到QGLWidget窗口中,显示如下图形