OPENGL是一個底層的圖形庫,可以跨平台,我們這裡學習的是他的可程式設計渲染管線。我會對完整代碼的每一句做講解。請仔細讀一讀。
推薦一個學習網站https://learnopengl-cn.github.io/
一、建立一個視窗
完整代碼
說明:這一部分主要作為了解,建立視窗的過程是固定的,沒有什麼知識可言。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
//導入的兩個OPENGL庫,注意這倆是有順序的
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);。
void processInput(GLFWwindow *window);
//這兩個函數是回調函數,第一個是調節視窗大小的,第二個是退出視窗的。
int main()
{
glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//附版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//設定為可程式設計渲染模式
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
//建立一個GLFW視窗,前邊兩個是長寬,第三個是視窗标題,後邊兩個NULL不用管,寫NULL就行。
if (window == NULL)//如果視窗建立失敗了
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();//銷毀視窗
return -1;//退出main
}
glfwMakeContextCurrent(window);//這個也是必須寫的,沒有為啥
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//設定這個回調
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
//這個glad是給OPENGL做函數指針用的,它也需要建立,如果建立失敗了就退出,和上邊那個GLFW的一樣。
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
while (!glfwWindowShouldClose(window))
{
//這塊叫做渲染循環,因為你要繪制的東西肯定是動态的,咱這是3D,是以東西必須得是循環繪制的,很好了解。
processInput(window);//這個自己看去
glfwSwapBuffers(window);//這個是繪制,告訴OPENGL需要繪制。
glfwPollEvents();//檢測觸發事件,我需要檢測是否觸發事件。在這裡是按ESC退出,實作這個在processInput裡邊。
}
glfwTerminate();//銷毀視窗,上邊出現過。
return 0;
}
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//如果點選ESC
glfwSetWindowShouldClose(window, true);//正常退出
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
//讓視口,也就是渲染區域大小跟着視窗大小變,比如我全屏了,那麼渲染的區域也得是全屏。
}
二、建立三角形
基本概念的認識:
1.圖元:組成圖形的最小機關,我們這裡是三角形,這節課畫的也是三角形。這個怎麼了解呢,你去找一個OBJ模型,然後放大了看,你會發現這個OBJ的3D模型是由一個一個的三角形組成的(有的是四邊形),這個三角形(或是四邊形)就叫圖元。
2.光栅化:我們的顯示屏叫光栅顯示屏,也就是圖形顯示都是用像素塊顯示的,這個大家都知道。把圖形轉化成像素塊的過程就叫光栅化。
3.着色器(Shader):運作在GPU上的程式,OPENGL裡分為兩個,頂點着色器和片段着色器,前者是繪制頂點的,你看不到,後者是讓GPU往你電腦屏上顯示東西的,點連成面。還有一個着色器叫幾何着色器,我們不咋用。當然也可以做計算,看這個程式你怎麼寫了。
4.渲染:就是顯示東西的意思。
5.VBO,EBO,VAO:VBO叫頂點緩沖對象,VAO叫頂點數組對象,EBO叫索引緩沖對象。啥意思呢,就是說VBO是存頂點的,EBO是存頂點繪制順序的,最後這倆都要放進VAO裡,友善管理。這個要在程式裡體會。
完整代碼
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//兩個着色器程式
//頂點着色器
const char *vertexShaderSource = "#version 330 core\n"//版本号
"layout (location = 0) in vec3 aPos;\n"//要從0号通道接收資料,定義的頂點資料。vec3是個向量。
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//這個就是告訴着色器是頂點了gl_Position是自帶的一個變量。
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//繪圖了,FragColor 也是自帶的,前邊是RGB值。
"}\n\0";
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//建立shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
//綁定源代碼
glCompileShader(vertexShader);
int success;
char 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 shader//和上邊一樣
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 連接配接shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//查錯
glGetProgramiv(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);
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
//定義頂點資料
unsigned int VBO, VAO;//建立VAO,VBO對象
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
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(float), (void*)0);//1、0号location錄入資料2、三個資料3、浮點類型4、不轉化為标準空間,一般都是FALSE 5、步長 6、位置在數組的開頭,是以是0
glEnableVertexAttribArray(0);//這個也是說0号location
glBindBuffer(GL_ARRAY_BUFFER, 0); //綁定VAO
glBindVertexArray(0);
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//背景顔色
glClear(GL_COLOR_BUFFER_BIT);//清空顔色緩沖區
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);//畫三角形
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}