在 GLSL 着色器中使用多个纹理时纹理被覆盖
Texture gets over written when using multiple textures in GLSL shader
我正在努力将多个纹理发送到单个着色器,但遇到一个奇怪的问题,即着色器中的两个采样器似乎都获得了相同的纹理数据。我知道还有很多其他的多纹理问题和答案(这里有一些我已经读过很多遍了 1, 2, )但是一些错误正在躲避我和我开始失去理智了。我相当有信心我已正确设置所有内容,但显然仍然存在一些问题。
所以,目前我有形状、Material、纹理和着色器 classes。我的形状 class 是执行实际绘制的父级。它有一个 material 成员,其中有一个着色器和一个纹理数组。 material 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();
}
这是我的全部 material 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
}
可以看到,当material从我们正在渲染的.obj的.mtl文件中接收到material数据时,我们在Material::SetMaterialData函数中添加了两个新纹理对象到纹理列表。我们传入要加载的文件名和 glsl 统一采样器的字符串标识符。
启用 material 后,我们将启用着色器和每个纹理对象。
这是我的纹理 class。
#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);
这发生在任何纹理创建流程之前,因此在我们设置制服之前正确绑定了着色器。
如有任何混淆,我们深表歉意。
glUniform1i 仅为当前启用的着色器绑定制服:
glUniform operates on the program object that was made part of current state by calling glUseProgram.
好像你没有在 glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
之前调用 glUseProgram
(如果没有 SetMaterialData
的调用者代码,我不能肯定地说)并且制服实际上没有绑定到着色器的 unitId
。
所以试试这个:
glUseProgram(programId);
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);
我正在努力将多个纹理发送到单个着色器,但遇到一个奇怪的问题,即着色器中的两个采样器似乎都获得了相同的纹理数据。我知道还有很多其他的多纹理问题和答案(这里有一些我已经读过很多遍了 1, 2,
所以,目前我有形状、Material、纹理和着色器 classes。我的形状 class 是执行实际绘制的父级。它有一个 material 成员,其中有一个着色器和一个纹理数组。 material 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();
}
这是我的全部 material 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
}
可以看到,当material从我们正在渲染的.obj的.mtl文件中接收到material数据时,我们在Material::SetMaterialData函数中添加了两个新纹理对象到纹理列表。我们传入要加载的文件名和 glsl 统一采样器的字符串标识符。
启用 material 后,我们将启用着色器和每个纹理对象。
这是我的纹理 class。
#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);
这发生在任何纹理创建流程之前,因此在我们设置制服之前正确绑定了着色器。
如有任何混淆,我们深表歉意。
glUniform1i 仅为当前启用的着色器绑定制服:
glUniform operates on the program object that was made part of current state by calling glUseProgram.
好像你没有在 glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
之前调用 glUseProgram
(如果没有 SetMaterialData
的调用者代码,我不能肯定地说)并且制服实际上没有绑定到着色器的 unitId
。
所以试试这个:
glUseProgram(programId);
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);