这篇博客,我是准备介绍几个经典的案例,分别是 Hello window,然后是hello triangle,并且会解释涉及到的一些关于着色器的相关知识。好,那么跟随小编,进入主题

第一个,hello window.

看代码:

#include <iostream>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;// The MAIN function, from here we start the application and run the game loop
int main()
{std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// Create a GLFWwindow object that we can use for GLFW's functionsGLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;// Initialize GLEW to setup the OpenGL Function pointersif (glewInit() != GLEW_OK){std::cout << "Failed to initialize GLEW" << std::endl;return -1;}// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);// Game loopwhile (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Swap the screen buffersglfwSwapBuffers(window);}// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;
}// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{std::cout << key << std::endl;if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

ok,看到代码的第一件事情,就是copy,然后粘贴在自己的.cpp文件下,然后按照上一篇博客的相关配置,然后右击重新生成解决方案,然后调试–>开始执行(不调试),然后结果如下:
然后我们来分析代码:
从上往下:

#include <iostream>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;

以上代码做的事情: 包含相关的库,然后声明函数,然后定义几个常量值,好,接着就是Main函数主体:

std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

这堆代码感觉的话,就是一个套路吧,就是用opengl,你都是要写这一堆代码。接着,

GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;

这堆代码是说:我们定义一个window窗体,然后的话,给这个窗体设置大小,然后还绑定一个函数,这里顺便把这个函数代码给加上(就是开头声明的那个函数)

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{std::cout << key << std::endl;if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

这个函数做的事情,其实很简单,就是说,当我们按下esc键的时候,程序就会退出。ok,好的,继续看。

    if (glewInit() != GLEW_OK){std::cout << "Failed to initialize GLEW" << std::endl;return -1;}// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);

这里做的事情,主要是定义了一个视角,viewport。如果不是很懂的话,可以试着改下width为width/2之类,可以看看哈。(不过好像没有什么改变,哈哈,不管了,这个不是很重要啊,接着看)

// Game loopwhile (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Swap the screen buffersglfwSwapBuffers(window);}// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;
}

以上的代码,是核心,就是我们绘制的图形,在没有按esc键退出之前,是要不断的刷新的。这里,我们每次刷新,是要把之前屏幕画面给全部刷新一遍,是用glClearColor(0.2f, 0.3f, 0.3f, 1.0f);这个函数来指定重新填充的颜色,后面的函数,就是指定一些类型,然后填充。好,这里我们进行一个小小的修改,加入个随机数,来改变我们的颜色,看看有什么效果,当然在c语言中,如果要用rand()函数,我们要这么用。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

加上以上头文件,在main函数第一行加上

srand(time(0));

以上是生成随机数种子
glClearColor((float)((rand()%100)/50)+0.5f, (float)((rand()%100) /50)+0.5f, (float)((rand()%100) / 100)+0.5f, 1.0f);
相应代码位置改成以上的这种,注意一点,是这里都是处于标准状态下,就是都是-1~1之间,所以如果超过1了,是无效的噢。效果如下:

是不是感觉有点意思。好,那我们进入下一个案例,

Hello triangle.

好,首先上代码,还是一样,直接copy进.cpp文件中即可,然后按照之前一样的配置就行:

#include <iostream>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;// Shaders
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";// The MAIN function, from here we start the application and run the game loop
int main()
{// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// Create a GLFWwindow object that we can use for GLFW's functionsGLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;// Initialize GLEW to setup the OpenGL Function pointersglewInit();// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);// Build and compile our shader program// Vertex shaderGLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// Check for compile time errorsGLint success;GLchar infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// Fragment shaderGLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// Check for compile time errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// Link shadersGLuint shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// Check for linking errorsglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);// Set up vertex data (and buffer(s)) and attribute pointersGLfloat vertices[] = {-0.5f, -0.5f, 0.0f, // Left  0.5f, -0.5f, 0.0f, // Right 0.0f,  0.5f, 0.0f  // Top   };GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbindglBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)// Game loopwhile (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);glBindVertexArray(0);// Swap the screen buffersglfwSwapBuffers(window);}// Properly de-allocate all resources once they've outlived their purposeglDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;
}// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

