标注采样的位置,然后在其他片段上进行片段差值。Fragment Interpolation
使用纹理坐标获取纹理颜色叫做采样(Sampling),下面的图片展示了我们是如何把纹理坐标映射到三角形上的。
纹理超出(0,0)到(1,1)时的环绕方式。
纹理环绕方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
第一个参数指定纹理目标,2D
第二个参数指定纹理轴。
最后一个参数传递一个环绕方式。
如果选择GL_CLAMP_TO_BORDER选项,还要设置边缘的颜色,需要使用glTexParameter函数的fv后缀形式。
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
纹理过滤:
因为纹理坐标不依赖于分辨率,所以OpenGL需要知道怎么讲纹理像素映射到纹理坐标。
过滤分两种:
临近过滤:GL_NEAREST,Nearest Neighbor Filtering,是默认处理方式。
选择离中心点最近的那个像素颜色。
线性过滤,计算差值,选择混合色。
下图为两种的区别:
纹理环绕方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
第一个参数指定纹理目标,2D
第二个参数指定纹理轴。
最后一个参数传递一个环绕方式。
如果选择GL_CLAMP_TO_BORDER选项,还要设置边缘的颜色,需要使用glTexParameter函数的fv后缀形式。
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
纹理过滤:
因为纹理坐标不依赖于分辨率,所以OpenGL需要知道怎么讲纹理像素映射到纹理坐标。
过滤分两种:
临近过滤:GL_NEAREST,Nearest Neighbor Filtering,是默认处理方式。
选择离中心点最近的那个像素颜色。
线性过滤,计算差值,选择混合色。
可以为放大Magnify和缩小Minify时设置纹理过滤选项。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
多级渐远纹理:
简单概念:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理。性能很好。
两个级别Level层之间会产生僵硬,可以用之前提到的两种过滤。
也可以用下面四中过滤方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
四种方式为:
过滤方式 描述
GL_NEAREST_MIPMAP_NEAREST 使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
GL_LINEAR_MIPMAP_NEAREST 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
GL_NEAREST_MIPMAP_LINEAR 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
GL_LINEAR_MIPMAP_LINEAR 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
加载与创建纹理
SOIL又要下载。
后面使用SOIL_load_image函数加载图片。
参数:图片路径,两个int指针返回宽度和高度,图片的通道数量-0,加载方式RGB所以存储一个数组。
生成纹理:
纹理同样使用ID引用。
GLuint texture;
glGenTexture;
片段着色器也应该能访问纹理对象,GLSL提供一个供纹理对象使用的内建数据类型,采样器Sampler,它以纹理类型作为后缀。
之后会把纹理赋值给这个uniform
我们使用内建的texture函数来采样纹理颜色,它第一个参数是纹理采样器,第二个是纹理坐标。
法克,困扰了将近一上午的问题竟然图片路径问题,直接把图片拖到Source Files的话文件并不在该文件夹内。
得到图像:
代码:
source:
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include <SOIL.h>
void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);
//顶点着色器源码:
const GLuint WIDTH = 800, HEIGHT = 600;
int main()
{
std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;
//这都是初始化GLFW,后面的要看文档
glfwInit();
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);
//创建窗口,
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文
//不知道回调函数为什么要加在这里??
//glfwSetKeyCallback(window, key_callback2);回调函数只有最下面那个有作用
glfwSetKeyCallback(window, key_callback);
//初始化GLEW(管理OpenGL的函数指针)
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
glViewport(0, 0, WIDTH, HEIGHT);//前两个控制左下角的位置,后面是窗口宽度高度(像素)
Shader ourShader("shader.vs", "shader.frag");
GLfloat 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 // 左上
};
GLuint indices[] = { // Note that we start from 0!
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
GLuint VBO, VAO,EBO;
//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象??
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &EBO);
//绑定VAO
glBindVertexArray(VAO);
//复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
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(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
//因为现在是两个属性,需要向右移动步长,6个
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
//由于我们添加了额外的顶点属性,我们要告诉OPenGL新的顶点格式。
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
//安全?
//生成纹理
GLuint texture;
glGenTextures(1, &texture);
//glGenTextures函数首先需要输入生成纹理的数量,然后储存在GLuint数组中,让之前任何纹理指令都可以配置当前绑定的纹理。
//下面这一句是绑定纹理。
glBindTexture(GL_TEXTURE_2D, texture);
//设置到两个轴上
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT (usually basic wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//上面两句是对S和T轴设置环绕方式,环绕方式为重复纹理图像。
//下面生成纹理。2D纹理,因此纹理目标是GL_TEXTURE_2D
//下面两句设置方法和缩小的纹理过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//使用SOIL加载图片,自此到循环前都是加载并生成纹理。
int width, height;
std::cout << SOIL_last_result() << std::endl;
unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
std::cout << SOIL_last_result() << std::endl;
if (image)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
//第一个参数纹理目标Target,会生成与当前绑定纹理对象同样纹理
//设置纹理指定多级渐远纹理级别,0,基本级别
//第三个参数,纹理储存为何种格式,RGB
//四五是设置最终的纹理的宽度和高度,就用之前的变量。
//0
//七八位源图的格式和数据类型,使用RGB加载并储存为char(byte)数组
//最后参数为图像的数据
}
else
{
std::cout << "Failed to load texture." << std::endl;
}
//当前绑定的纹理对象会被附加上纹理图像,然而只有0级别的纹理图像被加载 了,多级的话需要手动设置
//或者可以,在生成纹理之后调用glGenerateMipmap,这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
//防止退出的循环
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//glClearColor是一个状态设置函数,glClear是一个状态应用函数
glBindTexture(GL_TEXTURE_2D, texture);
//绘图
ourShader.Use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
//回调函数
//释放:
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate(); //释放/删除之前的分配的所有资源
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
std::cout << key << std::endl;
//用户按下ESC键,我们设置window窗口WindowShouldClose属性为true
//关闭应用程序
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
顶点着色器:
#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;
void main()
{
gl_Position = vec4(position, 1.0f);
ourColor = color;
TexCoord = texCoord;
}
片段着色器:
#version 330 core
in vec3 ourColor;
in vec2 TexCoord;
out vec4 color;
uniform sampler2D ourTexture;
void main()
{
color = texture(ourTexture, TexCoord);
}
texture函数采样纹理颜色,第一个参数是纹理采样器,第二个是纹理坐标,该函数会使用之前设置好的纹理参数
对响应色值进行采样,输出纹理坐标过滤后的颜色。
采样器Sampler还不是很理解??作用大概是把纹理对象传给片段着色器。
然后可以把纹理颜色和顶点颜色混合
color = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f);
效果图:
OK,小面的说明正好解决我的疑惑。
没有给uniform赋值的原因:使用glUniform1i,我们可以给纹理采样器分配一个位置值。
这样我们能在一个片段着色器中设置多个纹理,一个纹理称为一个纹理单元。0为默认不用设置!
纹理单元TExture Unit,默认为0,
可以一次绑定多个纹理,通过纹理单元。
glActiveTexture(GL_TEXTURE0);//先激活
glBinfTexture(GL_TEXTURE_2D,texture);//
color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
最终输出颜色现在是两个纹理的结合。GLSL内建的mix函数需要接受两个值作为参数,
并对它们根据第三个参数进行线性插值。。
如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。
0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。
为了使用第二个纹理(以及第一个),我们必须改变一点渲染流程,先绑定两个纹理到对应的纹理单元,
然后定义哪个uniform采样器对应哪个纹理单元
纹理单元:
多个纹理单元可以混合mix:
多个纹理单元要激活了,而且写法也有了区别。注意下:
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include <SOIL.h>
void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);
//顶点着色器源码:
const GLuint WIDTH = 800, HEIGHT = 600;
int main()
{
std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;
//这都是初始化GLFW,后面的要看文档
glfwInit();
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);
//创建窗口,
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文
//不知道回调函数为什么要加在这里??
//glfwSetKeyCallback(window, key_callback2);回调函数只有最下面那个有作用
glfwSetKeyCallback(window, key_callback);
//初始化GLEW(管理OpenGL的函数指针)
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
glViewport(0, 0, WIDTH, HEIGHT);//前两个控制左下角的位置,后面是窗口宽度高度(像素)
Shader ourShader("shader.vs", "shader.frag");
GLfloat 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 // 左上
};
GLuint indices[] = { // Note that we start from 0!
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
GLuint VBO, VAO,EBO;
//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象??
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &EBO);
//绑定VAO
glBindVertexArray(VAO);
//复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
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(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
//因为现在是两个属性,需要向右移动步长,6个
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
//由于我们添加了额外的顶点属性,我们要告诉OPenGL新的顶点格式。
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
//安全?
//生成纹理
GLuint texture1,texture2;
glGenTextures(1, &texture1);//??
//glGenTextures(1, &texture2);
//glGenTextures函数首先需要输入生成纹理的数量,然后储存在GLuint数组中,让之前任何纹理指令都可以配置当前绑定的纹理。
//下面这一句是绑定纹理。
//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);
//设置到两个轴上
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT (usually basic wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//上面两句是对S和T轴设置环绕方式,环绕方式为重复纹理图像。
//下面生成纹理。2D纹理,因此纹理目标是GL_TEXTURE_2D
//下面两句设置方法和缩小的纹理过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//使用SOIL加载图片,自此到循环前都是加载并生成纹理。
int width, height;
std::cout << SOIL_last_result() << std::endl;
unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
std::cout << SOIL_last_result() << std::endl;
if (image)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
//第一个参数纹理目标Target,会生成与当前绑定纹理对象同样纹理
//设置纹理指定多级渐远纹理级别,0,基本级别
//第三个参数,纹理储存为何种格式,RGB
//四五是设置最终的纹理的宽度和高度,就用之前的变量。
//0
//七八位源图的格式和数据类型,使用RGB加载并储存为char(byte)数组
//最后参数为图像的数据
}
else
{
std::cout << "Failed to load texture." << std::endl;
}
//当前绑定的纹理对象会被附加上纹理图像,然而只有0级别的纹理图像被加载 了,多级的话需要手动设置
//或者可以,在生成纹理之后调用glGenerateMipmap,这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
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);
image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
//防止退出的循环
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//glClearColor是一个状态设置函数,glClear是一个状态应用函数
//绘图
ourShader.Use();
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);
//这里因为要使用第二个纹理,改变一点渲染流程,先绑定两个纹理到对应的纹理单元,然后定义哪个uniform采样器应对哪个纹理单元。
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
//回调函数
//释放:
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate(); //释放/删除之前的分配的所有资源
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
std::cout << key << std::endl;
//用户按下ESC键,我们设置window窗口WindowShouldClose属性为true
//关闭应用程序
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
图示上下颠倒,原因OpenGL的y轴的(0,0)坐标在图片底部,但图片的y轴(0,0)坐标通常在顶部。
:我们可以编辑顶点着色器来自动翻转y坐标,替换TexCoord的值为TexCoord = vec2(texCoord.x, 1.0f - texCoord.y);。
替换后图片正过来了,改的也就是纹理坐标的y轴。
联系:
1.让笑脸朝另一个方向:
把应对于y轴的方法,同样施给x轴。
TexCoord = vec2(1.0f-texCoord.x, 1.0f-texCoord.y);
2.尝试用不同的纹理环绕方式,设定一个从0.0f到2.0f范围内的(而不是原来的0.0f到1.0f)纹理坐标。试试看能不能在箱子的角落放置4个笑脸:参考解答,结果。记得一定要试试其他的环绕方式。
把纹理坐标改成0.5时的效果:
设置成0.0到2.0效果:
第一个纹理目标更换为GL_CLAMP_TO_EDGE,边缘拉伸环绕方式。
镜像的环绕方式: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);
通过该函数来指定颜色。
3.尝试在矩形上只显示纹理图像的中间一部分,修改纹理坐标,达到能看见单个的像素的效果。尝试使用GL_NEAREST的纹理过滤方式让像素显示得更清晰
更改纹理过滤方式:GL_NEAREST 把范围缩的很小:
稍微有那么一点点的evil。
4.使用一个uniform变量作为mix函数的第三个参数来改变两个纹理可见度,使用上和下键来改变箱子或笑脸的可见度
这个做出来还是很有成就感的。
更改uniform全局关键字:
glUniform1f(glGetUniformLocation(ourShader.Program, "para"), Offset);
在回调函数中添加操作:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
std::cout << key << std::endl;
//用户按下ESC键,我们设置window窗口WindowShouldClose属性为true
//关闭应用程序
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key == GLFW_KEY_UP&&action == GLFW_PRESS)
{
Offset += 0.1;
if (Offset >= 1.0f) Offset = 1.0f;
}
if (key == GLFW_KEY_DOWN&&action == GLFW_PRESS)
{
Offset -= 0.1;
if (Offset <= 0.0f) Offset = 0.0f;
}
}
不要忘了添加一个全局变量 Offset
如果读者有什么问题,可以直接留言或者私信我,我会努力帮忙解决的。