1. 程式人生 > >用glew,glfw,FreeImage實現opengl學習筆記6座標變換

用glew,glfw,FreeImage實現opengl學習筆記6座標變換

這個程式碼參考教程點選開啟連結  這個教程很詳細,程式碼是實現座標的轉換,教程帶原始碼,我仿照原始碼在自己本地的實現,這裡給大家參考使用載入圖片是用的FreeImage是一個開源的載入圖片的,網上的資料也挺多的。這個程式的原始碼在這點選開啟連結  點選裡面的shader3資料夾裡面有兩個著色器程式和一個read檔案,使用shader.h和transfromation.cpp

程式執行後是個旋轉的效果,這裡是截圖


下邊是程式碼shader.h 

#ifndef SHADER_H
#define SHADER_H
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<GL/glew.h>
using namespace std;
class Shader
{
public:
	//程式的ID
	GLuint Program;
	//讀取渲染程式並建立Shader
	Shader(const GLchar * vertexSourcePath,const GLchar *fragmentSource);
	{
		//1.從檔案路徑獲得vertex/fragment原始碼
		string vertexCode;
		string fragmentCode;
		try{
			//開啟檔案Open files
			ifstream vShaderFile(vertexPath);
			ifstream fShaderFile(fragmentPath);

			stringstream vShaderStream,fShaderStream;
			//讀取檔案緩衝到流、
			vShaderStream<<vShaderFile.rdbuf();
			fShaderStream<<fShaderFile.rdbuf();

			//關閉檔案控制代碼
			vShaderFile.close();
			fShaderFile.close();
			//將流轉為GLchar陣列
			 vertexCode = vShaderStream.str(); 
        fragmentCode = fShaderStream.str(); 
		}
		catch(std::exception e) 
    { 
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;  
    }
		const GLchar* vShaderCode = vertexCode.c_str();
        const GLchar * fShaderCode = fragmentCode.c_str();
        // 2.編譯著色器
        GLuint vertex, fragment;
        GLint success;
        GLchar infoLog[512];
        // 頂點著色器
        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vShaderCode, NULL);
        glCompileShader(vertex);
        // 列印著色器是否錯誤
        glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(vertex, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
        }
        // 片段著色器
        fragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment, 1, &fShaderCode, NULL);
        glCompileShader(fragment);
        // 列印是否有任何錯誤
        glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragment, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
        }
        // 著色器程式
        this->Program = glCreateProgram();
        glAttachShader(this->Program, vertex);
        glAttachShader(this->Program, fragment);
        glLinkProgram(this->Program);
        // 列印是否有錯誤
        glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
        if (!success)
        {
            glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
        }
        // 刪除著色器程式
        glDeleteShader(vertex);
        glDeleteShader(fragment);
	}
	//使用Program
	void Use();
	{
glUseProgram(this->Program);
	}
}
#endif
transformation.cpp
//紋理單元的使用
#include<iostream>
//GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>
#pragma comment(lib,"FreeImage.lib")
// Other Libs
#include<FreeImage.h>
#include<Shader.h>// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
using namespace std;
// 函式原型
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