运行的结果如下:

然后我们开始分析代码:

#include <iostream>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;

这段代码,和上一次的hello window代码一样,没有差别。都是添加库文件,声明函数,定义常量。接着看:

// Shaders
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

这里开始和之前不一样了,这里的代码,虽然看起来挺复杂的,但是我们仔细看看,其实就是定义了两个GLchar*指针变量,可能我们不了解GLchar类型,但是我们可以类比char类型,那么我们可以把以上代码理解为定义了两个字符串常量,而已。其实,后面我们就会知道,
那个字符串里面就是shadow语言,好吧,这里我们可能有点蒙了,我们可以这样理解,这里就像在python里面调用c++语言的程序一样。字符串里面就是另一种语言的源代码。接下来我们就来看main函数里面的代码:

std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// Create a GLFWwindow object that we can use for GLFW's functionsGLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;// Initialize GLEW to setup the OpenGL Function pointersglewInit();// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);

ok,到现在为止,代码和之前的hello world,代码是一致。接着看:

// Build and compile our shader program// Vertex shaderGLint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// Check for compile time errorsGLint success;GLchar infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}

这里的代码,内容就是把之前的shadow语言源代码,在这里进行编译。

GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);

这里就是把之前定义的字符串(内容就是shadow语言源代码)与我们的vertexShader变量联系起来。然后进行编译。这里是顶点着色器,然后是片段着色器。

// Fragment shaderGLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// Check for compile time errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}

这里的原理和上面一致。

// Link shadersGLint shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// Check for linking errorsglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);

以上代码就是把我们shader变量的和程序链接起来。接着:

// Set up vertex data (and buffer(s)) and attribute pointersGLfloat vertices[] = {-0.5f, -0.5f, 0.0f, // Left  0.5f, -0.5f, 0.0f, // Right 0.0f,  0.5f, 0.0f  // Top   };GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbindglBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)

好了,到这里的话,我们注意一点,就是以上两个着色器,其实是有执行顺序的,具体的执行流程,如图:

那我们在这个过程中,可能比较关心的问题就是,由顶点数据到顶点着色器的这个过程:好。里面涉及到几个关系:
首先我们的顶点数据就是一个数组格式,显然就是vertices,然后我们定义了一个最大的变量,叫 VAO,顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中,之后我们可以添加一个VBO,指的是一个顶点缓冲对象,当然有,顶点数组对象里面可以包含很多的顶点缓冲对象,然后又有每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取则是通过在调用glVetexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。由于在调用glVetexAttribPointer之前绑定的是先前定义的VBO对象,所以逻辑上是说可以唯一找到对应的顶点数据。所以这种逻辑下,我们可以在绑定一个VBO时,指定相应的顶点数据,然后我们绑定一个新的VBO,如果没有的话,就是0,然后我们指定新的顶点数组对象VAO,通过glbindVertexArray()这个方法我们来指定新的VAO,如果没有就是0。好的,到这里我们就建立一个完整的顶点数组对象。然后里面存放了相关的点数据。接着:

    while (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);glBindVertexArray(0);// Swap the screen buffersglfwSwapBuffers(window);}

其中的重点就是:

        // Draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);glBindVertexArray(0);

使用上面的shadow对象,glUseProgram(shaderProgram);然后指定一个VAO,里面就是有很多顶点,然后我们使用

glDrawArrays(GL_TRIANGLES, 0, 3);

glDrawArrays函数第一个参数是我们打算绘制的OpenGL图元的类型。由于我们在一开始时说过,我们希望绘制的是一个三角形,这里传递GL_TRIANGLES给它。第二个参数指定了顶点数组的起始索引,我们这里填0。最后一个参数指定我们打算绘制多少个顶点,这里是3(我们只从我们的数据中渲染一个三角形,它只有3个顶点长)。至于前后都是指定顶点对象数组,从VAO->0,然后双缓冲中把图像输出到屏幕。
接着

    glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;

