I followed the tutorial on https://learnopengl.com/Lighting/Lighting-maps and got some weird errors when using multiple textures (one for specular and one for diffuse lighting). When using only one Texture everything works fine but after using two textures, the cube renders either only black or only the specular texture is applied.
I searched through all similar questions on stackoverflow and read through the OpenGL documentation but still can't figure out why the second texture is not working.
This is the render-loop part of my main.cpp that is creating the cube that is supposed to be rendered with the two textures. The shaders and textures all load and compile fine.
// 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");
Another weird thing is that if I change the 0 and 1 to groundTexture.ID and groundSpecular.ID, at least the specular part renders, even though glUniform1i expects the unit numbers here and not the Texture-IDs.
// 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);
For completeness sake, here's also my shader-class and texture-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);
}
and
#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);
}
I am using OpenGL 4.4, initialized like below and glad to load the OpenGL function pointers.
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
Thanks in advance.
EDIT: Here are the two shaders (obviously important): texture_lighted.vs (vertex shader)
#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;
}
And texture_lighted.fs (fragment shader)
#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);
}