// Window 尺寸
const GLuint WIDTH = 800, HEIGHT = 600;

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW 初始化好GLFW
    glfwInit();
    // Set all the required options for GLFW 設定GLFW要求設定的選項
    glfwWindowHint(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 functions 創造一個GLFWwindow object that can use for GLFW's
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions 設定回撥函式
  glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a 
	//modern approach to retrieving function pointers and extensions 設定為true 用現在的方法恢復函式的指標和擴充套件
    glewExperimental = GL_TRUE; 
    // Initialize GLEW to setup the OpenGL Function pointers  初始化GLEW 去設定opengl的函式指標
    glewInit();

    // Define the viewport dimensions 定義檢視尺寸
    glViewport(0, 0, WIDTH, HEIGHT);


    // Build and compile our shader program    構建和編譯我們的渲染程式
    Shader ourShader("D:/C語言/openglflew/shader3/shader.vs", "D:/C語言/openglflew/shader3/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers  設定頂點資料和指標屬性
    GLfloat vertices[] = {
        // Positions   位置       // Colors  顏色         // Texture Coords  紋理座標
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // Top Right
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // Bottom Right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // Bottom Left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // Top Left 
    };
    GLuint indices[] = {  // Note that we start from 0! 備註我們開始從0
        0, 1, 3, // First Triangle 第一個三角形
        1, 2, 3  // Second Triangle第二個三角形
    };
    GLuint 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);

    // Position attribute  位置屬性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute  顏色屬性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    // TexCoord attribute   紋理座標屬性
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO 解綁VAO


    // Load and create a texture   載入和建立紋理
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1  紋理1
    // ====================
    glGenTextures(1, &texture1);
	// All upcoming GL_TEXTURE_2D operations now have effect on our texture object 全部即將更改的GL_TEXTURE_2D 操作將會改變紋理物件
    glBindTexture(GL_TEXTURE_2D, texture1); 
    // Set our texture parameters   設定我們的紋理引數
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT  設定紋理包裝給GL_REPEAT 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering  設定紋理過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps 
  
	// Load, create texture and generate mipmaps 載入,建立紋理並且形成譯碼


   //image format
    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	FREE_IMAGE_FORMAT fifmt;
	int width, height;
	FIBITMAP *dib(0);
	
	//1 獲取圖片格式
 fifmt = FreeImage_GetFileType("D:/C語言/openglflew/wall.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt))
   dib = FreeImage_Load(fifmt, "D:/C語言/openglflew/wall.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib));//大小
    //if the image failed to load, return failure 如果載入圖片返回失敗
    if(!dib)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib = FreeImage_ConvertTo24Bits(dib);

//4 獲取資料指標
BYTE *pixels = (BYTE*)FreeImage_GetBits(dib);
 

 width = FreeImage_GetWidth(dib);
 height = FreeImage_GetHeight(dib);
 cout<<"width:"<<width<<endl;
  cout<<"height:"<<height<<endl;
  //----------------------------------第二個紋理的圖片
  FREE_IMAGE_FORMAT fif1 = FIF_UNKNOWN;
	FREE_IMAGE_FORMAT fifmt1;
	int width1, height1;
	FIBITMAP *dib1(0);
   
	//1 獲取圖片格式
 fifmt1 = FreeImage_GetFileType("D:/C語言/openglflew/face.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt1))
   dib1 = FreeImage_Load(fifmt1, "D:/C語言/openglflew/face.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib1));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib1));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib1));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib1));//大小
    //if the image failed to load, return failure  如果圖片載入失敗 
    if(!dib1)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib1= FreeImage_ConvertTo24Bits(dib1);

//4 獲取資料指標
BYTE *pixels1 = (BYTE*)FreeImage_GetBits(dib1);
 

 width1 = FreeImage_GetWidth(dib1);
 height1 = FreeImage_GetHeight(dib1);
 cout<<"width1:"<<width1<<endl;
  cout<<"height1:"<<height1<<endl;

	
  

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glGenerateMipmap(GL_TEXTURE_2D);
  
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2 紋理2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters 設定紋理引數
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //經第七個引數由GL_RGB改為GL_BGR_EXT
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels1);
    glGenerateMipmap(GL_TEXTURE_2D);
   
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
		//如果有事件(鍵盤和滑鼠等等)呼叫相應的響應函式
        glfwPollEvents();

        // Render 渲染
        // Clear the colorbuffer  清楚顏色快取
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);


        // Bind Textures using texture units  繫結紋理 使用紋理單元
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader 啟用渲染
        ourShader.Use();       

        // Create transformations 建立轉變
        glm::mat4 transform; //建立一個單位矩陣
        transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));//移動
        transform = glm::rotate(transform, (GLfloat)glfwGetTime() * 50.0f, glm::vec3(0.0f, 0.0f, 1.0f));//旋轉

        // Get matrix's uniform location and set matrix  得到矩陣uniform 位置並且設定矩陣,將我們設定的資料傳給著色器
		//首先請求uniform變數的地址,然後用glUniform函式把矩陣資料傳送給著色器,這個函式有一個Matrix4fv的字尾
		//第一個引數就比較熟悉了,它是uniform的地址,第二個引數告訴Opengl多少個矩陣被髮送過去,目前是1
		//第三個引數詢問我們我們是否希望對矩陣進行置換,這樣會把矩陣的行轉換為列。penGL使用的內部的矩陣佈局叫做以列為主順序(column-major ordering)佈局。
		//GLM已經是用以列為主順序定義了它的矩陣,所以並不需要置換矩陣,我們可以填為GL_FALSE、最後一個引數是實際的矩陣資料,但是GLM並不是把它們的矩陣儲存為OpenGL所希望的那種,
		//因此我們要先用GLM的內建函式value_ptr來變換這些資料。
        GLint transformLoc = glGetUniformLocation(ourShader.Program, "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
        
        // Draw container  畫集合
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // Swap the screen buffers 交換螢幕的快取
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose  適當的的釋放資源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.終止GLFW,清除所有的通過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);
}
畫素著色器(片段著色器)shader.frag
#version 330 core
in vec3 ourColor;
in vec2 TexCoord;

out vec4 color;

// Texture samplers
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;

void main()
{
	// Linearly interpolate between both textures (second texture is only slightly combined)
	color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}

頂點著色器shader,vs
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;

out vec3 ourColor;
out vec2 TexCoord;
  
uniform mat4 transform;

void main()
{
    gl_Position = transform * vec4(position, 1.0f);
    ourColor = color;
    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
} 


下面是上文教程的第二個實現,實現的是一個立方體的旋轉,也用到了載入紋理,程式時動態的這裡只是截圖。在上文給的原始碼中找到shader4.裡面含有著色器程式


標頭檔案和上文中的一樣

實現實現程式碼transfromation1.cpp

//繪製立方體
#include<iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include<FreeImage.h>
#include<Shader.h>

using namespace std;


// Function prototypes 函式的原型
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

// Window dimensions window的尺寸
const GLuint WIDTH = 800, HEIGHT = 600;

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW 初始化GLFW 
    glfwInit();
    // Set all the required options for GLFW 設定GLFW要求設定的選項
    glfwWindowHint(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 functions 建立GLFW視窗物件,這樣我們能使用GLFW的功能
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions  設定回撥函式
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers   初始化GLEW去設定opengl的函式指標
    glewInit();

    // Define the viewport dimensions 定義視窗的尺寸
    glViewport(0, 0, WIDTH, HEIGHT);

    // Setup OpenGL options  設定opengl選項
	//啟動深度測試後opengl自動識別深度然後把遮掩的不顯示
    glEnable(GL_DEPTH_TEST); ///啟動深度測試


    // Build and compile our shader program  構建和編譯我們的著色器程式
    Shader ourShader("D:/C語言/openglflew/shader4/shader.vs", "D:/C語言/openglflew/shader4/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers 建立頂點資料和頂點屬性
    GLfloat 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
    };
	//建立VBO,VAO
    GLuint 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);

    // Position attribute   位置屬性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute 紋理屬性
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO  解綁VAO


	// Load image, create texture and generate mipmaps 載入影象並且建立紋理和產生編碼
   //image format
    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	
	int width, height;
	FIBITMAP *dib(0);
	
   // unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
	//1 獲取圖片格式
FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType("D:/C語言/openglflew/wall.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt))
   dib = FreeImage_Load(fifmt, "D:/C語言/openglflew/wall.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib));//大小
    //if the image failed to load, return failure
    if(!dib)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib = FreeImage_ConvertTo24Bits(dib);

//4 獲取資料指標
BYTE *pixels = (BYTE*)FreeImage_GetBits(dib);
 

 width = FreeImage_GetWidth(dib);
 height = FreeImage_GetHeight(dib);
 cout<<"width:"<<width<<endl;
  cout<<"height:"<<height<<endl;
  
  ///----------------------------------------載入第二個紋理圖片----
	FREE_IMAGE_FORMAT fif1 = FIF_UNKNOWN;
	FREE_IMAGE_FORMAT fifmt1;
	int width1, height1;
	FIBITMAP *dib1(0);
   
	//1 獲取圖片格式
 fifmt1 = FreeImage_GetFileType("D:/C語言/openglflew/face.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt1))
   dib1 = FreeImage_Load(fifmt1, "D:/C語言/openglflew/face.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib1));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib1));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib1));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib1));//大小
    //if the image failed to load, return failure
    if(!dib1)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib1= FreeImage_ConvertTo24Bits(dib1);

//4 獲取資料指標
BYTE *pixels1 = (BYTE*)FreeImage_GetBits(dib1);
 

 width1 = FreeImage_GetWidth(dib1);
 height1 = FreeImage_GetHeight(dib1);
 cout<<"width1:"<<width1<<endl;
  cout<<"height1:"<<height1<<endl;

	

    // Load and create a texture   載入和建立紋理
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1 紋理1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters 設定我們的紋理的引數
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering 設定紋理的過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   
 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glGenerateMipmap(GL_TEXTURE_2D);
 
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2紋理2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters 設定我們的紋理引數
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering  設定紋理過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width1, height1, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels1);
    glGenerateMipmap(GL_TEXTURE_2D);
 
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop 迴圈
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer 清理顏色快取和深度快取
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units  繫結紋理和紋理單元
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Create transformations  建立轉換
        glm::mat4 model;//建立模型矩陣
        glm::mat4 view;//建立檢視矩陣
        glm::mat4 projection;//建立個投影矩陣
        model = glm::rotate(model, (GLfloat)glfwGetTime() * 50.0f, glm::vec3(0.5f, 1.0f, 0.0f));
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
        // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get their uniform location  得到uniform的位置 
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass them to the shaders  把資料傳給著色器
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw container 畫
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
//清除
	FreeImage_Unload(dib);
 FreeImage_DeInitialise();
	FreeImage_Unload(dib1);
  FreeImage_DeInitialise();
    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);
}
畫素著色器程式碼shader.frag
#version 330 core
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;

void main()
{
    color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}
頂點著色器shader.vs程式碼
<pre name="code" class="cpp">#version 330 core
layout (location = 0) in vec3 position;
layout (location = 2) in vec2 texCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{     //位置=投影矩陣*檢視矩陣*模型矩陣*頂點資料
    gl_Position = projection * view * model * vec4(position, 1.0f);
    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}


這個程式時教程中第三個例項,繪製多個立方體,程式執行效果如下,著色器程式在原始碼檔案中的shader5

標頭檔案shader.h還是之前xingtong

實現程式碼 transfromation2.cpp

//多個立方體旋轉
#include <iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <FreeImage.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include <Shader.h>

// Function prototypes  函式的原型
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

// Window dimensions windows的尺寸
const GLuint WIDTH = 800, HEIGHT = 600;

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(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 functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Setup OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("D:/C語言/openglflew/shader5/shader.vs", "D:/C語言/openglflew/shader5/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers 建立頂點陣列和指標屬性
    GLfloat 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
    };
    // World space positions of our cubes  世界空間這箱子的位置(世界空間的解釋看教程中)
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint 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);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


	// Load image, create texture and generate mipmaps 載入影象並且建立紋理和產生編碼
   //image format
    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	
	int width, height;
	FIBITMAP *dib(0);
	
   // unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
	//1 獲取圖片格式
FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType("D:/C語言/openglflew/wall.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt))
   dib = FreeImage_Load(fifmt, "D:/C語言/openglflew/wall.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib));//大小
    //if the image failed to load, return failure
    if(!dib)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib = FreeImage_ConvertTo24Bits(dib);

//4 獲取資料指標
BYTE *pixels = (BYTE*)FreeImage_GetBits(dib);
 

 width = FreeImage_GetWidth(dib);
 height = FreeImage_GetHeight(dib);
 cout<<"width:"<<width<<endl;
  cout<<"height:"<<height<<endl;
  
  ///----------------------------------------載入第二個紋理圖片----
	FREE_IMAGE_FORMAT fif1 = FIF_UNKNOWN;
	FREE_IMAGE_FORMAT fifmt1;
	int width1, height1;
	FIBITMAP *dib1(0);
   
	//1 獲取圖片格式
 fifmt1 = FreeImage_GetFileType("D:/C語言/openglflew/face.png", 0);

//2 載入圖片

 if(FreeImage_FIFSupportsReading(fifmt1))
   dib1 = FreeImage_Load(fifmt1, "D:/C語言/openglflew/face.png",0);
  printf("bit: %d\n", FreeImage_GetBPP(dib1));//灰度
  printf("type: %d\n",FreeImage_GetImageType(dib1));//返回型別
  printf("bit: %d\n",FreeImage_GetColorsUsed(dib1));//調色盤的大小
  printf("bit: %d\n",FreeImage_GetDIBSize(dib1));//大小
    //if the image failed to load, return failure
    if(!dib1)
        cout<<55<<endl;
//3 轉化為rgb 24色
dib1= FreeImage_ConvertTo24Bits(dib1);

//4 獲取資料指標
BYTE *pixels1 = (BYTE*)FreeImage_GetBits(dib1);
 

 width1 = FreeImage_GetWidth(dib1);
 height1 = FreeImage_GetHeight(dib1);
 cout<<"width1:"<<width1<<endl;
  cout<<"height1:"<<height1<<endl;

	

    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1  紋理1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,  GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glGenerateMipmap(GL_TEXTURE_2D);
   
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width1, height1, 0,  GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels1);
    glGenerateMipmap(GL_TEXTURE_2D);
   
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer 清楚顏色快取和深度快取
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units  建立紋理使用紋理單元
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Create transformations   建立轉換
        glm::mat4 view; //建立檢視矩陣
        glm::mat4 projection;//建立投影矩陣
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));//移動
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get their uniform location 得到uniform位置
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shades 把矩陣傳給著色器
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO); //繫結VAO
		//現在在遊戲迴圈中我們打算呼叫glDrawArrays函式10次,每次迴圈給頂點著色器傳送的模型矩陣都不相同。
		//我們會在遊戲迴圈內建立一個小迴圈來把物體渲染10次,
		//每次的模型矩陣都不同。要注意的是,我們對箱子也施加了一個旋轉。
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing 計算每一個模型矩陣物件並且在繪製前把資料傳給著色器
            glm::mat4 model;
			//model = glm::rotate(model, (GLfloat)glfwGetTime() * 50.0f, glm::vec3(1.0f, 0.0f, 0.0f));     //旋轉
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(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);
}

畫素著色器
#version 330 core
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;

void main()
{
    color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}
頂點著色器
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 2) in vec2 texCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}