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
#include
#include
#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);
}

没问题的话运行结果如下:
OpenGL学习(四)给图案添加纹理_第1张图片

如果要将纹理颜色和顶点颜色混合,只需要在片段着色器中将纹理颜色和顶点颜色相乘就行了:

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

结果变为:
OpenGL学习(四)给图案添加纹理_第2张图片

纹理单元

纹理单元可以让我们在着色器中使用多个纹理。通过把纹理单元赋值为采样器,我们可以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
#include
#include
#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);
}

OpenGL学习(四)给图案添加纹理_第3张图片

通过在加载图片前加入语句stbi_set_flip_vertically_on_load(true);可以翻转y轴:
OpenGL学习(四)给图案添加纹理_第4张图片

练习:

  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_REPEATOpenGL学习(四)给图案添加纹理_第5张图片

  • GL_MIRRORED_REPEATOpenGL学习(四)给图案添加纹理_第6张图片

  • GL_CLAMP_TO_EDGEOpenGL学习(四)给图案添加纹理_第7张图片

  • GL_CLAMP_TO_BORDEROpenGL学习(四)给图案添加纹理_第8张图片

  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);

效果如图OpenGL学习(四)给图案添加纹理_第9张图片

  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);
...;

OpenGL学习(四)给图案添加纹理_第10张图片

你可能感兴趣的:(OpenGL学习)