这一小节回顾一下基于C++的OpenGL Hello World程序代码。
这一小节,我们要绘制LearnOpenGL上的一个三角形实例,基于OpenGL+GLEW+FreeGLUT。我们用代码加注释的形式进行走读。
main.cpp文件:
#include "Angel.h"
#include <string>
const int NUM_POINTS = 3;
// 绑定数据都是有套路的,一般来说,就是:分配vao-绑定vao-分配vbo-绑定vbo-喂数据-读取着色器并使用-传数据给GPU
void init()
{
// 定义三角形的三个点,注意范围应该都在[-1,1]
vec2 vertices[3] = {
vec2(-0.75, -0.75), vec2(0.0, 0.75), vec2(0.75, -0.75)
};
// 创建顶点数组对象
GLuint vao;
#ifdef __APPLE__ // for MacOS
glGenVertexArraysAPPLE(1, &vao); // 分配1个顶点数组对象
glBindVertexArrayAPPLE(vao); // 绑定顶点数组对象
#else // for Windows
glGenVertexArrays(1, &vao); // 分配1个顶点数组对象
glBindVertexArray(vao); // 绑定顶点数组对象
#endif
// 创建顶点缓存对象
GLuint buffer;
// 分配1个顶点缓存对象
glGenBuffers(1, &buffer);
// 绑定顶点缓存对象
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 分配数据所需的存储空间,将数据拷贝到OpenGL服务端内存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 读取着色器并使用
std::string vshader, fshader;
#ifdef __APPLE__ // for MacOS
vshader = "shaders/vshader_mac.glsl";
fshader = "shaders/fshader_mac.glsl";
#else // for Windows
vshader = "shaders/vshader_win.glsl";
fshader = "shaders/fshader_win.glsl";
#endif
GLuint program = InitShader(vshader.c_str(), fshader.c_str());
glUseProgram(program);
// 从顶点着色器中初始化顶点的位置(在着色器中有vPosition这个变量,后面讲)
GLuint location = glGetAttribLocation(program, "vPosition");
// 启用顶点属性数组
glEnableVertexAttribArray(location);
// 关联到顶点属性数组 (index, size, type, normalized, stride, *pointer)
glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// 白色背景
glClearColor(1.0, 1.0, 1.0, 1.0);
}
void display(void)
{
// 清理窗口
glClear(GL_COLOR_BUFFER_BIT);
// 绘制所有点
glDrawArrays(GL_TRIANGLES, 0, NUM_POINTS);
glFlush();
}
int main(int argc, char **argv)
{
// 初始化GLUT库,必须是应用程序调用的第一个GLUT函数
glutInit(&argc, argv);
// 配置窗口的显示特性
glutInitDisplayMode(GLUT_RGBA);
// 配置窗口大小
glutInitWindowSize(512, 512);
// 创建窗口,指令窗口名
glutCreateWindow("Red Triangle");
// for Windows
#ifdef WIN32
// 检测是否使用了freeglut,并检测是否使用到了OpenGL 3.3
glutInitContextVersion(3, 3);
glutInitContextProfile(GLUT_CORE_PROFILE);
// 保证GLEW使用更多的现代技术管理OpenGL,防止core profile出现崩溃
glewExperimental = GL_TRUE;
glewInit();
#endif
// 定义、初始化、绑定三角形数据
init();
// 指定当前窗口进行重绘时要调用的函数
glutDisplayFunc(display);
// 负责一直处理窗口和操作系统的用户输入等操作
glutMainLoop();
return 0;
}
InitShader.cpp当成黑盒子使用就可以了,通吃顶点着色器和片元着色器:
#include "Angel.h"
namespace Angel {
// Create a NULL-terminated string by reading the provided file
static char*
readShaderSource(const char* shaderFile)
{
#ifdef __APPLE__ // for MacOS
FILE *fp;
fp = fopen(shaderFile, "r");
#else // for windows
FILE *fp;
fopen_s(&fp, shaderFile, "r");
#endif
if ( fp == NULL ) { return NULL; }
fseek(fp, 0L, SEEK_END);
long size = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char* buf = new char[size + 1];
memset(buf, 0, size + 1);
fread(buf, 1, size, fp);
buf[size] = '\0';
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();
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;
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
顶点着色器vshader.glsl:
#version 330 core
in vec3 vPosition;
void main()
{
gl_Position = vec4(vPosition, 1.0);
}
片元着色器fshader.glsl:
#version 330 core
out vec4 fColor;
void main()
{
fColor = vec4(0.3, 0.4, 0.5, 1.0);
}
让着色器稍微复杂一点(其实也不复杂→_→),我们就可以得到一个颜色插值三角形。
vshader.glsl:
#version 330 core
in vec3 vPosition;
out vec3 vColor;
void main()
{
gl_Position = vec4(vPosition, 1.0);
vColor = vPosition;
}
fshader.glsl:
#version 330 core
in vec3 vColor;
out vec4 fColor;
void main()
{
// 暂时把顶点位置信息当作颜色信息
fColor = vec4(vColor, 1.0);
}