简体   繁体   English

在 GLSL 着色器中使用多个纹理时纹理被覆盖

[英]Texture gets over written when using multiple textures in GLSL shader

I am working on sending multiple textures to a single shader and am having a weird issue where both samplers in the shader seem to get the same texture data.我正在努力将多个纹理发送到单个着色器,但遇到一个奇怪的问题,即着色器中的两个采样器似乎都获得了相同的纹理数据。 I know there are a lot of other multiple texture questions and answers out there ( Here are a few I've read multiple times already 1 , 2 , 3 ) but some bug is eluding me and I'm starting to lose my marbles.我知道还有很多其他的多纹理问题和答案(这里有一些我已经多次阅读过123 )但是一些错误正在躲避我,我开始失去理智。 I am fairly confident I have everything setup correctly but obviously there is still some issue.我相当有信心我已经正确设置了所有内容,但显然仍然存在一些问题。

So, currently I have Shape, Material, Texture, and Shader classes.所以,目前我有 Shape、Material、Texture 和 Shader 类。 My shape class is the parent that performs the actual draw.我的形状 class 是执行实际绘制的父级。 It has a material member which has a shader and an array of textures.它有一个材质成员,它有一个着色器和一组纹理。 The material class draw looks like this:材料 class 绘制如下所示:

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

Here is my whole material class:这是我的全部资料 class:

#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
}

As you can see, when the material receives the material data from the.mtl file of the.obj we are rendering in the Material::SetMaterialData function, we are adding two new texture objects to the list of textures.可以看到,当材质从我们在Material::SetMaterialData function 中渲染的.obj 的.mtl 文件中接收到材质数据时,我们正在向纹理列表中添加两个新的纹理对象。 We are passing in the filename to be loaded and the string identifier of the glsl uniform sampler.我们传入要加载的文件名和 glsl 统一采样器的字符串标识符。

When the material is enabled, we are enabling the shader and each of the texture objects.启用材质后,我们将启用着色器和每个纹理对象。

Here is the wip of my Texture class.这是我的纹理 class 的 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);
}

So, the first thing I do is grab the ID of the sampler uniform from the shader and bind that sample to the texture unit I'm looking for.因此,我做的第一件事是从着色器中获取采样器制服的 ID,并将该样本绑定到我正在寻找的纹理单元。 We then generate that texture, activate the same unit and bind my generated texture to it.然后我们生成该纹理,激活相同的单元并将我生成的纹理绑定到它。 I'm guessing that it is somewhere in here that I have blundered but I can't seem to figure out how.我猜这是我在这里的某个地方犯了错误,但我似乎无法弄清楚如何。

Here are my shaders as they currently stand.这是我目前的着色器。

// 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);
    }
}

I am expecting each half of my triangle to have different textures but for some reason both samplers seem to get the same texture.我希望我的三角形的每一半都有不同的纹理,但出于某种原因,两个采样器似乎都获得了相同的纹理。 If I use that color in the frag shader instead of the texture I get half texture and half the color so it... that at least works...如果我在碎片着色器中使用该颜色而不是纹理,我会得到一半的纹理和一半的颜色,所以它......至少有效......

在此处输入图像描述

The other thing that I noticed that I thought was weird was that the texture that gets rendered seems to always be the first one I add.我注意到的另一件我认为很奇怪的事情是,渲染的纹理似乎总是我添加的第一个纹理。 If I flip the order of the AddTexture calls in Material::SetMaterialData the other texture appears.如果我翻转 Material::SetMaterialData 中 AddTexture 调用的顺序,则会出现另一个纹理。 Maybe someone can explain to my why that would be obvious but I would have expected that if I had somehow goofed the binding of my textures that it would be the second one overwriting the first but hey ¯_(ツ)_/¯ I'm ready to be educated on that one.也许有人可以向我解释为什么这很明显,但我本以为如果我以某种方式搞砸了我的纹理绑定,那将是第二个覆盖第一个但是嘿¯_(ツ)_/¯ 我是准备接受这方面的教育。

Edit编辑

I apologize but apparently it was not clear that the shader is being properly bound.我很抱歉,但显然不清楚着色器是否已正确绑定。

At the beginning of the Shape::Draw function we are calling m_Material.Enable();在 Shape::Draw function 的开头我们调用m_Material.Enable();

The beginning of which calls m_Shader.Bind();其中开头调用m_Shader.Bind(); which in turn calls glUseProgram(m_ProgramId);依次调用glUseProgram(m_ProgramId);

This occurs before any of the texture creation flow so the shader is properly bound before we are setting the uniforms.这发生在任何纹理创建流程之前,因此在我们设置制服之前正确绑定着色器。

Apologies for any confusion.对任何混淆表示歉意。

glUniform1i binds an uniform only for the currently enabled shader: glUniform1i仅为当前启用的着色器绑定制服:

glUniform operates on the program object that was made part of current state by calling glUseProgram. glUniform 在程序 object 上运行,该程序通过调用 glUseProgram 成为当前 state 的一部分。

Seems like you don't call glUseProgram before glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);好像你没有在glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);之前调用glUseProgram (I can't say for sure without caller code of the SetMaterialData ) and the uniform is not actually binded to the unitId for the shader. (如果没有SetMaterialData的调用者代码,我不能肯定地说)并且制服实际上并未绑定到着色器的unitId

So try this:所以试试这个:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM