OpenGL學習(四)給圖案添加紋理

參考官方文檔https://learnopengl-cn.github.io/


紋理是一個2D圖片,它可以用來添加物體的細節。

爲了能把紋理映射到三角形上,我們要指定三角形的每個頂點各自對應紋理的哪個部分。這樣每個頂i點就會關聯一個紋理座標,用來標明該從紋理圖像的哪個部分採樣,然後在圖形的其他片段上進行插值。用紋理座標獲取紋理顏色叫做採樣。紋理座標如下:

float texCoords[]={
    0.0f,0.0f,
    1.0f,0.0f,
    0.5f,1.0f
}

紋理環繞方式

紋理座標範圍一般是從(0,0)到(1,1),如果把紋理座標設置在範圍之外,OpenGL默認重複這個紋理。下面是一些可選的做法:

  • GL_REPEAT:默認行爲,重複紋理圖像
  • GL_MIRRORED_REPEAT:重複的圖片是鏡像放置的
  • GL_CLAMP_TO_EDGE:超出部分會重複紋理座標的邊緣
  • GL_CLAMP_TO_BORDER:超出的座標爲用戶指定的邊緣顏色

我們可以用glTexParameteri函數對單獨的一個座標軸設置。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

如果選擇GL_CLAMP_TO_BORDER,我們需要指定一個邊緣的顏色:

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

紋理過濾

我們這裏討論紋理過濾的兩種選項:GL_NEAREST和GL_LINEAR。

  • GL_NEAREST:鄰近過濾,默認方式。選擇中心點最接近紋理座標的那個像素
  • GL_LINEAR:線性過濾,基於紋理座標附近的紋理像素,計算出一個插值,近似出這些像素之間的顏色。

如果在一個很大的物體上應用一張低分辨率的紋理時,GL_NEAREST會產設顆粒狀的圖案,我們可以清晰看到組成紋理的像素,而GL_LINEAR能夠產生更平滑的圖案,很難看出單個的紋理像素。當進行放大和縮小操作時可以設置紋理過濾的選項。如縮小時使用鄰近過濾,放大時使用線性過濾。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多級漸遠紋理

對於遠處的物體,如果它用和近處物體相同的分辨率的紋理的話就會顯得不真實。這時可以使用多級漸遠紋理來解決問題。

可以有四個選項來指定不同多級漸遠紋理級別之間的過濾方式:

  • GL_NEAREST_MIPMAP_NEAREST
  • GL_LINEAR_MIOMAO_NEAREST
  • GL_NEAREST_MIPMAP_LINEAR
  • GL_LINEAR_MIPMAP_LINEAR

用glTexParameteri設置:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

加載與創建紋理

這裏需要下載一個頭文件圖像加載庫stb_image.h,它能夠加載大部分流行的文件格式,且能夠簡單的整合到你的工程中。網址:

https://github.com/nothings/stb/blob/master/stb_image.h。下載完成後添加到項目中,記得把相關的目錄添加到項目屬性的包含目錄。看起來簡單的操作都是容易出錯的。這時還有一個必要的操作是在項目中添加一個新建C++文件,命名無所謂,在其中輸入下面的代碼:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

這一步是非常必要的。通過定義STB_IMAGE_IMPLEMENTATION,預處理器會修改頭文件,讓其只包含相關的函數定義源碼,等於是將這個頭文件變爲一個.cpp文件。

加載圖片的代碼如下:

int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);

這段代碼是放到主程序中的一個適當的位置的。

生成紋理

這個過程如下:

//創建
unsigned int texture;
glGenTextures(1, &texture);
//綁定
glBindTexture(GL_TEXTURE_2D, texture);
// 爲當前綁定的紋理對象設置環繞、過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加載並生成紋理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
//其中的參數依次代表:紋理目標、多級漸遠紋理的級別、希望把紋理儲存爲何種方式、紋理的寬和高、設置爲0、源圖的格式和數據類型、真正的圖像數據。
    //生成
    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;
}
//釋放圖像的內存
stbi_image_free(data);

應用紋理

在前面的着色器類的基礎上。

shader.vs頂點着色器

