texture 纹理(贴图)

本文详细介绍了如何在OpenGL中使用纹理叠加技术,通过两张图片的叠加来增加物体的细节表现。文章提供了完整的源代码示例,包括使用stb_image.h库解析图片、设置纹理坐标、创建纹理缓冲对象等关键步骤。

纹理

纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节。
这是两张照片叠加的效果
在这里插入图片描述
由下面两张叠加而成
在这里插入图片描述
在这里插入图片描述

源代码

在这里插入图片描述

shaders类在自定义着色器 中有完整的源代码。
下边我们使用stb_image.h来解析图片。

#define GLEW_STATIC // 这个一定要加不然报错 静态链接库

#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include<iostream>
#include "Shaders.h"

#define STB_IMAGE_IMPLEMENTATION

#include "stb_image.h"

using namespace std;

void processInput(GLFWwindow);

void processInput(GLFWwindow *window) {
    //如果键盘输入esc 则触发 退出
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        // 设置 要求退出
        glfwSetWindowShouldClose(window, true);
    }
}

// 逆时针方向绘制  默认情况下,逆时针的顶点连接顺序被定义为三角形的正
// 逆时针或顺时针都是相对于观察者方向的
// uv(st)坐标 u(s)为x轴 v(t)为y轴  是给图片定义的 范围都是0 - 1
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, 2, //第一个三角形的索引
        2, 3, 0 //第二个三角形的索引
};

int main() {



    // 初始化GLFW
    glfwInit();
    // 提示 我们使用的版本是3.3
    // 主版本
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    // 次版本
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    // 简介
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    // 创建一个窗口对象
    GLFWwindow *window = glfwCreateWindow(800, 600, "Test window", NULL, NULL);
    if (window == NULL) {
        cout << "open window failed." << endl;
        // 终止 glfw
        glfwTerminate();
    }
    // 绑定window到上下文对象 创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
    glfwMakeContextCurrent(window);

    glewExperimental = true;
    // GLEW_OK 0
    //init GLEW
    if (glewInit() != GLEW_OK) {
        cout << "glew init failed." << endl;
        // 终止 glfw
        glfwTerminate();
        return -1;
    }
    // OpenGL渲染窗口的尺寸大小
    // glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)
    glViewport(0, 0, 800, 600);

    // 设置剔除 (opegl默认正面背面都显示(不剔除))
    //glEnable(GL_CULL_FACE);
    // 剔除背面 GL_BACK 剔除正面 GL_FRONT
    //glCullFace(GL_BACK);
    // 线框模式
    //第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    //VAO对象
    unsigned int VAO;
    // 生成一个VAO对象 这个方法可以生成多个 由第一个参数决定
    glGenVertexArrays(1, &VAO);
    // 绑定 VAO
    glBindVertexArray(VAO);

    unsigned int VBO; //如果多个可以用 VBO[]数组 这个方法可以生成多个 由第一个参数决定
    glGenBuffers(1, &VBO);
    //将新创建的缓冲绑定到 GL_ARRAY_BUFFER目标上
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // glBufferData 是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
    // GL_STATIC_DRAW 数据不会或几乎不会改变。
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    unsigned int EBO;
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    Shaders *shaders = new Shaders("./shaders/vertexSource.txt", "./shaders/fragmentSource.txt");



    // glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
    // 从 0号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为0
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) 0);
    // 以顶点属性位置值作为参数,启用顶点属性;顶点属性默认是禁用的
    //读取到0号栏位上
    glEnableVertexAttribArray(0);
    // 读取颜色属性 从 1号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为3个float
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (3 * sizeof(float)));
    // 读取到1号栏位上
    glEnableVertexAttribArray(1);

    // 读取纹理坐标
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    // 纹理缓冲对象
    unsigned int TexBufferA;
    unsigned int TexBufferB;
    //创建纹理缓冲对象
    glGenTextures(1, &TexBufferA);

    // 绑定
    //glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, TexBufferA);
    //glActiveTexture(GL_TEXTURE3);


    // stb_image.h 使用
    int width, height, nrchannels;
    //  在OpenGl中 y轴的读取是与正常图片相反了 所以需要 设置 翻转(flip)垂直(vertically)加载(load) 为true
    stbi_set_flip_vertically_on_load(true);
    //获取图片的rgb
    unsigned char *data1 = stbi_load("container.jpg", &width, &height, &nrchannels, 0);
    cout<<width<<" "<<height<<endl;
    if (data1) {

        // 纹理可以通过glTexImage2D来生成
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data1);
        //直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。 必须要有 glGenerateMipmap 否则无法显示纹理 
        glGenerateMipmap(GL_TEXTURE_2D);

    } else {
        cout << "load image failed." << endl;
    }
    //释放图像的内存
    stbi_image_free(data1);
    glBindTexture(GL_TEXTURE_2D,0);

    glGenTextures(1, &TexBufferB);
    glBindTexture(GL_TEXTURE_2D, TexBufferB);

    unsigned char *data2 = stbi_load("ground-texture.jpg", &width, &height, &nrchannels, 0);
    cout<<width<<" "<<height<<endl;
    if (data2) {

        // 纹理可以通过glTexImage2D来生成
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data2);
        //直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
        glGenerateMipmap(GL_TEXTURE_2D);

    } else {
        cout << "load image failed." << endl;
    }
    stbi_image_free(data2);
    glBindTexture(GL_TEXTURE_2D,0);
    //渲染循环 ,它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:
    //glfwWindowShouldClose 我们每次循环的开始前检查一次GLFW是否被要求退出
    while (!glfwWindowShouldClose(window)) {

        //自定义事件 当键盘触发esc 退出
        processInput(window);

        glClearColor(0.2, 0.3, 0.3, 1.0);
        // GL_COLOR_BUFFER_BIT 颜色,GL_DEPTH_BUFFER_BIT 深度 和 GL_STENCIL_BUFFER_BIT 模板
        // 清除前面的那一帧的颜色
        glClear(GL_COLOR_BUFFER_BIT);

        // 绑定 TextureBuffer
		
		
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, TexBufferA);


        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, TexBufferB);

        // 绑定 VAO
        glBindVertexArray(VAO);


        shaders->use();
        // 如果只有单个纹理,则不需要以下操作,OpenGL会自动绑定
        glUniform1i(glGetUniformLocation(shaders->ID, "ourTexture1"), 0);
        glUniform1i(glGetUniformLocation(shaders->ID, "ourTexture3"), 3);


        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//可以不需要这个 绑定VAO的同时也会自动绑定EBO
        //glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        //解绑VAO
        glBindVertexArray(0);


        glfwSwapBuffers(window);

        glfwPollEvents();
    }

    // 最后终止 glfw
    glfwTerminate();


   


    return 0;
}

