纹理
每个顶点就会关联着一个纹理坐标,Texture Coordinate:
标注采样的位置,然后在其他片段上进行片段差值。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 // GLFW#include #include #include "Shader.h"#include 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 corelayout(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 corein 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 // GLFW#include #include #include "Shader.h"#include 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
如果读者有什么问题,可以直接留言或者私信我,我会努力帮忙解决的。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~