#version 330 core
layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aColor;
layout(location=2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main(){
	gl_Position=vec4(aPos.x,aPos.y,aPos.z,1.0);
	ourColor=aColor;
	TexCoord=aTexCoord;
}

shader.fs片段着色器

#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;

void main(){
    FragColor = texture(ourTexture, TexCoord);
}

然後主程序main.cpp

#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>
#include"shader.h"
#include"stb_image.h"

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}
//用着色器語言GLSL編寫頂點着色器,然後編譯它,下面是一個非常基礎的GLSL頂點着色器的源代碼
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout(location=1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"	ourColor=aColor;\n"
"}\0";
//片段着色器,計算像素最後的顏色輸出
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(ourColor,1.0);\n"
"}\n\0";

int main() {
	//initialize
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);

	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GlAD" << std::endl;
		return -1;
	}
	//Shader類
	Shader ourShader("shader.vs", "shader.fs");
	
	float vertices[] = {
		//     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
			 0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
			 0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
			-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
			-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
	};
	unsigned int indices[] = {
		0,1,3,
		1,2,3
	};
	//頂點數組對象VAO,創建
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	//索引緩衝對象
	unsigned int EBO;
	glGenBuffers(1, &EBO);
	//
	glBindVertexArray(VAO);
	//把頂點數組複製到緩衝中供OpenGL使用
	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, 8 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	//顏色屬性
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);
	//紋理座標
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2);
	//加載並創建紋理
	unsigned int texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	int width, height, nrChannels;
	//下面函數的第一個參數輸入本地的圖片文件的路徑就行了
	unsigned char* data = stbi_load("C:\\Users\\xhh\\Downloads\\article_phy\\container.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;
	}
	stbi_image_free(data);


	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);


	glViewport(0, 0, 800, 600);
	void framebuffer_size_callback(GLFWwindow * window, int width, int height);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	while (!glfwWindowShouldClose(window))
	{
		processInput(window);

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		//綁定紋理
		glBindTexture(GL_TEXTURE_2D, texture);
		//激活着色器程序對象
		ourShader.use();
		
		//ourShader.setFloat("delta", 0.5f);

		//glDrawArrays(GL_TRIANGLES, 0, 3);
		

		glBindVertexArray(VAO);
		//使用當前綁定的索引緩衝對象中的索引進行繪製
		//其中的參數分別是:繪製的模式、繪製頂點的個數、索引的類型和指定EBO中的偏移量,
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		//glDrawArrays(GL_TRIANGLES, 0, 3);
		glfwSwapBuffers(window);//交換顏色緩衝,它在每一次迭代中被用來繪製,並作爲輸出顯示在屏幕上
		glfwPollEvents();//檢測有沒有觸發什麼事件、更新窗口狀態,並調用對應的回調函數
	}
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glDeleteBuffers(1, &EBO);

	ourShader.del();
	glfwTerminate();
	return 0;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

沒問題的話運行結果如下:
在這裏插入圖片描述

如果要將紋理顏色和頂點顏色混合,只需要在片段着色器中將紋理顏色和頂點顏色相乘就行了:

FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);

結果變爲:
在這裏插入圖片描述

紋理單元

紋理單元可以讓我們在着色器中使用多個紋理。通過把紋理單元賦值爲採樣器,我們可以i一次綁定多個紋理,但我們需要首先激活對應的紋理:

glActiveTexture(GL_TEXTURE0); // 在綁定紋理之前先激活紋理單元
glBindTexture(GL_TEXTURE_2D, texture);

這時我們通過片段着色器來接受另一個採樣器:

#version 330 core
...

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

這樣最終輸出的顏色是兩個紋理的結合。mix函數接受兩個值作爲參數,第三個參數是二者的顏色比例。

主程序main.cpp的代碼爲:

