【问题标题】:Texture gets over written when using multiple textures in GLSL shader在 GLSL 着色器中使用多个纹理时,纹理会被覆盖
【发布时间】:2021-04-07 12:49:27
【问题描述】:

我正在努力将多个纹理发送到单个着色器,并且遇到了一个奇怪的问题,即着色器中的两个采样器似乎都获得了相同的纹理数据。我知道还有很多其他的多重纹理问题和答案(这里有一些我已经读过很多遍了123)但有些错误是躲避我,我开始失去理智。我相当有信心我已正确设置所有内容,但显然仍然存在一些问题。

所以,目前我有 Shape、Material、Texture 和 Shader 类。我的形状类是执行实际绘制的父级。它有一个材质成员,它有一个着色器和一组纹理。材质类绘制如下所示:

void Shape::Draw(GLenum mode, glm::mat4& model, glm::mat4& view, glm::mat4& proj)
{
    m_Material.Enable();
    m_Material.UpdateTransform(model, view, proj);
    glBindVertexArray(m_VAO);
    glDrawElements(mode, m_NumVerts, GL_UNSIGNED_INT, 0);
    m_Material.Disable();
}

这是我的整个材料课:

#include "pch.h"
#include "Material.h"

Material::Material() :
    m_LightService(LightService::GetInstance())
{
    OGLR_CORE_INFO("CREATING MATERIAL");
}

void Material::SetShader(std::string fileName)
{
    m_Shader.SetShaderFileName(fileName);
}

void Material::Enable() {
    m_Shader.Bind();
    for (const auto text : m_Textures) {
        text->Enable();
    }

    UploadUniforms();
}

void Material::Disable() {
    m_Shader.Unbind();
    for (const auto text : m_Textures) {
        text->Disable();
    }
}

void Material::AddTexture(std::string fileName, std::string typeName) {
    m_Textures.push_back(std::make_shared<Texture>(fileName, m_Shader.ShaderId(), typeName, m_Textures.size()));
}

void Material::UpdateTransform(glm::mat4& model, glm::mat4& view, glm::mat4& proj) {
    m_Shader.UploadUniformMat4("u_Projection", proj);
    m_Shader.UploadUniformMat4("u_View", view);
    m_Shader.UploadUniformMat4("u_Model", model);
}

void Material::UploadUniforms() {
    if (m_Shader.isLoaded()) {
        auto ambient = m_LightService->GetAmbientLight();
        m_Shader.UploadUniformFloat3("uAmbientLight", ambient.strength * ambient.color);
    }
}

void Material::SetMaterialData(std::shared_ptr<MaterialData> matData) {
    AddTexture(matData->ambient_texname, "t_Ambient"); // Wall
    AddTexture(matData->diffuse_texname, "t_Diffuse"); // Farm
}

如您所见,当材质从我们在 Material::SetMaterialData 函数中渲染的 .obj 的 .mtl 文件中接收到材质数据时,我们将两个新的纹理对象添加到纹理列表中。我们传入要加载的文件名和 glsl 统一采样器的字符串标识符。

启用材质后,我们将启用着色器和每个纹理对象。

这是我的 Texture 类的 wip。

#include "pch.h"
#include "Texture.h"
#include <stb_image.h>

Texture::Texture(std::string fileName, uint32_t programId, std::string uniformId, uint16_t unitId)
{
    m_FileName = ASSET_FOLDER + fileName;
    unsigned char* texData = stbi_load(m_FileName.c_str(), &m_Width, &m_Height, &m_NrChannels, 0);

    m_ProgramId = programId;
    
    glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
    glGenTextures(1, &m_TextureId);

    m_TextureUnit = GL_TEXTURE0 + unitId;
    glActiveTexture(m_TextureUnit);
    glBindTexture(GL_TEXTURE_2D, m_TextureId);

    if (texData)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_Width, m_Height, 0, GL_RGB, GL_UNSIGNED_BYTE, texData);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        OGLR_CORE_ERROR("Failed to load texture");
        throw std::runtime_error("Failed to load texture: "+ m_FileName);
    }

    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_image_free(texData);
    Disable();
}

void Texture::Enable()
{
    glActiveTexture(m_TextureUnit); // activate the texture unit first before binding texture
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
}

void Texture::Disable()
{
    glBindTexture(GL_TEXTURE_2D, 0);
}

所以,我要做的第一件事是从着色器中获取采样器制服的 ID,并将该样本绑定到我正在寻找的纹理单元。然后我们生成该纹理,激活相同的单元并将我生成的纹理绑定到它。我猜这是我在这里的某个地方犯了错误,但我似乎无法弄清楚是怎么回事。

这是我目前使用的着色器。

// vertex
#version 330 core

layout (location = 0) in vec3 a_Position;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;

out vec3 outNormal;
out vec2 TexCoord;

uniform mat4 u_Projection;
uniform mat4 u_View;
uniform mat4 u_Model;

void main() {
    vec4 worldPosition = u_Model * vec4(a_Position,1.0);
    gl_Position = u_Projection * u_View * worldPosition;
    outNormal = aNormal;
    TexCoord = aTexCoord;
}

// fragment
#version 330 core

out vec4 color;

in vec3 outNormal;
in vec2 TexCoord;

uniform sampler2D t_Ambient;
uniform sampler2D t_Diffuse;

void main() {
    if (TexCoord.x > 0.50)
    {
        //color = vec4(TexCoord.x, TexCoord.y, 0.0, 1.0);
        color = texture(t_Diffuse, TexCoord);
    }
    else
    {
        color = texture(t_Ambient, TexCoord);
    }
}

我希望三角形的每一半都有不同的纹理,但由于某种原因,两个采样器似乎都获得了相同的纹理。如果我在碎片着色器中使用该颜色而不是纹理,我会得到一半纹理和一半颜色,所以它......至少可以工作......

我注意到我认为奇怪的另一件事是渲染的纹理似乎总是我添加的第一个纹理。如果我在 Material::SetMaterialData 中翻转 AddTexture 调用的顺序,则会出现另一个纹理。也许有人可以向我解释为什么这很明显,但我原以为如果我以某种方式弄错了纹理的绑定,那将是第二个覆盖第一个但是嘿¯_(ツ)_/¯ 我是准备好接受这方面的教育。

编辑

我很抱歉,但显然不清楚着色器是否正确绑定。

在 Shape::Draw 函数的开头我们调用m_Material.Enable();

开头调用m_Shader.Bind();,然后调用glUseProgram(m_ProgramId);

这发生在任何纹理创建流程之前,因此在我们设置制服之前正确绑定了着色器。

对任何混淆表示歉意。

【问题讨论】:

  • 不,着色器未绑定。调用Material::AddTexture!之前必须绑定着色器

标签: c++ opengl glsl shader


【解决方案1】:

glUniform1i 仅为当前启用的着色器绑定统一:

glUniform 对通过调用 glUseProgram 成为当前状态一部分的程序对象进行操作。

似乎您没有在glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId); 之前调用glUseProgram(如果没有SetMaterialData 的调用者代码,我不能肯定地说),并且制服实际上并未绑定到着色器的unitId

所以试试这个:

glUseProgram(programId);
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);

【讨论】:

  • 啊,我应该澄清一下(并将添加对问题的编辑)。我很抱歉。是的,着色器是绑定的。 Material::Enable()开头的m_Shader.Bind()调用调用glUseProgram
  • 啊,这最后是对的!虽然我在每次绘制之前都绑定了着色器,但在纹理加载流之前我没有绑定,并且不知何故反复忽略了这一点。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多