[OpenGL]纹理

文章目录[x]
  1. 1:自定义Texture类
  2. 2:笔记

自定义Texture类


首先,下载 stb_image.h
然后,加入到工程中
最后,新建一个 stb_image.cpp,在里面写入下面代码

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

 

.h文件
#pragma once

#include "Renderer.h"


class Texture
{
private:
    unsigned int m_RendererID;
    std::string m_FilePath;
    unsigned char* m_LocalBuffer;
    int m_Width, m_Height, m_BPP;
    int m_UWrap, m_VWrap;
    int m_MinFilter, m_MagFilter;

public:
    Texture(const std::string& path, 
        int uWrap = GL_CLAMP_TO_EDGE, int vWrap = GL_CLAMP_TO_EDGE,
        int minFilter = GL_NEAREST, int magFilter = GL_LINEAR);
    ~Texture();

    void Bind(unsigned int slot = 0) const;
    void UnBind() const;

    inline int GetWidth() const { return m_Width; }
    inline int GetHeight() const { return m_Height; }

private:
    void Rebuild(int flag_true_if_should_flip = true);

};

 

.cpp文件
#include "Texture.h"
#include "stb_image/stb_image.h"

Texture::Texture(const std::string& path, int uWrap, int vWrap, int minFilter, int magFilter) :
    m_FilePath(path), m_RendererID(0), m_Width(0), m_Height(0), m_BPP(0), 
    m_UWrap(uWrap), m_VWrap(vWrap), m_MinFilter(minFilter), m_MagFilter(magFilter)
{
    GLCall(glGenTextures(1, &m_RendererID));
    GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));

    // GL_REPEAT	        对纹理的默认行为。重复纹理图像。
    // GL_MIRRORED_REPEAT	和GL_REPEAT一样,但每次重复图片是镜像放置的。
    // GL_CLAMP_TO_EDGE	    纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
    // GL_CLAMP_TO_BORDER	超出的坐标为用户指定的边缘颜色。
    // 设置纹理环绕方式  S,T代表X,Y,即U,V
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, uWrap));
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vWrap));
    // 设置纹理过滤方式,当纹理缩小时采用NEAREST, 放大时采用LINEAR
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter));
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter));

    // 设置多级渐远纹理,即Mipmap
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));

    Rebuild();
}

Texture::~Texture()
{
    GLCall(glDeleteTextures(1, &m_RendererID));
}

void Texture::Bind(unsigned int slot /*= 0*/) const
{
    GLCall(glActiveTexture(GL_TEXTURE0 + slot));
    GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));
}

void Texture::UnBind() const
{   
    GLCall(glBindTexture(GL_TEXTURE_2D, 0));
}

void Texture::Rebuild(int flag_true_if_should_flip /* = true*/ )
{
    stbi_set_flip_vertically_on_load(flag_true_if_should_flip);
    m_LocalBuffer = stbi_load(m_FilePath.c_str(), &m_Width, &m_Height, &m_BPP, 4); // 4 == RGBA

    if (m_LocalBuffer)
    {
        // 参数:绑定纹理类型,设置多级渐远纹理级别,设置以何种方式存储GL_RGBA8代表每个通道有8个bits,宽,高,这个参数总是0,使用RGB方式加载,存储为(char)byte数组,图像颜色数据
        GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_LocalBuffer));
        GLCall(glGenerateMipmap(GL_TEXTURE_2D));

        // 释放纹理
        stbi_image_free(m_LocalBuffer);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
}

 

笔记


单张纹理绘制
class Basic : public SubTestBed
{
public:
    Basic(const std::string& shaderName) : SubTestBed(shaderName) { }

    virtual void Setup() override
    {
        float vertices[] = {
            // --- 位置 ---      --- 颜色 ---     --- 纹理坐标 ---
            -0.5f, -0.5f, 0,  1.0f, 0.0f, 0.0f,   0.0f, 0.0f,
            +0.5f, -0.5f, 0,  0.0f, 1.0f, 0.0f,   1.0f, 0.0f,
            +0.5f, +0.5f, 0,  0.0f, 0.0f, 1.0f,   1.0f, 1.0f,
            -0.5f, +0.5f, 0,  1.0f, 1.0f, 0.0f,   0.0f, 1.0f
        };

        unsigned int indices[] = {
            0, 1, 2,
            2, 3, 0
        };

        m_VA = new VertexArray();
        m_VB = new VertexBuffer(vertices, sizeof(vertices));
        VertexBufferLayout layout;
        layout.Push<float>(3);
        layout.Push<float>(3);
        layout.Push<float>(2);
        m_VA->AddBuffer(*m_VB, layout);
        m_IB = new IndexBuffer(indices, 6);
        m_Texture[0] = new Texture("Resources/Textures/container.jpg");

        m_VB->UnBind();
        m_VA->UnBind();
        m_IB->UnBind();
        m_Texture[0]->UnBind();
    }


