当前位置: 首页 > 知识库问答 >
问题:

在GLSL着色器中使用多个纹理时,纹理会被改写

何哲
2023-03-14

我正在努力发送多个纹理到一个单一的着色器,并有一个奇怪的问题,在着色器中的两个采样器似乎得到相同的纹理数据。我知道还有很多其他的多纹理问题和答案(这里有一些我已经读过很多次了1,2,3),但一些bug正在逃避我,我开始失去我的弹珠。我很有信心我已经把一切都安排好了,但显然还是有一些问题。

所以,目前我有形状,材料,纹理,和着色器类。我的shape类是执行实际绘制的父类。它有一个具有着色器和纹理数组的材料成员。material类绘制如下所示:

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从我们在material::SetMaterialData函数中呈现的。obj的。mtl文件中接收到material数据时,我们将向纹理列表中添加两个新的纹理对象。我们正在传入要加载的文件名和glsl统一采样器的字符串标识符。

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

这是我的纹理类的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);
    }
}

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

我注意到的另一件事是,我觉得奇怪的是,得到渲染的纹理似乎总是我添加的第一个。如果我在Material::SetMaterialData中翻转AddTexture调用的顺序,另一个纹理就会出现。也许有人可以向我解释为什么这是显而易见的,但我本以为,如果我在绑定纹理时搞砸了,那么它将是第二个覆盖第一个的纹理,但嘿()_/'我已经准备好接受这方面的教育了。

编辑

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

在shape::draw函数的开头,我们调用m_material.enable();

开头调用m_shader.bind();,然后调用gluseprogramm(m_ProgramId);

很抱歉有任何混淆。

共有1个答案

颜经艺
2023-03-14

glUniform1i仅为当前启用的着色器绑定制服:

glUniform对通过调用GluseProgram而成为当前状态一部分的program对象进行操作。

gluniform1i(glGetUniformLocation(programId,uniformid.c_str()),unitId)之前,似乎没有调用gluseprogram(如果没有setMaterialData)的调用者代码,我就不能确定了),而且该uniform实际上并没有绑定到着色器的unitId

所以试试这个:

glUseProgram(programId);
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);
 类似资料:
  • 如何在平面上滚动纹理?所以我有一个平面有一个纹理,我可以使用一个着色器滚动左从右(无限)的纹理上?

  • 我希望能够在GLSL片段着色器中组合两种纹理。我目前正在使用PyOpenGL,到目前为止我使用着色器所做的一切都很好。 当我尝试从片段着色器访问多个纹理时遇到困难,例如,以下着色器显示正确的纹理减去蓝色像素: 但是 导致空白屏幕。 我有一种感觉,问题可能在于我如何将纹理制服传递给着色器,但我一辈子都无法弄清楚为什么第一个纹理有效,而第二个纹理无效。下面是完整的程序。

  • 我在Mac OSX下工作,试图通过GLSL着色器在立方体上映射图像。 我显示立方体(以及图像,当它不通过着色器时)的方法是: 正如你所看到的,我正在检查,在这个测试中,是否有一个着色器应用到我的对象上(它是一个包装,效果非常好) 如果没有着色器,我只启用GL_纹理_坐标_数组,如果有,我尝试将图像绑定到着色器中的采样2D均匀。 我使用的着色器非常简单:它只显示纹理。我在Quartz Compose

  • 在一个教程之后,我将尝试使用FreeType在OpenGL中渲染文本。因此,灰度8位图像用作每个字符的纹理,使图像的每个字节对应于纹理的红色分量。 为了以其他颜色呈现文本,建议您使用着色器。但是,当使用提供的着色器时,我看到的不是彩色字母,而是彩色框,好像根本没有纹理。 以下是没有着色器时的效果: 这是它在着色器中的外观: (盒子的位置也会发生变化) 以下是顶点着色器代码: 这是片段着色器代码:

  • 目前,我有问题与传递多个纹理到一个glsl着色器在iOS。 在渲染中,想要的纹理被绑定到制服上 我在这里一直使用GL_TEXTURE0和GL_TEXTURE1,因为当纹理(实际上是来自iPhone摄像头的luma和Chroma纹理,最终用于计算相应的rgb值)被创建时,这些textureLots被使用。 我使用的fragmentshader非常简单,它只是用给定的纹理对一个简单的屏幕填充四方进行纹

  • 我是OpenGL的新手,在整理如何将纹理和着色器绑定到VBOs时遇到了困难。 我正在使用Cinder的纹理和着色器类。以下是我绘制方法的一部分: 在上面的代码中,如果我注释掉对mShader的调用。bind(),我的球体VBO将显示纹理(myImage)。我的着色器适用于普通(无纹理)形状,但当我在绘制任何带有包裹纹理的形状之前绑定着色器时,它会阻止纹理显示。 这是我使用的着色器的问题,还是我不理