1.VBO
VBO归根到底是显卡存储空间里的一块缓存区(Buffer)而已,这个Buffer有它的名字(VBO的ID),OpenGL在GPU的某处记录着这个ID和对应的显存地址。这里对位置,颜色,纹理坐标,法线,还是其他顶点属性进行设置,但是一个VBO对于交给它存储的数据到底是什么,完全不知道。
2.VAO
VAO的全名是Vertex Array Object,首先,它不是Buffer-Object,所以不用作存储数据。其次,它针对”顶点“而言,也就是说它跟”顶点的绘制“息息相关,在GL3.0的世界观里,这相当于”与VBO息息相关“。它的定位是state-object(状态对象,记录存储状态信息)。这明显区别于buffer-object。VAO记录的是一次绘制中所需要的信息,这包括”数据在哪里-glBindBuffer(GL_ARRAY_BUFFER)“、”数据的格式是怎样的-glVertexAttribPointer“等等。
3.例子:
triangles.vert
attribute vec4 vVertex;
attribute vec4 vColor;
varying vec4 vFragColor;
void main(void)
{
vFragColor = vColor;
gl_Position = vVertex;
}
triangles.frag
varying vec4 vFragColor;
void main(void)
{
gl_FragColor = vFragColor;
}
main.cpp
#include <stdio.h>
#include <iostream>
#include <GL/glew.h>
#include <GLUT/GLUT.h>
using namespace std;
char *textFileRead(const char *fn) {
FILE *fp;
char *content = NULL;
int count=0;
if(fn!=NULL) {
fp=fopen(fn,"rt");
if(fp!=NULL){
fseek(fp,0,SEEK_END);
count=ftell(fp);//ftell,用于得到文件位置指针当前位置相对于文件首的偏移字节数,一般用于读取文件的长度
rewind(fp);//rewind,将文件内部的指针重新指向一个流的开头
if(count>0){
content=(char*)malloc(sizeof(char)*(count+1));
count=fread(content,sizeof(char),count,fp);
content[count]='\0';
}
fclose(fp);
}
}
return content;
}
GLuint vShader,fShader;//顶点着色器对象
//顶点位置数组
float positionData[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f };
//颜色数组
float colorData[] = {
1.0f, 0.0f, 0.0f,
0.0f,1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
GLuint vaoHandle;//vertex array object
void initShader(const char *VShaderFile, const char *FShaderFile)
{
//1、编译着色器
//创建着色器对象:顶点着色器
vShader = glCreateShader(GL_VERTEX_SHADER);
//错误检测
if (0 == vShader)
{
cerr<<"ERROR:Create vertex shader failed"<<endl;
exit(1);
}
//把着色器源代码和着色器对象相关联
const GLchar *vShaderCode = textFileRead(VShaderFile);
const GLchar *vCodeArray[1] = {vShaderCode};
glShaderSource(vShader, 1, vCodeArray, NULL);
//编译着色器对象
glCompileShader(vShader);
//检查编译是否成功
GLint compileResult;
glGetShaderiv(vShader,GL_COMPILE_STATUS,&compileResult);
if (GL_FALSE == compileResult)
{
GLint logLen;
//得到编译日志长度
glGetShaderiv(vShader,GL_INFO_LOG_LENGTH,&logLen);
if (logLen > 0)
{
char *log = (char *)malloc(logLen);
GLsizei written;
//得到日志信息并输出
glGetShaderInfoLog(vShader,logLen,&written,log);
cerr << "vertex shader compile log : " << endl;
cerr << log << endl;
free(log);//释放空间
}
}
//创建着色器对象:片断着色器
fShader = glCreateShader(GL_FRAGMENT_SHADER);
//错误检测
if (0 == fShader)
{
cerr << "ERROR : Create fragment shader failed" << endl;
exit(1);
}
//把着色器源代码和着色器对象相关联
const GLchar *fShaderCode = textFileRead(FShaderFile);
const GLchar *fCodeArray[1] = {fShaderCode};
glShaderSource(fShader,1,fCodeArray,NULL);
//编译着色器对象
glCompileShader(fShader);
//检查编译是否成功
glGetShaderiv(fShader,GL_COMPILE_STATUS,&compileResult);
if (GL_FALSE == compileResult)
{
GLint logLen;
//得到编译日志长度
glGetShaderiv(fShader,GL_INFO_LOG_LENGTH,&logLen);
if (logLen > 0)
{
char *log = (char *)malloc(logLen);
GLsizei written;
//得到日志信息并输出
glGetShaderInfoLog(fShader,logLen,&written,log);
cerr << "fragment shader compile log : " << endl;
cerr << log << endl;
free(log);//释放空间
}
}
//2、链接着色器对象
//创建着色器程序
GLuint programHandle = glCreateProgram();
if (!programHandle)
{
cerr << "ERROR : create program failed" << endl;
exit(1);
}
//将着色器程序链接到所创建的程序中
glAttachShader(programHandle,vShader);
glAttachShader(programHandle,fShader);
//将这些对象链接成一个可执行程序
glLinkProgram(programHandle);
//查询链接的结果
GLint linkStatus;
glGetProgramiv(programHandle,GL_LINK_STATUS,&linkStatus);
if (GL_FALSE == linkStatus)
{
cerr << "ERROR : link shader program failed" << endl;
GLint logLen;
glGetProgramiv(programHandle,GL_INFO_LOG_LENGTH,&logLen);
if (logLen > 0)
{
char *log = (char *)malloc(logLen);
GLsizei written;
glGetProgramInfoLog(programHandle,logLen,&written,log);
cerr << "Program log : " << endl;
cerr << log << endl;
}
}
else//链接成功,在OpenGL管线中使用渲染程序
{
glUseProgram(programHandle);
}
}
void initVBO()
{
//创建VBO
GLuint vboHandles[2];
glGenBuffers(2, vboHandles);
GLuint positionBufferHandle = vboHandles[0];
GLuint colorBufferHandle = vboHandles[1];
//绑定VBO以供使用
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
//加载数据到VBO
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float), positionData,GL_STATIC_DRAW);
//绑定VBO以供使用
glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);
//加载数据到VBO
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float), colorData,GL_STATIC_DRAW);
//创建VAO
glGenVertexArraysAPPLE(1, &vaoHandle);
glBindVertexArrayAPPLE(vaoHandle);
//此处不太清楚为什么vertexIndex为1,而colorIndex为0,可能是显卡,GL版本不同造成,感觉正常情况应该是positionIndex为0,colorIndex为1.
//如果顶点着色器只有顶点位置需要传入,则positionIndex为0。
int positionIndex = 1;
int colorIndex = 0;
//调用glVertexAttribPointer之前需要进行绑定操作
//默认情况下顶点着色器的属性(Attribute)变量都是关闭的,启用指定属性,才可在顶点着色器中访问逐顶点的属性数据
glEnableVertexAttribArray(positionIndex);//顶点坐标
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
glVertexAttribPointer(positionIndex,
3,//位置向量有x,y,z三个元素
GL_FLOAT, GL_FALSE,
0/*两个连续元素之间的偏移字节数*/,
(GLubyte *)NULL /*不使用VBO的情况下,指向的是需要上传到顶点数据指针。使用VBO的情况下,因为已经glBindBuffer,就不需要指定数据,此处为数据偏移量*/);
glEnableVertexAttribArray(colorIndex);//顶点颜色
glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
glVertexAttribPointer(colorIndex,
3,//颜色向量有r,g,b三个元素
GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );
glBindVertexArrayAPPLE(0); //解绑
}
void init()
{
//初始化glew扩展库
GLenum err = glewInit();
if( GLEW_OK != err )
{
cout <<"Error initializing GLEW: " << glewGetErrorString(err) << endl;
}
initShader("/Users/jlb/Documents/OpenGLTest/OpenGLTest/triangles.vert",
"/Users/jlb/Documents/OpenGLTest/OpenGLTest/triangles.frag");
initVBO();
glClearColor(0.0, 0.0, 0.0, 0.0);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
//使用VAO、VBO绘制
glBindVertexArrayAPPLE(vaoHandle);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArrayAPPLE(0);
glutSwapBuffers();
}
void keyboard(unsigned char key,int x,int y)
{
switch(key)
{
case 27:
glDeleteShader(vShader);
glUseProgram(0);
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(600,600);
glutInitWindowPosition(100,100);
glutCreateWindow("GLSL Test : Draw a triangle");
init();
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}