OpenGL多层纹理叠加MIX(九-1)
1 前提:两张图片大小一样,进行 MIX混合,两个图片要是24位RGB就都是24位,如果一个是24位RGB,一个是32位RGBA,请加载纹理数据的时候使用同样数量的图层
(图片来源于网络截图,不商用,仅进行博客demo展示)
再譬如:
两个采样器进行采样,之后进行mix混合;参考:(17条消息) OpenGL纹理叠加_LV小猪精的博客-CSDN博客_opengl 纹理叠加
#include "stdafx.h" #include<windows.h> #include <glad/glad.h> #include <GLFW/glfw3.h> #define STB_IMAGE_IMPLEMENTATION #include <stb_image.h> #include<string> #include<fstream> #include<sstream> #include<iostream> #include <stdio.h> // settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; unsigned int VBO = 0; unsigned int VAO = 0; unsigned int EBO = 0; unsigned int texturePIC = 0; unsigned int texturePIC2 = 0; int shaderProgram = 0; GLuint texId_bottom = 99; GLuint texId_top = 99; //本地文件夹下有个图片加载到项目上(注意参数列表中的引用表示变量本身,不要用变量的副本) void LoadPicture(unsigned int& textureIndex,unsigned int& textureIndex2) { //返回不同采样器的序号 glUseProgram(shaderProgram); texId_bottom = glGetUniformLocation(shaderProgram, " ourTextureB"); texId_top = glGetUniformLocation(shaderProgram, "ourTextureT"); glGenTextures(1, &textureIndex); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureIndex); //为bind的纹理设置环绕,过滤方式 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); //加载图片生成纹理 : stbi_set_flip_vertically_on_load(true); //stbi是一个图片载入的开源组件,文件名,宽高,通道数,你期望的通道数(使用的是宽大于高的图片,如果是高>宽,程序需要改否则渲染异常) int W, H, channels_in_file, desired_channels = 3; unsigned char* data = stbi_load("./3.jpg", &W, &H, &channels_in_file, desired_channels); if (channels_in_file == 3) { //数据生成纹理;根据指定的参数,把输入数据生成一张2D纹理 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, W, H, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glUniform1i(texId_bottom,0); //生成mipmap数组 glGenerateMipmap(GL_TEXTURE_2D); } stbi_image_free(data); data = nullptr; //加载彩虹 glGenTextures(1, &textureIndex2); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureIndex2); //为bind的纹理设置环绕,过滤方式 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); //加载图片生成纹理 : stbi_set_flip_vertically_on_load(true); //stbi是一个图片载入的开源组件,文件名,宽高,通道数,你期望的通道数 int W2, H2, channels_in_file2, desired_channels2 = 3; unsigned char* data2 = stbi_load("./tm.bmp", &W2, &H2, &channels_in_file2, desired_channels2); if (channels_in_file2 == 3) { //数据生成纹理;根据指定的参数,把输入数据生成一张2D纹理 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, W2, H2, 0, GL_RGB, GL_UNSIGNED_BYTE, data2); glUniform1i(texId_top, 1); //生成mipmap数组 glGenerateMipmap(GL_TEXTURE_2D); } stbi_image_free(data2); data2 = nullptr; glUseProgram(0); } void render() { glBindVertexArray(VAO); glUseProgram(shaderProgram); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glUseProgram(0); glBindVertexArray(0); } void initmodule() { //做个一模型;正方形;映射了顶点坐标和纹理坐标的对应关系 float vertexs[] = { //顶点坐标-------纹理坐标 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 右上 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下 -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 }; //一个正方形是由两个三角形得来的;记录顶点的索引顺序 unsigned int indexs[] = { 0,1,3, 1,2,3, }; //做VAO glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); //做VBO glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); //创建显存空间 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_STATIC_DRAW); //设置索引缓冲 glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexs), indexs, GL_STATIC_DRAW); //设置纹理图片 //加载纹理图片,生成纹理 LoadPicture(texturePIC, texturePIC2); //绑定纹理,OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15 //设置第0个锚点,3个点,不需要归一化,跨度5个float可以读下一个点 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); //打开顶点 glEnableVertexAttribArray(0); //纹理属性设置,纹理在第一个锚点上(指定顶点数据)你在顶点着色器程序中制定了锚点1的位置对应的是纹理坐标 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); //打开纹理 glEnableVertexAttribArray(1); //解除绑定VBO glBindBuffer(GL_ARRAY_BUFFER, 0); //解绑VAO glBindVertexArray(0); } void initshader(const char* verpath, const char* fragpath) { //编译shader,并记录shaderID std::string VerCode(""); std::string fregCode(""); //读文件 std::ifstream vShaderFile; std::ifstream fShaderFile; vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { vShaderFile.open(verpath); fShaderFile.open(fragpath); std::stringstream vsstream, fsstream; vsstream << vShaderFile.rdbuf(); fsstream << fShaderFile.rdbuf(); VerCode = vsstream.str(); fregCode = fsstream.str(); } catch (const std::exception&) { std::cout << "read file error" << std::endl; } const char* vshader = VerCode.c_str(); const char* fshader = fregCode.c_str(); //shader 编译连接 unsigned int vertexID = 0, fragID = 0; char infoLog[512];//存储错误信息 int successflag = 0; vertexID = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexID, 1, &vshader, NULL); glCompileShader(vertexID); //获取编译是否成功 glGetShaderiv(vertexID, GL_COMPILE_STATUS, &successflag); if (!successflag) { glGetShaderInfoLog(vertexID, 512, NULL, infoLog); std::string errstr(infoLog); std::cout << "v shader err" << infoLog; } //frag fragID = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragID, 1, &fshader, NULL); glCompileShader(fragID); //获取编译是否成功 glGetShaderiv(fragID, GL_COMPILE_STATUS, &successflag); if (!successflag) { glGetShaderInfoLog(fragID, 512, NULL, infoLog); std::string errstr(infoLog); std::cout << "f shader err" << infoLog; } //链接 shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexID); glAttachShader(shaderProgram, fragID); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &successflag); if (!successflag) { glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog); std::string errstr(infoLog); std::cout << "link error"; } //编译完成后,可以把中间的步骤程序删除 glDeleteShader(vertexID); glDeleteShader(fragID); } void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { //将窗口设置为关闭,跳出循环 glfwSetWindowShouldClose(window, true); } } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } int main() { //glfw初始化 glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //glfw创建窗口 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); if (window == NULL) { printf("创建窗口失败"); //终止 glfwTerminate(); return -1; } //显示窗口 glfwMakeContextCurrent(window); //设置回调,当窗口大小调整后将调用该回调函数 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // glad初始化 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { printf("加载失败"); return -1; } initshader("vertexShader.glsl", "fragmentShader.glsl"); initmodule(); // 使用循环达到循环渲染效果 while (!glfwWindowShouldClose(window)) { //自定义输入事件 processInput(window); glClearColor(0.5f, 0.5f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); render(); //交互缓冲区,否则显示空白 glfwSwapBuffers(window); //输入输出事件,否则无法对窗口进行交互 glfwPollEvents(); } //终止渲染 关闭并清理glfw本地资源 glfwTerminate(); return 0; }
#version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D ourTextureB; uniform sampler2D ourTextureT; void main() { //FragColor = texture(ourTexture,TexCoord); //mix(a,b,factor)=a*(1-factor)+b*factor 作者:__inker https://www.bilibili.com/read/cv10694142/ 出处:bilibili FragColor = mix(texture(ourTextureT, TexCoord), texture(ourTextureB, TexCoord), 0.8); //FragColor = texture(ourTextureT, TexCoord)*0.7 + texture(ourTextureB, TexCoord)*1; };
#version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec2 texCoord; out vec2 TexCoord; void main() { gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0); TexCoord = texCoord; };
使用小狗狗和彩虹,由于彩虹是透明的,所以直接像素相加也行:
FragColor = texture(ourTextureT, TexCoord)*0.6+ texture(ourTextureB, TexCoord)*1;
//本文使用的是mix;blend也可以用于图片叠加、