    virtual void Render() override
    {
        m_Texture[0]->Bind(191); // 我的GPU是RTX 2060 位宽192bit,所以最大填191
        this->m_Renderer.Draw(*m_VA, *m_IB, *m_Shader);
        m_Shader->SetUniform1i("u_Texture191", 191);
    }
};
#Shader Vertex
#version 330 core  

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_Color;
layout(location = 2) in vec2 a_TexCoord;

out vec3 c_Color;
out vec2 v_TexCoord;

void main()
{
    gl_Position = vec4(a_position, 1.0);
    c_Color = a_Color;
    v_TexCoord = a_TexCoord;
}


#Shader Fragment
#version 330 core   
    
out vec4 fragColor; 

in vec3 c_Color;
in vec2 v_TexCoord;

uniform sampler2D u_Texture191;

void main()                                
{                 
    fragColor = texture(u_Texture191, v_TexCoord) * vec4(c_Color, 1.0);
};

 

两张纹理
// 从上面的Basic基础上修改,在Setup()中添加
m_Texture[1] = new Texture("Resources/Textures/awesomeface.png");
m_Texture[1]->UnBind();

// Render()
virtual void Render() override
{
    m_Texture[0]->Bind(190);
    m_Texture[1]->Bind(191);
    this->m_Renderer.Draw(*m_VA, *m_IB, *m_Shader);
    m_Shader->SetUniform1i("u_Texture190", 190);
    m_Shader->SetUniform1i("u_Texture191", 191);
}
// 在Basic.shader上修改
#Shader Fragment
#version 330 core   
    
out vec4 fragColor; 

in vec3 c_Color;
in vec2 v_TexCoord;

uniform sampler2D u_Texture190;
uniform sampler2D u_Texture191;

void main()                                
{                 
    vec4 texColor190 = texture(u_Texture190, v_TexCoord);
    vec4 texColor191 = texture(u_Texture191, v_TexCoord);
    fragColor = mix(texColor190, texColor191, 0.2)  * vec4(c_Color, 1.0);
};

 

练习题1: 修改片段着色器,仅让笑脸图案朝另一个方向看

// CPP代码使用Basic

// FaceInvert.shader
#Shader Vertex
#version 330 core

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_Color;
layout(location = 2) in vec2 a_TexCoord;

out vec3 c_Color;
out vec2 v_TexCoord;

void main()
{
    gl_Position = vec4(a_position, 1.0);
    c_Color = a_Color;
    v_TexCoord = a_TexCoord;
}


#Shader Fragment
#version 330 core

out vec4 fragColor;

in vec3 c_Color;
in vec2 v_TexCoord;

uniform sampler2D u_Texture190;
uniform sampler2D u_Texture191;

void main()
{
    vec4 texColor190 = texture(u_Texture190, v_TexCoord);
    vec2 texCoord = vec2(1 - v_TexCoord.x, v_TexCoord.y);  // 水平翻转
    vec4 texColor191 = texture(u_Texture191, texCoord);
    fragColor = mix(texColor190, texColor191, 0.2);
};

 

练习题2: 尝试用不同的纹理环绕方式,设定一个从0.0f到2.0f范围内的(而不是原来的0.0f到1.0f)纹理坐标。试试看能不能在箱子的角落放置4个笑脸
class DifferentWrap : public SubTestBed
{
public:
    DifferentWrap(const std::string& shaderName) : SubTestBed(shaderName) { }

