OpenGL/C++:将多个纹理传递给一个着色器时出现问题
OpenGL/C++: Problem passing multiple textures to one shader
我按照 https://learnopengl.com/Lighting/Lighting-maps 上的教程进行操作,但在使用多个纹理(一个用于镜面反射,一个用于漫反射照明)时遇到了一些奇怪的错误。当只使用一个纹理时一切正常,但在使用两个纹理后,立方体要么只渲染黑色,要么只应用镜面反射纹理。
我在 Whosebug 上搜索了所有类似的问题并通读了 OpenGL 文档,但仍然无法弄清楚为什么第二个纹理不起作用。
这是我的 main.cpp 的渲染循环部分,它创建了应该用两个纹理渲染的立方体。着色器和纹理都可以正常加载和编译。
// Load textures
Texture groundTexture = Texture("GROUND", TEXTURE_DIR+"png/container2.png");
Texture groundSpecular = Texture("SPECULAR", TEXTURE_DIR+"png/container2_specular.png");
// Load shaders
Shader textureShader = Shader("SIMPLE_TEXTURE", SHADER_DIR+"vertex/texture_lighted.vs", SHADER_DIR+"fragment/texture_lighted.fs");
Shader lampShader = Shader("WHITE", SHADER_DIR+"vertex/basic.vs", SHADER_DIR+"fragment/white.fs");
// projection matrix
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
// shader configuration
// --------------------
textureShader.use();
// Get the uniform variables location.
GLuint diffLocation = glGetUniformLocation(textureShader.ID, "material.diffuse");
GLuint specLocation = glGetUniformLocation(textureShader.ID, "material.specular");
另一个奇怪的事情是,如果我将 0 和 1 更改为 groundTexture.ID 和 groundSpecular.ID,至少镜面反射部分会渲染,即使 glUniform1i 期望这里的单位编号而不是纹理- ID。
// Then bind the uniform samplers to texture units:
glUseProgram(textureShader.ID);
glUniform1i(diffLocation, 0);
glUniform1i(specLocation, 1);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// Calc time
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// be sure to activate shader when setting uniforms/drawing objects
textureShader.use();
textureShader.setVec3("light.position", lightPos);
textureShader.setVec3("viewPos", camera.Position);
// light properties
textureShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
textureShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
textureShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
// material properties
textureShader.setFloat("material.shininess", 64.0f);
// view/projection transformations
glm::mat4 view = camera.getViewMatrix();
textureShader.setMat4("projection", projection);
textureShader.setMat4("view", view);
// world transformation
glm::mat4 model = glm::mat4(1.0f);
textureShader.setMat4("model", model);
// bind texture-maps
glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, groundTexture.ID);
glActiveTexture(GL_TEXTURE0 + 1); // Texture unit 1
glBindTexture(GL_TEXTURE_2D, groundSpecular.ID);
glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
// render the cube
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
为了完整起见,这也是我的着色器-class 和纹理-class:
#include "shader.h"
#include <stdlib.h>
#include <string.h>
#include <glad/glad.h>
#include <iostream>
#include <glm/ext.hpp>
#include <fstream>
#include <sstream>
/*
* Creates a new Shader.
* name = name of the shader
* vShader = vertex shader
* fShader = fragment shader
*/
Shader::Shader(std::string name, std::string vShaderPath, std::string fShaderPath) {
this->name = name;
this->vShaderPath = vShaderPath;
this->fShaderPath = fShaderPath;
compileAndLink();
}
Shader::Shader(const Shader& s) {
this->name = s.name;
this->vShaderPath = s.vShaderPath;
this->fShaderPath = s.fShaderPath;
compileAndLink();
}
void Shader::compileAndLink() {
std::string vShader, fShader;
std::ifstream v_inFile, f_inFile;
std::stringstream v_strStream, f_strStream;
// Read File for vertexShader
v_inFile.open(vShaderPath);
if (!v_inFile.is_open()) {
std::cout << "ERROR LOADING VERTEX SHADER FILE: " << vShaderPath << std::endl;
} else {
v_strStream << v_inFile.rdbuf();
vShader = v_strStream.str();
}
// Read File for fragmentShader
f_inFile.open(fShaderPath);
if (!f_inFile.is_open()) {
std::cout << "ERROR LOADING FRAGMENT SHADER FILE: " << fShaderPath << std::endl;
} else {
f_strStream << f_inFile.rdbuf();
fShader = f_strStream.str();
}
// Compile and link
const char* vertex_shader = vShader.c_str();
const char* fragment_shader = fShader.c_str();
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// store reference in ID
ID = glCreateProgram();
// link shaders
glAttachShader(ID, vertexShader);
glAttachShader(ID, fragmentShader);
glLinkProgram(ID);
// check for linking errors
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// clean up
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
/*
* Tell OpenGL to use this shader-program.
*/
void Shader::use() {
glUseProgram(ID);
}
和
#include <glad/glad.h>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "../image-loader/stb_image.h"
#include "texture.h"
Texture::Texture(std::string name, std::string path) {
this->name = name;
this->path = path;
createTexture();
}
Texture::Texture(const Texture& t) {
this->name = t.name;
this->path = t.path;
createTexture();
}
void Texture::createTexture() {
unsigned int texture;
glGenTextures(1, &texture); // generate one texture
// Load file
int width, height, nrChannels;
unsigned char *data = stbi_load(this->path.c_str(), &width, &height, &nrChannels, 0);
if (data) {
GLenum format;
if (nrChannels == 1)
format = GL_RED;
else if (nrChannels == 3)
format = GL_RGB;
else if (nrChannels == 4)
format = GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
// Set Texture Parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // x
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // y
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data); // clean up
} else {
std::cout << "Failed to load texture" << std::endl;
}
this->ID = texture;
}
void Texture::use() {
glBindTexture(GL_TEXTURE_2D, this->ID);
}
我正在使用 OpenGL 4.4,初始化如下,很高兴加载 OpenGL 函数指针。
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
提前致谢。
编辑:
这是两个着色器(显然很重要):
texture_lighted.vs(顶点着色器)
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
TexCoords = aTexCoords;
// transpose normal vectors with normal matrix of model matrix
Normal = mat3(transpose(inverse(model))) * aNormal;
}
和texture_lighted.fs(片段着色器)
#version 330 core
out vec4 FragColor;
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;
void main()
{
// ambient
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
贴图的名称(值)由glGenTexture
, the named texture has to be bound to a texturing target by glBindTexture
, before the texture image is specified by glTexImage2D
生成后。
glTexImage2D
为当前绑定到指定目标的纹理定义纹理图像:
glGenTextures(1, &texture);
// [...]
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
我按照 https://learnopengl.com/Lighting/Lighting-maps 上的教程进行操作,但在使用多个纹理(一个用于镜面反射,一个用于漫反射照明)时遇到了一些奇怪的错误。当只使用一个纹理时一切正常,但在使用两个纹理后,立方体要么只渲染黑色,要么只应用镜面反射纹理。
我在 Whosebug 上搜索了所有类似的问题并通读了 OpenGL 文档,但仍然无法弄清楚为什么第二个纹理不起作用。
这是我的 main.cpp 的渲染循环部分,它创建了应该用两个纹理渲染的立方体。着色器和纹理都可以正常加载和编译。
// Load textures
Texture groundTexture = Texture("GROUND", TEXTURE_DIR+"png/container2.png");
Texture groundSpecular = Texture("SPECULAR", TEXTURE_DIR+"png/container2_specular.png");
// Load shaders
Shader textureShader = Shader("SIMPLE_TEXTURE", SHADER_DIR+"vertex/texture_lighted.vs", SHADER_DIR+"fragment/texture_lighted.fs");
Shader lampShader = Shader("WHITE", SHADER_DIR+"vertex/basic.vs", SHADER_DIR+"fragment/white.fs");
// projection matrix
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
// shader configuration
// --------------------
textureShader.use();
// Get the uniform variables location.
GLuint diffLocation = glGetUniformLocation(textureShader.ID, "material.diffuse");
GLuint specLocation = glGetUniformLocation(textureShader.ID, "material.specular");
另一个奇怪的事情是,如果我将 0 和 1 更改为 groundTexture.ID 和 groundSpecular.ID,至少镜面反射部分会渲染,即使 glUniform1i 期望这里的单位编号而不是纹理- ID。
// Then bind the uniform samplers to texture units:
glUseProgram(textureShader.ID);
glUniform1i(diffLocation, 0);
glUniform1i(specLocation, 1);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// Calc time
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// be sure to activate shader when setting uniforms/drawing objects
textureShader.use();
textureShader.setVec3("light.position", lightPos);
textureShader.setVec3("viewPos", camera.Position);
// light properties
textureShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
textureShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
textureShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
// material properties
textureShader.setFloat("material.shininess", 64.0f);
// view/projection transformations
glm::mat4 view = camera.getViewMatrix();
textureShader.setMat4("projection", projection);
textureShader.setMat4("view", view);
// world transformation
glm::mat4 model = glm::mat4(1.0f);
textureShader.setMat4("model", model);
// bind texture-maps
glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, groundTexture.ID);
glActiveTexture(GL_TEXTURE0 + 1); // Texture unit 1
glBindTexture(GL_TEXTURE_2D, groundSpecular.ID);
glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
// render the cube
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
为了完整起见,这也是我的着色器-class 和纹理-class:
#include "shader.h"
#include <stdlib.h>
#include <string.h>
#include <glad/glad.h>
#include <iostream>
#include <glm/ext.hpp>
#include <fstream>
#include <sstream>
/*
* Creates a new Shader.
* name = name of the shader
* vShader = vertex shader
* fShader = fragment shader
*/
Shader::Shader(std::string name, std::string vShaderPath, std::string fShaderPath) {
this->name = name;
this->vShaderPath = vShaderPath;
this->fShaderPath = fShaderPath;
compileAndLink();
}
Shader::Shader(const Shader& s) {
this->name = s.name;
this->vShaderPath = s.vShaderPath;
this->fShaderPath = s.fShaderPath;
compileAndLink();
}
void Shader::compileAndLink() {
std::string vShader, fShader;
std::ifstream v_inFile, f_inFile;
std::stringstream v_strStream, f_strStream;
// Read File for vertexShader
v_inFile.open(vShaderPath);
if (!v_inFile.is_open()) {
std::cout << "ERROR LOADING VERTEX SHADER FILE: " << vShaderPath << std::endl;
} else {
v_strStream << v_inFile.rdbuf();
vShader = v_strStream.str();
}
// Read File for fragmentShader
f_inFile.open(fShaderPath);
if (!f_inFile.is_open()) {
std::cout << "ERROR LOADING FRAGMENT SHADER FILE: " << fShaderPath << std::endl;
} else {
f_strStream << f_inFile.rdbuf();
fShader = f_strStream.str();
}
// Compile and link
const char* vertex_shader = vShader.c_str();
const char* fragment_shader = fShader.c_str();
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// store reference in ID
ID = glCreateProgram();
// link shaders
glAttachShader(ID, vertexShader);
glAttachShader(ID, fragmentShader);
glLinkProgram(ID);
// check for linking errors
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// clean up
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
/*
* Tell OpenGL to use this shader-program.
*/
void Shader::use() {
glUseProgram(ID);
}
和
#include <glad/glad.h>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "../image-loader/stb_image.h"
#include "texture.h"
Texture::Texture(std::string name, std::string path) {
this->name = name;
this->path = path;
createTexture();
}
Texture::Texture(const Texture& t) {
this->name = t.name;
this->path = t.path;
createTexture();
}
void Texture::createTexture() {
unsigned int texture;
glGenTextures(1, &texture); // generate one texture
// Load file
int width, height, nrChannels;
unsigned char *data = stbi_load(this->path.c_str(), &width, &height, &nrChannels, 0);
if (data) {
GLenum format;
if (nrChannels == 1)
format = GL_RED;
else if (nrChannels == 3)
format = GL_RGB;
else if (nrChannels == 4)
format = GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
// Set Texture Parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // x
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // y
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data); // clean up
} else {
std::cout << "Failed to load texture" << std::endl;
}
this->ID = texture;
}
void Texture::use() {
glBindTexture(GL_TEXTURE_2D, this->ID);
}
我正在使用 OpenGL 4.4,初始化如下,很高兴加载 OpenGL 函数指针。
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
提前致谢。
编辑: 这是两个着色器(显然很重要): texture_lighted.vs(顶点着色器)
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
TexCoords = aTexCoords;
// transpose normal vectors with normal matrix of model matrix
Normal = mat3(transpose(inverse(model))) * aNormal;
}
和texture_lighted.fs(片段着色器)
#version 330 core
out vec4 FragColor;
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;
void main()
{
// ambient
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
贴图的名称(值)由glGenTexture
, the named texture has to be bound to a texturing target by glBindTexture
, before the texture image is specified by glTexImage2D
生成后。
glTexImage2D
为当前绑定到指定目标的纹理定义纹理图像:
glGenTextures(1, &texture);
// [...]
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);