#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>
#include"shader.h"
#include"stb_image.h"

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}
//用着色器語言GLSL編寫頂點着色器,然後編譯它,下面是一個非常基礎的GLSL頂點着色器的源代碼
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout(location=1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"	ourColor=aColor;\n"
"}\0";
//片段着色器,計算像素最後的顏色輸出
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(ourColor,1.0);\n"
"}\n\0";

int main() {
	//initialize
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);

	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GlAD" << std::endl;
		return -1;
	}
	//Shader類
	Shader ourShader("shader.vs", "shader.fs");
	
	float vertices[] = {
		//     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
			 0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
			 0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
			-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
			-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
	};
	unsigned int indices[] = {
		0,1,3,
		1,2,3
	};
	//頂點數組對象VAO,創建
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	//索引緩衝對象
	unsigned int EBO;
	glGenBuffers(1, &EBO);
	//
	glBindVertexArray(VAO);
	//把頂點數組複製到緩衝中供OpenGL使用
	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, 8 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	//顏色屬性
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);
	//紋理座標
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2);
	//加載並創建紋理
	//第一個
	unsigned int texture1;
	glGenTextures(1, &texture1);
	glBindTexture(GL_TEXTURE_2D, texture1);
	

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	int width, height, nrChannels;
	//下面函數的第一個參數輸入本地的圖片文件的路徑就行了
	unsigned char* data = stbi_load("C:\\Users\\xhh\\Downloads\\article_phy\\container.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 texture1" << std::endl;
	}
	stbi_image_free(data);

	//第二個
	unsigned int texture2;
	glGenTextures(1, &texture2);
	glBindTexture(GL_TEXTURE_2D, texture2);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	//下面函數的第一個參數輸入本地的圖片文件的路徑就行了
	data = stbi_load("C:\\Users\\xhh\\Downloads\\article_phy\\awesomeface.png", &width, &height, &nrChannels, 0);
	if (data) {
		//注意這個png文件具有透明度屬性,因此有一個alpha信道,要告訴OpenGL數據類型爲GL_RGBA
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else {
		std::cout << "Failed to load texture2" << std::endl;
	}
	stbi_image_free(data);

	ourShader.use(); // don't forget to activate/use the shader before setting uniforms!
	// either set it manually like so:
	glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
	// or set it via the texture class
	ourShader.setInt("texture2", 1);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	

	glViewport(0, 0, 800, 600);
	void framebuffer_size_callback(GLFWwindow * window, int width, int height);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	while (!glfwWindowShouldClose(window))
	{
		processInput(window);

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		//綁定紋理
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture1);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture2);
		//激活着色器程序對象
		ourShader.use();
		//ourShader.setFloat("delta", 0.5f);

		//glDrawArrays(GL_TRIANGLES, 0, 3);
		

		glBindVertexArray(VAO);
		//使用當前綁定的索引緩衝對象中的索引進行繪製
		//其中的參數分別是:繪製的模式、繪製頂點的個數、索引的類型和指定EBO中的偏移量,
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		//glDrawArrays(GL_TRIANGLES, 0, 3);
		glfwSwapBuffers(window);//交換顏色緩衝,它在每一次迭代中被用來繪製,並作爲輸出顯示在屏幕上
		glfwPollEvents();//檢測有沒有觸發什麼事件、更新窗口狀態,並調用對應的回調函數
	}
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glDeleteBuffers(1, &EBO);

	ourShader.del();
	glfwTerminate();
	return 0;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

在這裏插入圖片描述

通過在加載圖片前加入語句stbi_set_flip_vertically_on_load(true);可以翻轉y軸:
在這裏插入圖片描述

練習:

  1. 修改片段着色器,讓笑臉圖案朝另一個方向看
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(1.0f-TexCoord.x,1.0f-TexCoord.y)),0.2);
}
  1. 嘗試用不同的紋理環繞方式,設定一個從0.0f2.0f範圍內的(而不是原來的0.0f1.0f)紋理座標。試試看能不能在箱子的角落放置4個笑臉.

紋理座標改爲:

float vertices[] = {
		//     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
			 0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   2.0f, 2.0f,   // 右上
			 0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   2.0f, 0.0f,   // 右下
			-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
			-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 2.0f    // 左上
	};

下面分別爲4種不同的環繞方式:

  • GL_REPEAT在這裏插入圖片描述

  • GL_MIRRORED_REPEAT在這裏插入圖片描述

  • GL_CLAMP_TO_EDGE在這裏插入圖片描述

  • GL_CLAMP_TO_BORDER在這裏插入圖片描述

  1. 嘗試在矩形上只顯示紋理圖像的中間一部分,修改紋理座標,達到能看見單個的像素的效果。嘗試使用GL_NEAREST的紋理過濾方式讓像素顯示得更清晰

可以將紋理座標改爲

float vertices[] = {
		//     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
			 0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   0.7f, 0.7f,   // 右上
			 0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   0.7f, 0.3f,   // 右下
			-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.3f, 0.3f,   // 左下
			-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.3f, 0.7f    // 左上
	};

然後紋理的環繞方式和過濾方式爲

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

效果如圖在這裏插入圖片描述

  1. 使用一個uniform變量作爲mix函數的第三個參數來改變兩個紋理可見度,使用上和下鍵來改變箱子或笑臉的可見度。

這個有點意思。但其實並不難。所以根絕OpenGL學起來很難學,但慢慢的學習這個東西后發現它是真的能做到別的工具做不到的東西,而且一步一步的學習,並沒有想象的那麼難。

片段着色器shader.fs

#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float transparency;
void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x,TexCoord.y)),transparency);
}

main.cpp按鍵監聽響應以及參數設置:

float transparency = 0.0f;
void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
	else if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
		if (transparency < 1.0f)
			transparency += 0.001f;
		//std::cout << transparency << std::endl;
	}
	else if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
		if (transparency > 0.0f)
			transparency -= 0.001f;
		//std::cout << transparency << std::endl;
	}
}
...;
		ourShader.use();
		ourShader.setFloat("transparency", transparency);
		glBindVertexArray(VAO);
...;

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章