    virtual void Setup() override
    {
        float vertices[] = {
            // --- 位置 ---      --- 颜色 ---     --- 纹理坐标 ---
            -0.5f, -0.5f, 0,  1.0f, 0.0f, 0.0f,   0.0f, 0.0f,
            +0.5f, -0.5f, 0,  0.0f, 1.0f, 0.0f,   2.0f, 0.0f,
            +0.5f, +0.5f, 0,  0.0f, 0.0f, 1.0f,   2.0f, 2.0f,
            -0.5f, +0.5f, 0,  1.0f, 1.0f, 0.0f,   0.0f, 2.0f
        };

        unsigned int indices[] = {
            0, 1, 2,
            2, 3, 0
        };

        m_VA = new VertexArray();
        m_VB = new VertexBuffer(vertices, sizeof(vertices));
        VertexBufferLayout layout;
        layout.Push<float>(3);
        layout.Push<float>(3);
        layout.Push<float>(2);
        m_VA->AddBuffer(*m_VB, layout);
        m_IB = new IndexBuffer(indices, 6);
        m_Texture[0] = new Texture("Resources/Textures/container.jpg");
        m_Texture[1] = new Texture("Resources/Textures/awesomeface.png", GL_REPEAT, GL_REPEAT);

        m_VB->UnBind();
        m_VA->UnBind();
        m_IB->UnBind();
        m_Texture[0]->UnBind();
        m_Texture[1]->UnBind();
    }


    virtual void Render() override
    {
        m_Texture[0]->Bind(190);
        m_Texture[1]->Bind(191); 
        this->m_Renderer.Draw(*m_VA, *m_IB, *m_Shader);
        m_Shader->SetUniform1i("u_Texture190", 190);
        m_Shader->SetUniform1i("u_Texture191", 191);
    }
};

// 使用 FaceInvert.shader

 

练习题3: 尝试在矩形上只显示纹理图像的中间一部分,修改纹理坐标,达到能看见单个的像素的效果。尝试使用GL_NEAREST的纹理过滤方式让像素显示得更清晰
// class PartTexture
// 在习题2中的 DifferentWrap中的Setup()里修改
// 纹理坐标修改为 0.45f ~ 0.55f
float vertices[] = {
    // --- 位置 ---      --- 颜色 ---     --- 纹理坐标 ---
    -0.5f, -0.5f, 0,  1.0f, 0.0f, 0.0f,   0.45f, 0.45f,
    +0.5f, -0.5f, 0,  0.0f, 1.0f, 0.0f,   0.55f, 0.45f,
    +0.5f, +0.5f, 0,  0.0f, 0.0f, 1.0f,   0.55f, 0.55f,
    -0.5f, +0.5f, 0,  1.0f, 1.0f, 0.0f,   0.45f, 0.55f
};

// 纹理过滤方式修改为 GL_NEAREST
m_Texture[0] = new Texture("Resources/Textures/container.jpg", GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST);
m_Texture[1] = new Texture("Resources/Textures/awesomeface.png", GL_REPEAT, GL_REPEAT, GL_NEAREST, GL_NEAREST);

// 使用 FaceInvert.shader

 

练习题4: 使用一个uniform变量作为mix函数的第三个参数来改变两个纹理可见度,使用上和下键来改变箱子或笑脸的可见度
// class UniformMix
// 在Basic上修改Render(),由于已经我们在沙盒中写的代码,所以如果要接受键盘事件,就需要写Event/BroadMessage
// 所以这里我直接改成了下列形式,效果是一样的
virtual void Render() override
{
    m_Texture[0]->Bind(190);
    m_Texture[1]->Bind(191);
    this->m_Renderer.Draw(*m_VA, *m_IB, *m_Shader);
    m_Shader->SetUniform1i("u_Texture190", 190);
    m_Shader->SetUniform1i("u_Texture191", 191);
    m_Mix = (float)glfwGetTime();
    m_Mix = (sin(m_Mix) / 2) + 0.5f;
    m_Shader->SetUniform1f("u_Mix", m_Mix);
}
// vertex shader使用Basic.shader中的即可
#Shader Fragment
#version 330 core

out vec4 fragColor;

in vec3 c_Color;
in vec2 v_TexCoord;

uniform sampler2D u_Texture190;
uniform sampler2D u_Texture191;
uniform float u_Mix;

void main()
{
    vec4 texColor190 = texture(u_Texture190, v_TexCoord);
    vec4 texColor191 = texture(u_Texture191, v_TexCoord);
    fragColor = mix(texColor190, texColor191, u_Mix);
};

原GIF颜色256导出来有5.5M,就将就欣赏一下这个16色的GIF吧。哈哈

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像