shaders

顶点着色器

#version 330 core                                      
// 0号栏位读取顶点坐标
layout (location = 0) in vec3 aPos;                    
//1号栏位读取颜色颜色
layout (location = 1) in vec3 aColor;
// 2号栏位读取 纹理坐标
layout (location = 2) in vec2 aTexCoord;
out vec4 vertexColor;
out vec2 TexCoord;
void main()
{                                                      
//  gl_Position 是固定的名称 用来保存 顶点坐标的
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    vertexColor = vec4(aColor.x,aColor.y,aColor.z,1.0);
    TexCoord = aTexCoord;
}                                                      

片元着色器

#version 330 core                          
out vec4 FragColor;                        
//uniform vec4 outColor;                     
in vec4 vertexColor;
// 获取顶底着色器过来的纹理坐标
in vec2 TexCoord;
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture3;
void main()
{                                          
    //FragColor = vertexColor;
    // 采样纹理的颜色 第一个参数是纹理采样器 是cpu传过来的 第二个参数的对应的纹理坐标
      FragColor  = mix(texture(ourTexture1,TexCoord) , texture(ourTexture3,TexCoord),0.55);
}                                          

stb_image.h 代码

WebGLRenderingContext 是 WebGL 的核心接口,它提供了用于在网页上绘制 3D 图形的方法和属性。通过 WebGLRenderingContext,开发者可以调用 WebGL 的各种功能,如创建和管理着色器、缓冲区、纹理等,从而实现复杂的 3D 场景渲染。在 WebGL 中,使用 `canvas` 元素获取 WebGLRenderingContext 实例,进而进行后续的图形绘制操作,就像在引用[2]中提到的 WebGL 允许使用 JavaScript 调用封装过的 OpenGL ES2.0 标准接口一样,WebGLRenderingContext 是 JavaScript 与底层图形渲染功能交互的桥梁。 WebGLBuffer 是用于存储顶点数据(如顶点坐标、颜色、法线等)的对象。在 WebGL 中,为了将数据传递给 GPU 进行处理,需要将数据存储在 WebGLBuffer 中。开发者可以使用 WebGLRenderingContext 的方法创建、绑定和填充 WebGLBuffer,然后在绘制时将其与着色器程序关联起来,以实现对顶点数据的高效处理和渲染。例如,当需要绘制一个复杂的 3D 模型时,将模型的顶点数据存储在 WebGLBuffer 中,可以提高数据传输和处理的效率。 WebGLShader 是 WebGL 中的着色器对象,分为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。顶点着色器负责处理每个顶点的位置、颜色等属性,将顶点从模型空间转换到裁剪空间;片元着色器则负责计算每个像素的颜色值。在 WebGL 中,开发者需要编写 GLSL(OpenGL Shading Language)代码来实现顶点着色器和片元着色器的功能,并使用 WebGLRenderingContext 的方法创建、编译和链接这些着色器。如引用[3]中展示了获取着色器程序的相关代码,其中涉及到创建和使用着色器的操作,通过这些操作可以实现自定义的图形渲染效果。 WebGLTexture 是用于存储图像数据的对象,在 WebGL 中用于实现纹理映射。纹理映射是将二维图像(纹理)应用到 3D 模型表面的技术,可以增加模型的真实感和细节。开发者可以使用 WebGLRenderingContext 的方法创建、绑定和加载纹理数据,然后在片元着色器中使用纹理坐标来采样纹理图像,从而将纹理应用到模型表面。例如,在绘制一个带有纹理的 3D 立方体时,将纹理图像存储在 WebGLTexture 中,并在片元着色器中进行纹理采样,就可以实现立方体表面的纹理效果。 ```javascript // 示例代码:创建 WebGLRenderingContext const canvas = document.getElementById('glCanvas'); const gl = canvas.getContext('webgl'); // 创建 WebGLBuffer const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); const vertices = [/* 顶点数据 */]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 创建 WebGLShader const vertexShaderSource = ` attribute vec4 a_position; void main() { gl_Position = a_position; } `; const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); const fragmentShaderSource = ` precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `; const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); // 创建 WebGLTexture const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // 加载纹理数据 const image = new Image(); image.onload = function() { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.generateMipmap(gl.TEXTURE_2D); }; image.src = 'texture.jpg'; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值