我们就得删除那个VAO定点对象数组,然后删除VBO,然后结束整个程序。ok,好的,那么这里问题来的是,如果我们现在要画两个三角形,怎么画,现在能行的办法只有一个,就是

GLfloat vertices[] = {// 第一个三角形0.5f, 0.5f, 0.0f,   // 右上角0.5f, -0.5f, 0.0f,  // 右下角-0.5f, 0.5f, 0.0f,  // 左上角// 第二个三角形0.5f, -0.5f, 0.0f,  // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f   // 左上角};

然后最后指定绘制的总个数时候为6个,即

glDrawArrays(GL_TRIANGLES, 0, 6);

然后结果的话,如图所示:

然后的话,这里注意一点,

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);  //这里的3是是一个顶点有3个float值,不是三个点的意思,不能指定要几个点

好的,到这里来,我们可以看到,这样的方式显然是比较笨的。特别是当我们的三角形,本来有公共点或者公共边时,这样其实是多余存储了点,比如一个矩形,要两个三角形,其实只要4个点,但是通过这种方法,我们一共储存了6个点,所以我们有改进办法,索引缓冲对象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO),和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。与VBO类似,我们先绑定EBO然后用glBufferData把索引复制到缓冲里。同样,和VBO类似,我们会把这些函数调用放在绑定和解绑函数调用之间,只不过这次我们把缓冲的类型定义为GL_ELEMENT_ARRAY_BUFFER。还有,我们要在制定了定点缓冲对象之间,进行索引缓冲对象的绑定与数据输入

   glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbindglBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs), remember: do NOT unbind the EBO, keep it bound to this VAO

我们先指定VBO,然后输入顶点数据,然后绑定索引缓冲对象,然后输入索引数据,

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

然后再去给顶点进行属性赋值。最后画图是函数名变为:

//glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)

最后附上最终的代码:

#include <iostream>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;// Shaders
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";// The MAIN function, from here we start the application and run the game loop
int main()
{// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// Create a GLFWwindow object that we can use for GLFW's functionsGLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;// Initialize GLEW to setup the OpenGL Function pointersglewInit();// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);// Build and compile our shader program// Vertex shaderGLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// Check for compile time errorsGLint success;GLchar infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// Fragment shaderGLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// Check for compile time errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// Link shadersGLuint shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// Check for linking errorsglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);// Set up vertex data (and buffer(s)) and attribute pointersGLfloat vertices[] = {// 第一个三角形0.5f, 0.5f, 0.0f,   // 右上角0.5f, -0.5f, 0.0f,  // 右下角-0.5f, 0.5f, 0.0f,  // 左上角// 第二个三角形0.5f, -0.5f, 0.0f,  // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f   // 左上角};GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);//glGenBuffers(1, &VBO1);// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);  //这里的3便是一个顶点有3个float值glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbindglBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)// Game loopwhile (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 6);glBindVertexArray(0);// Swap the screen buffersglfwSwapBuffers(window);}// Properly de-allocate all resources once they've outlived their purposeglDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;
}// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

最终结果:

ok,熬到现在,终于把这两个案例的原理说清楚啦,好的,下次见啦。

