OpenGL學習筆記(3) 紋理

關於紋理

通常遊戲裏的物體不必定都是純色的物體,物體上面會有一些圖片貼在上面,好比牆壁,箱子,地板,能夠看到磚頭、木板和大理石組成的圖片,要把圖片貼到計算機裏的幾何圖形的話,就要把圖片的顏色採樣貼到幾何圖形上,採樣是計算機常常乾的工做,計算機要處理天然中的數據就須要對數據進行採樣,好比說對聲音採樣就是採集聲音的頻率和頻幅,分別表明聲音的音色和聲量,固然,採集到的是一個模擬量,然而計算機沒法處理模擬量,因此須要將模擬量量化數字量,也就是二進制數進行存儲,同理,顏色也是同樣,現代計算機中顏色通常以RGB的形式,分別表明Red,Green,Blud三種顏色在這個顏色中的佔比,通常使用3個字節存儲,也就是說顏色的數量能夠達到2^24種,已經足夠用了,想要把採集到的顏色貼到物體上的話,咱們須要指定頂點的紋理座標,告訴着色器要從紋理的哪一個點開始採樣, 紋理座標的範圍是0到1
項目的代碼
learnOpenGL裏的圖解
在這裏插入圖片描述git

紋理環繞

紋理座標的範圍是0到1,假如超出這個範圍的話,在OpenGL裏會有幾種方式來貼圖,這些方式叫作紋理環繞方式github

  • GL_REPEAT 對紋理的默認行爲。重複紋理圖像。
  • GL_MIRRORED_REPEAT 和GL_REPEAT同樣,但每次重複圖片是鏡像放置的。
  • GL_CLAMP_TO_EDGE 紋理座標會被約束在0到1之間,超出的部分會重複紋理座標的邊緣,產生一種邊緣被拉伸的效果。
  • GL_CLAMP_TO_BORDER 超出的座標爲用戶指定的邊緣顏色。

在個人工程裏有用來測試的函數SurroundTest,把環繞方式和過濾模式封裝成枚舉方便調試數組

//環繞模式
enum class SurroundMode
{
	Repeat = GL_REPEAT,						//重複紋理圖像
	MirroredRepeat = GL_MIRRORED_REPEAT,	//鏡像重複紋理圖像
	ClampToEdge = GL_CLAMP_TO_EDGE,			//將邊緣拉伸
	ClampToBoreder = GL_CLAMP_TO_BORDER		//超出的部分爲指定邊緣顏色
};
//過濾方式
enum class FilteringMode
{
	Nearest = GL_NEAREST,
	Linear = GL_LINEAR,
};

將紋理座標的份量值設置到[0,1]以外,進行測試緩存

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

測試結果
在這裏插入圖片描述函數

過濾模式

能夠看到環繞方式測試的圖片是有一丟丟模糊的(實際上是用錯過濾模式了),這是由於用了線性過濾模式(Linear),使用線性過濾模式採樣的時候,每一個像素的顏色會和周圍的顏色進行混合算出一個插值,這個插值近似於這些像素
而採用 鄰近過濾(Nearest) 的話,那麼像素的顏色就是採樣器採到的顏色,若是分辨率小的圖片貼到大的物體上的話,就會出現顆粒狀的圖案,但使用線性過濾模式的話這些顆粒就會變得比較平滑
測試一下
在這裏插入圖片描述
是否是能夠很明顯地看出Linear方式採樣的珂朵莉的眼神比較溫柔啊,這就是通過平滑處理過的圖片。在這裏順便說下unity的三種過濾模式測試

  • Point 點像素過濾,紋理像素會在附近變爲塊狀。
  • Bilinear 雙線性過濾,平均紋理樣本
  • Trilinear 三線性過濾,平均紋理樣本,同時也在多級漸遠紋理級別之間混合

測試代碼

首先是讀取圖片用的代碼,對 learnOpenGL 裏的代碼進行了一次封裝,針對不一樣位深度的圖片進行處理,位深度就是圖片記錄每一個像素點的位數,24位表示3個float,也就是RGB值,32位則是4個float,即RGBA,讀取圖片用了官方介紹的stb_imagespa

//加載紋理
template <typename S1 = std::string>
void LoadTexture(unsigned int &texture, S1&& pic)
{
	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;
	stbi_set_flip_vertically_on_load(true);
	unsigned char *data = stbi_load((TEXTURE_PATH + std::forward<std::string>(pic)).c_str(), &width, &height, &nrChannels, 0);
	std::cout << "nrChannels = " << nrChannels << endl;
	if (data)
	{
		//位深度爲24,3個通道(jpg
		if (nrChannels == 3)
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		//位深度爲32,4個通道(png
		else if (nrChannels == 4)
			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 texture" << std::endl;
	}
	stbi_image_free(data);
	data = nullptr;
}

着色器代碼
頂點着色器
vertex_3.vs3d

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

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

位置0是頂點的座標,位置1是輸入的紋理座標,要經過頂點着色器將紋理座標輸出到片斷着色器
片斷着色器
fragment_3.fs調試

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D ourTexture;

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

TexCoord是從頂點着色器接收的紋理座標,使用texture函數來採樣,第一個參數是採樣器(Sampler),第二個參數是紋理座標,着色器輸出過濾後的顏色值FragColor
接下來是就是正常的測試代碼了rest

void NormalTest()
{
	float vertices[] = {
		//     ---- 位置 ----     - 紋理座標 -
			 0.9f,  0.9f, 0.0f,  1.0f, 1.0f,   // 右上
			 0.9f, -0.9f, 0.0f,  1.0f, 0.0f,   // 右下
			-0.9f, -0.9f, 0.0f,  0.0f, 0.0f,   // 左下
			-0.9f,  0.9f, 0.0f,  0.0f, 1.0f    // 左上
	};
	//索引
	unsigned int indices[] = {
		0,1,3,
		1,2,3
	};
	//編譯着色器
	Shader ourShader("vertex_3.vs", "fragment_3.fs");
	ourShader.use();//glUseProgram(shaderProgram);
	unsigned int VAO, VBO, EBO;

	//頂點數組
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	//綁定頂點數組緩存
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//索引緩存
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	//生成紋理
	unsigned int texture1;
	LoadTexture(texture1, std::move("picture4.png"));	//加載紋理
	glBindTexture(GL_TEXTURE_2D, texture1);//綁定紋理

	// 位置屬性
	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);


	while (!glfwWindowShouldClose(glWindow))
	{
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		//draw
		glBindVertexArray(VAO);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		glBindVertexArray(0);
		glfwPollEvents();
		glfwSwapBuffers(glWindow);
	}
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);
	glfwTerminate();
}

測試結果

在這裏插入圖片描述 珂朵莉好可愛~

相關文章
相關標籤/搜索