文章目录
LearnGL - 学习笔记目录
本人才疏学浅,如有什么错误,望不吝指出。
上一篇:LearnGL - 02 - DrawTriangle、LearnGL - 02.1 - DrawTriangle_Extension,学会了如何画一个三角形。
这一篇:画一个 Quad(Quadrilateral) 四边形。
如何绘制?
之前已经画了一个三角形了。
那么画四边形就简单多了,两个三角形组合成一个四边形不就好了,如下图:
就是两个直角三角形的组合嘛,那么开始咯。
先画第一个直角三角形
先看看之前的文章话的
这个显然不是直角三角,需要调整一下数值。
之前的数值为:
float vertices[] = {
// x, y, z r, g, b
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
};
调整为:
float vertices[] = {
// x, y, z r, g, b
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
};
绘制效果
再画第二个直角三角形
最简单的思维是:直接添加多三个顶点即可。(后面会使用更好的方式)
float vertices[] = {
// x, y, z r, g, b
// 第一个直角三角性
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
// 第二个直角三角性
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
};
然后是 glDrawArray 的参数调整为绘制6个顶点:
原来是绘制3个顶点的:
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角图元,从0号顶点,开始绘制3个顶点
调整为绘制6个顶点的:
glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制三角图元,从0个顶点,开始绘制3个顶点
绘制效果
查看正面的顺/逆时针
可以通过 glGet 中的 glGetIntegerv
第一个参数使用符号常量:GL_FRONT_FACE
,第二个参数传入返回的指针 GLint*
即可拿到当前正面的设置。
也可以通过 glFrontFace 传入 GL_CW
或是 GL_CCW
来设置正面面向。
//glFrontFace(GL_CW); // 顺时针
//glFrontFace(GL_CCW); // 逆时针(默认的)ClockWise
GLint facing;
glGetIntegerv(GL_FRONT_FACE, &facing); // 获取正面的顺逆时针 : CW(ClockWise - 顺时针), CCW(Counter ClockWise - 逆时针)
std::cout << "facing : " << (facing == GL_CW ? "CW" : "CCW") << std::endl;
输出:facing : CCW
可以看到,我将 glFrontFace
函数都注释了,默认是 CCW 的。
我们的顶点的组合成的三角形的顶点顺序,一定要与Front Face 正面面向是一致,否则会有问题,如下图,CW(Clockwise=顺时针)与CCW(Counter Clockwise=逆时针):
jave.lin : 注意下图中的 CW, CCW 的示意图画反了,-_-,后续修复
使用 索引缓存 EBO/IBO 来节省显存
前面为了画一个四角形,我们用了多少个顶点,多少数据量?
float vertices[] = {
// x, y, z r, g, b
// 第一个直角三角性
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
// 第二个直角三角性
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
};
用了6个顶点,每个顶点有一个 xyz 表示坐标需要3个float,每个float需要4 bytes,3x4=12
,需要12 bytes,同样 rgb 表示颜色也需要3 float==12 bytes,所有 xyz + rgb = 12 + 12 = 24 bytes
,最终需要 24 bytes。
强调一下,最终 每个顶点 需要 24 bytes。
共 6 个顶点,需要:6 x 24 = 144 bytes
,144 bytes。
数据量需要的不少呢,就为了画一个带顶点颜色的 四边形。
一些复杂的效果每个顶点需要的更多,除了:xyz, rgb, 还有 uv(texture coordinates纹理坐标), normals(法线), 等。那么需要的数据帧的很多了,所以有什么办法可以节省顶点数量呢?
我们先来看看上面的数据有什么特性:
float vertices[] = {
// x, y, z r, g, b
// 第一个直角三角性
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
// 第二个直角三角性
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 右上角
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
};
OK,我们将每个顶点位置标记一下,可以看出,有两个顶点的位置,两两重复着。
答案就是使用索引,OpenGL 可以使用 GL_ELEMENT_ARRAY_BUFFER
缓存对象 EBO/IBO=element buffer object/index buffer object。
所以我们将顶点数据改为 四个 顶点的数据,并添加 索引缓存 需要的数据:
float vertices[] = {
x, y, z r, g, b
第一个直角三角性
//-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角
// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
//-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
第二个直角三角性
// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
// 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 右上角
//-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
// x, y, z r, g, b
// 直接放4个顶点
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 第0个顶点,左下角
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 第1个顶点,右下角
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 第2个顶点,右上角
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 第3个顶点,左上角
};
GLuint indices[] = { // 注意索引从0开始! // 通过索引缓存来指定 图元 组成 用的 顶点有哪些
0, 1, 3, // 放置顶点的索引,第一个三角形
1, 2, 3 // 放置顶点的索引,第二个三角形
};
瞧瞧,那些重复的顶点数据都去掉了。另外注意我们的索引是从 0 开始的。
重新计算显存顶点+索引用量
现在我们用到了 4个顶点,每个顶点还是和之前一个多:24 bytes。
所以顶点总共需要 4 x 24 = 96 bytes
。96 bytes。而我们之前的是:144 bytes。
但我们多了一个 索引缓存 看看它的大小:6 个 uint ,每个 uint 也是需要 4 bytes,那么共需要 6 x 4 = 24 bytes
。24 bytes。
旧的顶点缓存数据量A -(减去) (新的 顶点缓存数据+索引缓存数据的和B)= 34 bytes。34 bytes就是整体节省量。
A - B = 34 bytes。
96 + 24 = 110 bytes
,110 bytes。相比之前 144 bytes 节省了,34 bytes个。
这看起来好像不太多,但是如果你想想复杂项目中,一个模型几万、几十万个顶面的超级高清模型,每个顶点既有:point(3 float), color(3 float), normal(tb = 2 float), uv(2 float), uv2(2 float), uv3(2 float),等,一个顶点可能就需要:3+3+2+2+2+2=14个float,14 x 4 = 56 bytes
。每个顶点就要 56 bytes
了。
反正单个顶点的数据越大,使用索引缓存能节省的内存量就越大。
几万个顶点的模型的话7W,那么光顶点数据就有:70000 x 56 = 3,920,000 bytes = 3,828.125 kb = 3.7384033203125 mb。
然后场景中,还有N个这样的模型。所以内存/显存节省,能省则省。
索引缓存 EBO/IBO 的创建、绑定、设置数据
那么接下来,下面演示如何创建 EBO/IBO 索引缓存:
GLint rmat_location, vpos_location, vcol_location;
GLuint vertex_buffer, index_buffer, vertex_shader, fragment_shader, program;
GLint success;
glGenBuffers(1, &vertex_buffer); // 创建 VBO
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); // 绑定 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 设置 VBO 数据
glGenBuffers(1, &index_buffer); // 创建 EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); // 绑定 EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 设置 EBO 数据
绘制
现在 已去除了重复的顶点数据,也有 索引缓存对象 了,那么就使用另一个 API 绘制:glDrawElements 来绘制。
//glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制三角图元,从0个顶点,开始绘制6个顶点
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (GLvoid*)0); // 参数1:绘制三角图元;参数2:取6个索引来绘制三角图元(每个三角图元需要3个,所以可以画两个三角图元);参数3:将 GL_ELEMENT_ARRAY_BUFFER 每个元素视为 uint 类型;参数4:设置索引缓存的字节偏移量。也可以设置为另一个 缓存数据的指针,即:使用另一个数据。
glDrawElements 函数的参数简单说一下:
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices);
mode
填入GL_TRIANGLES
,绘制三角图元。count
填入6
,取6个索引来绘制三角图元(每个三角图元需要3个,所以可以画两个三角图元)。type
填入GL_ELEMENT_ARRAY_BUFFER
将 GL_ELEMENT_ARRAY_BUFFER 每个元素视为 uint 类型。indices
填入0
,设置索引缓存的字节偏移量。也可以设置为另一个 缓存数据的指针,即:使用另一个数据。
绘制效果是一毛一样的,就不发图了。
完整源码
// jave.lin
#include"glad/glad.h"
#include"GLFW/glfw3.h"
//#include"linmath.h"
// 把linmath.h 放在 iostream 之前include会有错误,所以放到iostream 后include就好了
// 而这个错误正式 xkeycheck.h 文件内 #error 提示的,所以可以使用 #define _XKEYCHECK_H 这个头文件的引用标记宏
// 就可以避免对 xkeycheck.h 头文件的 include 了。
#include<iostream>
#include"linmath.h"
#define PRINT_VERSION // 打印版本相关信息
#ifdef PRINT_VERSION
#define PROFILE_NAME_CORE "core"
#define PROFILE_NAME_COMPAT "compat"
static const char* get_api_name(int api) {
if (api == GLFW_OPENGL_API)
return "OpenGL";
else if (api == GLFW_OPENGL_ES_API)
return "OpenGL ES";
return "Unknown API";
}
static const char* get_profile_name_gl(GLint mask) {
if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
return PROFILE_NAME_COMPAT;
if (mask & GL_CONTEXT_CORE_PROFILE_BIT)
return PROFILE_NAME_CORE;
return "unknown";
}
// 打印各种版本信息
static void print_infos(GLFWwindow* window) {
//
// ====== GLFW Library的版本 ======
//
int glfw_major, glfw_minor, glfw_revision;
glfwGetVersion(&glfw_major, &glfw_minor, &glfw_revision);
// 头文件声明版本
printf("GLFW header version: %u.%u.%u\n",
GLFW_VERSION_MAJOR,
GLFW_VERSION_MINOR,
GLFW_VERSION_REVISION);
// 库版本
printf("GLFW library version: %u.%u.%u\n", glfw_major, glfw_minor, glfw_revision);
// 库版本的字符串描述
printf("GLFW library version string: \"%s\"\n", glfwGetVersionString());
//
// ====== client, context, profile 的版本 ======
//
int ch, client, major, minor, revision, profile;
client = glfwGetWindowAttrib(window, GLFW_CLIENT_API);
major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION);
profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE);
printf("%s context version string: \"%s\"\n",
get_api_name(client),
glGetString(GL_VERSION));
printf("%s context version parsed by GLFW: %u.%u.%u\n",
get_api_name(client),
major, minor, revision);
GLint mask;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
printf("%s profile mask (0x%08x): %s\n",
get_api_name(client),
mask,
get_profile_name_gl(mask));
//
// ====== render, vendor 的信息 ======
//
printf("%s context renderer string: \"%s\"\n",
get_api_name(client),
glGetString(GL_RENDERER));
printf("%s context vendor string: \"%s\"\n",
get_api_name(client),
glGetString(GL_VENDOR));
printf("%s context shading language version: \"%s\"\n",
get_api_name(client),
glGetString(GL_SHADING_LANGUAGE_VERSION));
}
#endif // PRINT_VERSION
static const char* vertex_shader_text =
"#version 450 compatibility\n"
"uniform mat4 rMat;\n"
"attribute vec3 vPos;\n"
"attribute vec3 vCol;\n"
"varying vec3 fCol;\n"
"void main() {\n"
" gl_Position = rMat * vec4(vPos, 1.0);\n"
" fCol = vCol;\n"
"}\n";
static const char* fragment_shader_text =
"#version 450 compatibility\n"
"varying vec3 fCol;\n"
"void main() {\n"
" gl_FragColor = vec4(fCol, 1.0);\n"
"}\n";
GLfloat vertices[] = {
x, y, z r, g, b
第一个直角三角性
//-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角
// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
//-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
第二个直角三角性
// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 - 重复
// 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 右上角
//-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角 - 重复
// x, y, z r, g, b
// 直接放4个顶点
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 第0个顶点,左下角
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 第1个顶点,右下角
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 第2个顶点,右上角
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 第3个顶点,左上角
};
GLuint indices[] = { // 注意索引从0开始! // 通过索引缓存来指定 图元 组成 用的 顶点有哪些
0, 1, 3, // 放置顶点的索引,第一个三角形
1, 2, 3 // 放置顶点的索引,第二个三角形
};
static void error_callback(int error, const char* description) {
fprintf(stderr, "ErrorCode : %d(0x%08x), Error: %s\n", error, error, description);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { // 当键盘按键ESCAPE按下时,设置该window为:需要关闭
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
int main() {
glfwSetErrorCallback(error_callback); // 安装glfw内部错误时的回调
if (!glfwInit()) { // 初始化glfw
std::cout << "glfwInit FAILURE" << std::endl; // 初始化失败
exit(EXIT_FAILURE);
}
// 设置最低的openGL 版本,major:主版本号,minor:次版本号
// openGl 太低版本的话是不支持CORE Profile模式的
// 会报错:ErrorCode: 65540(0x00010004), Error : Context profiles are only defined for OpenGL version 3.2 and above
//glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
//glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// 根据上面的错误提示,至少使用3.2才行,这里我们使用4.5
//glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
//glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// core profile 下运行有问题,不显示任何内容,但不会报错。
// 着色器编译、着色器程序链接都没有错误日志信息。
// 很有可能是因为我参考的学习网站使用的API相对比较老,使用的是3.3的。
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 所以这里我们不设置 major, minor的版本,默认使用本计算机能用的最高版本
// 使用 compatibility profile 就有内容出现了。
int width = 400;
int height = 300;
// 使用glfw创建窗体
GLFWwindow* window = glfwCreateWindow(width, height, "jave.lin - Learning OpenGL - 02_01 DrawTriangle_Extensions", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl; // 构建窗体失败
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback); // 安装glfw内部键盘按键的回调
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { // 装载OpenGL的C函数库
std::cout << "Failed to initialize OpenGL context" << std::endl; // 装载报错
glfwTerminate();
exit(EXIT_FAILURE);
}
#ifdef PRINT_VERSION
// 打印版本信息
print_infos(window);
#endif
GLint rmat_location, vpos_location, vcol_location;
GLuint vertex_buffer, index_buffer, vertex_shader, fragment_shader, program;
GLint success, infoLogLen;
glGenBuffers(1, &vertex_buffer); // 创建 VBO
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); // 绑定 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 设置 VBO 数据
glGenBuffers(1, &index_buffer); // 创建 EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); // 绑定 EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 设置 EBO 数据
vertex_shader = glCreateShader(GL_VERTEX_SHADER); // 创建 顶点着色器
glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); // 设置 顶点着色器源码
glCompileShader(vertex_shader); // 编译 顶点着色器
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); // 获取着色器编译状态
if (!success) { // 如果编译不成功
glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &infoLogLen);
GLchar* infoLog = (GLchar*)malloc(infoLogLen); // 如果编译失败,则将编译日志储存到:infoLog 中
glGetShaderInfoLog(vertex_shader, infoLogLen, NULL, infoLog);
std::cout << "Vertex Shader Compiling Error Status: " // 输出编译错误日志信息
<< success << ", Infomation Log : " << infoLog << std::endl;
free(infoLog);
exit(EXIT_FAILURE);
}
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); // 创建 片元着色器
glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); // 设置 片元着色器源码
glCompileShader(fragment_shader); // 编译 片元着色器
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); // 获取着色器编译状态
if (!success) { // 如果编译不成功
glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &infoLogLen);
GLchar* infoLog = (GLchar*)malloc(infoLogLen); // 如果编译失败,则将编译日志储存到:infoLog 中
glGetShaderInfoLog(fragment_shader, infoLogLen, NULL, infoLog);
std::cout << "Fragment Shader Compiling Error Status: " // 输出编译错误日志信息
<< success << ", Infomation Log : " << infoLog << std::endl;
free(infoLog);
exit(EXIT_FAILURE);
}
program = glCreateProgram(); // 创建着色器程序
glAttachShader(program, vertex_shader); // 附加 顶点着色器
glAttachShader(program, fragment_shader); // 附加 片元着色器
glLinkProgram(program); // 链接着色器程序
glGetProgramiv(program, GL_LINK_STATUS, &success); // 如果链接不成功
if (!success) {
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
GLchar* infoLog = (GLchar*)malloc(infoLogLen); // 如果链接失败,则将链接日志储存到:infoLog 中
glGetProgramInfoLog(program, infoLogLen, NULL, infoLog);
std::cout << "Program Linking Error Status: " // 输出链接错误日志信息
<< success << ", Infomation Log : " << infoLog << std::endl;
free(infoLog);
exit(EXIT_FAILURE);
}
glDeleteShader(vertex_shader); // 链接成功后,就可以删掉vertex 子程序了
glDeleteShader(fragment_shader); // 链接成功后,就可以删掉fragment 子程序了
rmat_location = glGetUniformLocation(program, "rMat"); // 获取 着色器程序的 uniform 变量的 location
vpos_location = glGetAttribLocation(program, "vPos"); // 获取 顶点着色器中的顶点 attribute 属性的 location
vcol_location = glGetAttribLocation(program, "vCol"); // 获取 顶点着色器中的顶点 attribute 属性的 location
glVertexAttribPointer(vpos_location, 3, GL_FLOAT, GL_FALSE, // 设置 顶点属性 vPos 格式
sizeof(GLfloat) * 6, (GLvoid*)0);
glEnableVertexAttribArray(vpos_location); // 启用 顶点缓存 location 位置的属性
glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE, // 设置 顶点属性 vCol 格式
sizeof(GLfloat) * 6, (GLvoid*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(vcol_location); // 启用 顶点缓存 location 位置的属性
mat4x4 rMat; // 声明定义一个 mat4x4 用的旋转矩阵
glEnable(GL_CULL_FACE); // 开启面向剔除
glCullFace(GL_BACK); // 设置剔除背面
GLboolean cf = glIsEnabled(GL_CULL_FACE); // 查看是否启用面向剔除
std::cout << "cull face enabled : " << (cf ? "true" : "false") << std::endl;
//glFrontFace(GL_CW); // 顺时针
//glFrontFace(GL_CCW); // 逆时针(默认的)ClockWise
GLint facing;
glGetIntegerv(GL_FRONT_FACE, &facing); // 获取正面的顺逆时针 : CW(ClockWise - 顺时针), CCW(Counter ClockWise - 逆时针)
std::cout << "facing : " << (facing == GL_CW ? "CW" : "CCW") << std::endl;
bool rotation = false; // 是否旋转
while (!glfwWindowShouldClose(window)) { // 检测是否需要关闭窗体
glfwGetFramebufferSize(window, &width, &height); // 获取窗口大小
glViewport(0, 0, width, height); // 设置Viewport
glClearColor(0.1f, 0.2f, 0.1f, 0.f); // 设置清理颜色缓存时,填充颜色值
glClear(GL_COLOR_BUFFER_BIT); // 清理颜色缓存
float angle = rotation ? (float)glfwGetTime() : 0; // 旋转的角度
mat4x4_identity(rMat); // 给矩阵单位化,消除之前的所有变换
mat4x4_rotate_Y(rMat, rMat, angle); // 沿着 y 轴旋转,旋转量为当前 glfw 启用到现在的时间点(秒)
glUniformMatrix4fv(rmat_location, 1, GL_FALSE, (const GLfloat*)rMat); // 设置, 着色器中 uniform mat4 rMat; 的矩阵数据
glUseProgram(program); // 使用此着色器程序
//glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制三角图元,从0个顶点,开始绘制6个顶点
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (GLvoid*)0); // 参数1:绘制三角图元;参数2:取6个索引来绘制三角图元(每个三角图元需要3个,所以可以画两个三角图元);参数3:将 GL_ELEMENT_ARRAY_BUFFER 每个元素视为 uint 类型;参数4:索引缓存的字节偏移量。
glfwSwapBuffers(window); // swap buffer, from backbuffer to front buffer
glfwPollEvents(); // 处理其他的系统消息
}
glfwDestroyWindow(window); // 销毁之前创建的window对象
glfwTerminate(); // 清理glfw之前申请的资源
return 0;
} // int main() {