小朱opengl学习笔记(二)-----小白入门案例介绍相关推荐

  1. 小朱opengl学习笔记(三)------着色器的详细学习

    上篇博客,主要是介绍了Opengl的大致使用流程,由于中间过程可能比较复杂, 所以今天笔者首先梳理一遍整个的流程. 祭宝图哈. 上篇博客我们的重点是放在由 顶点数据-->顶点着色器 的这个过程上 ...

  2. 小朱opengl学习笔记(一)

    第一课:vs2015+opengl3.3环境配置(windows环境) 首先,说明下,本人是一名大三学生,由于这个学期选修了计算机图形学,老师要求我们使用opengl来实现.本人在配置的过程中,遇到很 ...

  3. 现代OpenGL学习笔记二:第一个三角形

    本笔记主要是跟学LearnOpenGL的内容,并将其中一些不懂的地方通过查阅资料进行整理补充,推荐参考原文: 整个教程:https://learnopengl-cn.github.io/ 本笔记参考教 ...

  4. OpenGL学习笔记(二)

    一.OpenGL简介 OpenGL(Open Graphics Library)是一个跨编程语言.跨平台的编程图形程序接口,它将计算机的资源抽象称为一个个OpenGL的对象,对这些资源的操作抽象为一个 ...

  5. android opengl旋转,Android openGl学习笔记二,gl的旋转、位移、放大缩小

    这章文章是对上一篇的进一步学习,最终以达到类似动画效果的目的. 不管什么时候参考资料放在第一位:学习参考资料 这个是本人经过层层筛选后感觉比较全且比较易懂的文章了,学习记录将按照此链接的博客逐步深入学 ...

  6. OpenGl学习笔记二:创建自己的着色器

    1. GLSL 着色器是使用一种叫GLSL的类C语言写成的.GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性. 从基本意义上来说,着色器只是一种把输入转化为输出的程序.着色器也是 ...

  7. 学习笔记 | Orillusion-WebGPU小白入门(六)

    (六)Texture 1.基础介绍 贴图:将图片的内容贴在物体的表面上,对应片元的位置,输出原图片对应的像素信息. GPU如何匹配平面坐标和图片的像素位置 贴图的坐标系:与NDC坐标一样,WebGPU ...

  8. 2023-01-03 Echarts学习笔记(二) 常见Option配置项介绍:xAxis,yAxis,series,grid,toolbox,legend,tooltip,title,color等

    Echarts的基础配置 1.color:调色盘颜色列表 2.title:标题组件 2.1.设置图表的标题 2.2.同时主标题和副标题(了解) 3.tooltip:提示框组件 触发类型 4.legen ...

  9. TCP/IP协议学习笔记(二)TCP与UDP介绍

    TCP/IP中有两个具有代表性的传输层协议,它们分别是TCP和UDP.TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输. IP首部中有一个协议字段,用来标识网络层(IP) ...

最新文章

  1. 在CentOS_Linux版虚拟机中安装VMTools工具
  2. 数学理论—— 蒙特卡洛近似
  3. PresentViewController详解
  4. 学学python(4):面向对象及类,模块
  5. 数据结构--图(Graph)详解(四)
  6. python学习-列表的操作(常用函数均会介绍)
  7. Peacock:大规模主题模型及其在腾讯业务中的应用-2015
  8. P4888 三去矩阵
  9. nbu备份oracle rac,利用NETBACKUP将备份写到磁盘上
  10. 严加监管是否能解决超载问题
  11. Centos7 Springboot 启动脚本
  12. 期货权益可用资金(期货的权益和可用资金)
  13. Linux/ Unix 键盘检测程序
  14. docker运行yyets_docker 使用教程1
  15. Scratch软件编程等级考试三级——20200913
  16. Juniper Junos设置3层接口
  17. 【ES】CURL在windows中对ElasticSearch的一些简单的操作
  18. word如何首页和目录不编辑页码
  19. v12.2.8 released版本介绍--2019_7
  20. c++实现地图控制扫雷(界面优化版

热门文章

  1. 渐进地了解渐进式框架Vue
  2. python制作翻译小软件_python 制作一个翻译软件
  3. 5G工业智能网关行业应用
  4. 思维导图PPT七步制作法
  5. python实现音乐播放和下载小程序功能
  6. “英雄联盟注册页面”来咯~~
  7. 【工作技巧】Ubuntu备忘记录
  8. Redis五种数据类型应用场景详解(超级详细版)
  9. http://tech2ipo.com/79181
  10. 30岁后,投简历没面试,年纪太大没救了?