opengl從畫三角形到畫一個立方體(二)
前一節內容,如果讀者能夠堅持看到最後,並且能夠將立方體畫出來,我相信還是有一點點的成就感的,畢竟我們從點、到三角形、再到立方體、再到立方體的旋轉一步一步的走,而且學會了VBO、VAO、EBO的資料傳輸、shader的使用等等最最基本的知識,這一步好艱難,但是畢竟走過來了。
那麼下面我們將繼續征途,本小節的內容,相對短一點,而且很容易明白。
本小節的主要內容,就是為前一小節的立方體,貼上一張圖,使用紋理。
那麼在opengl中我們如何去準備一張圖、準備一張什麼格式的圖、以及把這個圖放在程式的什麼位置、以及怎麼載入這個圖、以及怎麼將這個圖傳遞給shader、是傳遞給shader的頂點著色器還是片段著色器、傳遞的這種圖有什麼用,怎麼使用。
首先,我們來回到最後一個問題,這張圖到底有什麼用,為什麼要使用紋理。
上一節中,我們的三角形,在光柵化之後,變為具體的一個一個畫素,每個畫素我們都是用了唯一的顏色進行賦值,那麼如果我們想讓顏色豐富起來,比如顯示一張樹葉,一條狗,那麼是不是要對每個畫素的顏色都要賦值呢?是的,你必須對每個畫素點進行賦值一個顏色,這個工作量不是一般的大,於是我們就想能不能拍一張樹葉的圖、或者一條狗的圖,用手機、相機、或者psp、或者直接從網上下載一個圖片就能方便的實現貼圖效果,答案是可以的。哦,到現在明白了,為什麼要使用紋理,紋理就是讓顏色豐富起來,讓現實中的一個圖貼到一個物體上去,就是刷漆,就是上色的效果。
問題又來了,我們使用這個圖片的哪個位置的畫素去貼到光柵化之後的每個畫素點呢?是不是要有個紋理座標呢?比如下面的三角形和一張紋理的對應情況:
於是我們修改之前的資料,變化後為:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
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"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和建立視窗
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//設定
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三個頂點資料
float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.5f, 1.0f,
};
unsigned int VBO, VAO;
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, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
int width, height, nrChannels;
unsigned char *data = stbi_load("leaf.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
執行結果為:
我們看到上圖顯示的圖片並不太對,於是我們第一個想法是找網址上https://learnopengl.com/#!Getting-started/Textures
是如何把圖片顯示清楚的,我們第一個做的就是把圖片換成:
https://learnopengl.com/img/textures/wall.jpg
結果執行之後得到了如下的圖:
結果就這樣正確了,我的理解是我們剛才的圖片的解析度不夠,這個圖的解析度為512*512。
那麼此時我們要做的實驗是,把圖片縮小一點看看還能不能正常顯示。
比如我們將圖改為50*50的大小,如何把圖片改為50*50呢,方法很簡單,直接使用畫圖工具即可。
這樣再改小圖片之後,我們的程式執行結果為:
這樣就出現了上面的問題,也就證明了我們的猜測。讓我們結束這個反人類的三角形,畫一個矩形試試,程式碼如下:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
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"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和建立視窗
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//設定
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三個頂點資料
float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
};
unsigned int indices[] = {
0,1,2,
0,2,3,
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
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, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
注意這裡的索引陣列是unsigned int型別的,開始的時候,我寫成了float型別,結果在glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);卻是unsigned int型別,所以導致畫不出來。
執行結果如下:
下面我們接著做的就是,將立方體新增紋理,讓立方體的六個面貼上紋理。我們除了將頂點的資料,換回原來的立方體資料,還要在while中改變畫圖的方式為: glDrawArrays(GL_TRIANGLES, 0, 36);
但是這樣畫出的立方體我們說過了,還是一個矩形,沒有立體的感覺,只能看到一個面,所以還要講立方體旋轉起來,ok,所以還要改變頂點著色器的程式碼。把之前的讓立方體的旋轉程式碼貼上過來。
程式碼如下:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
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"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和建立視窗
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//設定
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三個頂點資料
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
unsigned int VBO, VAO;
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, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glm::mat4 transform;
transform = glm::rotate(transform, 10 * (float)glfwGetTime(), glm::vec3(1.0f, 1.0f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
執行的結果如下:
你可能覺得上面的效果非常的奇怪,是的,非常奇怪,奇怪的地方有兩點1、透了,近處的面看不到,遠處的面居然看到了;2、沒有透視的效果,即沒有近大遠小的效果。
我們來看看原來網址中的關於第1點的解釋,你們可能也看不太懂,所以我還是給你們講解一下,當然也是忠實原文的意思,opengl通過畫三角形的方式來畫立方體,但是沒有任何的引數來限制先畫的三角形是否被後畫的三角形覆蓋。所以當畫完離我們近的三角形之後,再畫一個遠的三角形,這樣後畫的三角形就會被覆蓋了。
那麼opengl是怎麼解決這個問題的呢,是通過一個深度值來判斷是否要覆蓋之前的畫素,也就是傳說中的z-buffer。opengl把每個畫素點的z軸存放在深度緩衝中,當我們畫一個畫素點的時候,如果這個點的在之前相同位置點的z之後,那麼則丟棄這個點,這個過程就是所謂的深度測試。
opengl把每個點的深度儲存起來,自動的,完全不用我們操作。而深度測試,也不需要我們做,我們只需要啟用深度測試即可。一句程式碼搞定,任性的很,不錯你看到的就是任性二字。
glEnable(GL_DEPTH_TEST);
我們是將這句程式碼加到while迴圈中,還是非while迴圈中呢?
都可以,但是隻需要設定一次即可,所以只需要放在非while中即可。
那麼此時我們允許程式碼會發現,呀!什麼都沒有了,我們的立方體去哪了呢?幸好,原文中緊接著講到,我們要清楚深度快取,要在while迴圈中清理上一幀的深度資訊,之前我們的清除,只是清除顏色緩衝,這裡要再加一個深度清除。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
這樣之後,我們的立方體就正常顯示了。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
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"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和建立視窗
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//設定
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三個頂點資料
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
unsigned int VBO, VAO;
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, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
glEnable(GL_DEPTH_TEST);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glm::mat4 transform;
transform = glm::rotate(transform, 10 * (float)glfwGetTime(), glm::vec3(1.0f, 1.0f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
執行結果為:
yep,我們看到了正常的立方體了。
那麼下面我們的立方體能不能多弄幾個立方體呢?我們要做的只需要平移一個立方體即可。講到這裡,我們似乎還是對座標系統不太熟悉,什麼是本地座標、什麼是世界座標、什麼是視口座標、什麼是裁剪空間、什麼又是透視除法、什麼又是ndc空間,搞這麼多概念到底又有啥作用?哎……一股涼風再次襲來,不禁看到好難、好抽象呀,沒事慢慢聽我道來。
相關推薦
opengl從畫三角形到畫一個立方體(二)
前一節內容,如果讀者能夠堅持看到最後,並且能夠將立方體畫出來,我相信還是有一點點的成就感的,畢竟我們從點、到三角形、再到立方體、再到立方體的旋轉一步一步的走,而且學會了VBO、VAO、EBO的資料傳輸、shader的使用等等最最基本的知識,這一步好艱難,但是畢竟
opengl從畫三角形到畫一個立方體(四)
小孔成像 圖片到底是怎麼形成的?如下圖所示: 如上圖所示,座標真實的蠟燭,透過紅色面板上的點,最終形成一個倒立的蠟燭。我們可以自己做一個圖看看,利用相似三角形的原理,即可看到成像的規律。 如上圖所示,如果投影面與小孔面距離比較近,那麼蠟燭投影之後得
opengl繪製立方體(二)
使用opengl繪製兩個顏色不一樣的立方體 #include "stdio.h" #include <GL/glew.h> #include <GLFW/glfw3.h> #include <soil/SOIL.h> #include
效能優化之關於畫素管道及優化(二)
畫素管道,這個和我們寫程式碼息息相關的東西,我估計很多人都不太清楚它是個什麼,網上也有幾篇文章關於它的內容,但是不是那麼盡如人意,那麼我就詳細說說這個東西,以及如何優化它。 關於動畫載入與人們的反應 一個流暢的動畫關乎使用者體驗(留存) 延遲 使用者反應 0 - 16 毫秒 大部智慧裝置的重
性能測試從0到1的過程(二)
sleep efi python腳本 size sel 應用服務器 完全 展示 服務 本人,從畢業開始接觸測試,但是性能方面一無所知。之前在第一份工作,測過安卓客戶端,當時寫過一個非常簡單的shell腳本,push到手機系統內,用於手機硬件信息。但是在服務端的性能方面,
從零開始Vue專案實戰(二)-搭建環境
1、下載node.js並安裝 下載地址:https://nodejs.org/en/download/。 下載.msi 格式的直接連續下一步就可以了。安裝完成後可以用 node -v 和 npm -v 檢視版本號。 2、安裝vue-cli 腳手架構建工具 在命令列中輸入npm ins
從零開始學Socket:(二)連線服務端和客戶端
先了解一下執行緒問題,在Main函式裡就是主執行緒他能處理程式碼,而new Thread就是新開一個執行緒,他和主執行緒互不干預,但是主執行緒完了,他也完了。他完了,主執行緒沒事。 在上篇部落格裡,我們已經建立好了伺服器端和客戶端,但是他們倆還沒有聯絡,要把他們聯絡起來,就
HTTP Server Mock 從手工到平臺的演變(二)
大家都知道,不管是 Web 系統、還是移動 APP,各自在與內部、外部系統之間進行資料互動時,大多數情況下都是依賴介面。在基於介面約定開發的模式下,依賴介面的產出時間如果延遲,將直接影響了整個研發除錯的效率;如果不能對介面進行及早測試,那發現問題的時間就要被推遲了。既然雙方約定了介面格式,為何不按照這個規
vue外掛開發流程詳解-從開發到釋出至npm(二)
前記:上一篇 https://www.cnblogs.com/adouwt/p/9211003.html,(這裡感謝部落格園的網友,給我點贊推薦了) 說到了一個完整的vue外掛開發、釋出的流程,總結下來就講了這麼一個事,如何注入vue, 如果封裝vue外掛,如何測試vue外掛,以及如何釋出vue外掛到np
讓我們開發一個瀏覽器(二)
讓我們開發一個瀏覽器(一)實現了在一個標籤中跳轉的建議瀏覽器,接下來新增一些功能,如位址列、多標籤、前進、後退、重新整理... 實現效果如下: 原始碼如下: from PyQt5.QtCore import QUrl, pyqtSignal, Qt from PyQt5.QtGui
從零開始學習PYTHON3講義(二)把Python當做計算器
《從零開始PYTHON3》第二講 上一講我們說過了如何啟動Python IDLE整合開發學習環境,macOS/Linux都可以在命令列執行idle3。Windows則從開始選單中去尋找IDLE程式的圖示。 上一講我們還見到了Python的兩種工作模式,互動模式和程式模式。 通常在一個大型的系統中,程
從Java視角理解系統結構(二)CPU快取
從Java視角理解系統結構連載, 關注我的微博(連結)瞭解最新動態 眾所周知, CPU是計算機的大腦, 它負責執行程式的指令; 記憶體負責存資料, 包括程式自身資料. 同樣大家都知道, 記憶體比CPU慢很多. 其實在30年前, CPU的頻率和記憶體匯流排的頻率在同一個級別, 訪問記憶體只比訪問
從零開始做SSH專案(二)
使用hibernate測試載入資料、刪除資料和修改資料等功能時,針對的是與資料庫表user對應的User。 為了簡化對其他資料表對應的實體類的持久化操作,可以在專案中建立一個BaseHibernateDAO類,將資料的載入、新增、修改、刪除等持久化方法封裝其中。 BaseHiberna
從Client應用場景介紹IdentityServer4(二)
原文: 從Client應用場景介紹IdentityServer4(二) 本節介紹Client的ClientCredentials客戶端模式,先看下畫的草圖: 一、在Server上新增動態新增Client的API 介面。 為了方便測試,在Server服務端中先新增swagger,新增流程可參考:htt
webpack:從入門到真實專案配置(二)
如何在專案中使用 webpack 專案中已經配置了很簡單的 babel 和 webpack,直接執行 npm run start 即可 這時候你會發現這個 bundle.js 居然有這麼大,這肯定是不能接受的,所以接下來章節的主要目的就是將單個檔案拆分 為多個檔案,優
【webpack系列】從零搭建 webpack4+react 腳手架(二)
html檔案如何也同步到dist目錄?bundle.js檔案修改了,萬一被瀏覽器快取了怎麼辦?如何為匯出的檔案加md5?如何把js引用自動新增到html?非業務程式碼和業務程式碼如何分開打包?如何搭建開發環境?如何實現開發環境的熱更新? 在上一節我們已經搭建了一個最基本的webpack環境,
從ECMAScript規範深度分析JavaScript(二):變數物件(下)
本文譯自Dmitry Soshnikov的《ECMA-262-3 in detail》系列教程。其中會加入一些個人見解以及配圖舉例等等,來幫助讀者更好的理解JavaScript。 宣告:本文不涉及與ES6相關的知識。 前言 在本系列教程上一篇文章《從ECMAScript規範深度分
從ECMAScript規範深度分析JavaScript(二):變數物件(上)
本文譯自Dmitry Soshnikov的《ECMA-262-3 in detail》系列教程。其中會加入一些個人見解以及配圖舉例等等,來幫助讀者更好的理解JavaScript。 宣告:本文不涉及與ES6相關的知識。 前言 在學習變數物件之前,我們要對執行期上下文有所瞭解,可以先
【Unity3D】從今天開始做UnityProgrammer!(二)簡單熟悉Unity3DEditor的介面
前言:其實前篇基本沒說啥,其實就是在吹水,這節我們要建立我們第一個專案 正文: 開啟UnityEditor建立一個專案,編輯專案名稱和專案路徑,這裡由於我打算做一個小小的動作遊戲,所以命名為ActionProject了,由於博主並不具備美工素質,所以選擇了資源比較容易找
從零開始建立uCosIII專案(二):配置uCosIII
新增BSP檔案 新建BSP.h檔案,內容如下 #ifndef _BSP_H_ #define _BSP_H_ #include <stdarg.h> #include <stdio.h> #include <cpu.h> #i