I am having trouble sending two textures to my fragment shader via uniform sampler2Ds. My fragment shader has two uniforms but it appears that my code sends the same image to both samplers. The texture bound first seems to be automatically bound to both uniforms.
The full C++ code is below:
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SOIL/SOIL.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, const char* argv[])
{
GLFWwindow* display;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
display = glfwCreateWindow(1280, 720, "Game Engine", nullptr, nullptr);
glfwMakeContextCurrent(display);
glewExperimental = GL_TRUE;
GLenum success = glewInit();
GLint height, width;
glfwGetFramebufferSize(display, &width, &height);
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
GLuint program;
std::string vertexCode, fragmentCode;
std::ifstream vShaderFile, fShaderFile;
std::string vertexPath = "Basic.vert";
std::string fragmentPath = "Basic.frag";
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
vShaderFile.close();
fShaderFile.close();
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch(std::ifstream::failure error)
{
std::cout << "Error: Shader file not successfully read." << std::endl;
}
GLuint vertex, fragment;
GLchar infoLog[512];
const GLchar* vertShaderCode = vertexCode.c_str();
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertShaderCode, NULL);
glCompileShader(vertex);
const GLchar* fragShaderCode = fragmentCode.c_str();
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragShaderCode, NULL);
glCompileShader(fragment);
program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
glDeleteShader(vertex);
glDeleteShader(fragment);
GLfloat vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
};
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
(GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
GLuint texture0, texture1;
glGenTextures(1, &texture0);
glBindTexture(GL_TEXTURE_2D, texture0);
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);
unsigned char* image = SOIL_load_image("container.jpg",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
GLint uniform = glGetUniformLocation(program, "texture0");
glUniform1i(uniform, 0);
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
image = SOIL_load_image("awesomeface.png",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
uniform = glGetUniformLocation(program, "texture1");
glUniform1i(uniform, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
while (!glfwWindowShouldClose(display))
{
glClearColor(0.761f, 0.698f, 0.502f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(display);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
return EXIT_SUCCESS;
}
The associated GLSL shader files are below:
The vertex shader:
#version 400 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(position, 1.0f);
TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}
The fragment shader:
#version 400 core
in vec2 TexCoord;
out vec4 fragColor;
uniform sampler2D texture0;
uniform sampler2D texture1;
void main()
{
vec4 color1 = texture(texture0, TexCoord);
vec4 color2 = texture(texture1, TexCoord);
fragColor = mix(color1, color2, 0.4f);
}
glUniform1i(uniform, 0);
This function acts on the program that is currently in use. You don't call glUseProgram until the render loop. So it should give you a GL_INVALID_OPERATION error and therefore does nothing.
You should either use glProgramUniform from GL 4.1+ (which takes the program it acts on) or call glUseProgram before setting the uniform value.
In this section of your code:
GLuint texture0, texture1;
glGenTextures(1, &texture0);
glBindTexture(GL_TEXTURE_2D, texture0);
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);
unsigned char* image = SOIL_load_image("container.jpg",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
//GLint uniform = glGetUniformLocation(program, "texture0");
//glUniform1i(uniform, 0);
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
image = SOIL_load_image("awesomeface.png",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
//uniform = glGetUniformLocation(program, "texture1");
//glUniform1i(uniform, 1);
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, texture0);
//glActiveTexture(GL_TEXTURE1);
//glBindTexture(GL_TEXTURE_2D, texture1);
Remove the commented lines from the section above. Just load the images first;
don't worry about the uniforms yet and don't bind them here. You will need to active your shader/program first before setting any uniforms.
This section should now look like this:
GLuint texture0, texture1;
// Setup Texture1
glGenTextures(1, &texture0);
glBindTexture(GL_TEXTURE_2D, texture0);
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);
unsigned char* image = SOIL_load_image("container.jpg",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
// Setup Texture 2
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
image = SOIL_load_image("awesomeface.png",
&width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
Now after you have setup your two textures and after you have freed the resources from SOIL what you will need to do next is to use or activate your shader-program.
glUseProgram( program );// Need to activate your shader before setting any uniforms
Then after that you can acquire your uniforms before your render loop:
// After activating the shader set your uniforms here before your
// render loop. You don't want to do this on every iteration.
glUniform1i( glGetUniformLocation( program, "texture1" ), 0 );
glUniform1i( glGetUniformLocation( program, "texture2" ), 1 );
Then within the render loop after you handle or process any time & input information and after you set the clear color and clear the color buffer is where and when you want to bind your textures.
while ( !glfwWindowShouldClose( display ) ) {
// time
...
// input
...
// color buffer
...
// Bind textures
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, texture1 );
glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, texture2 );
// now render the container using the shader
glUseProgram( program );
glBindVertexArray( VAO );
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0 );
// Swap buffers
} // end loop
Also as a side note you can do this in your fragement shader:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;
void main() {
// linearly interpolate between both textures
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.4);
}
This is basically the same as what you have above but is written in a single line of code and it doesn't resort to using 2 local variables. Remember fragment shaders are expensive because they run for each and every pixel or fragment for each iteration of your render loop. It looks more convoluted in what I've shown than yours but it is more efficient. Compliments of www.learnopengl.com.
Related
Any idea, why decoded YUV -> RGB(shader conversion) has that extra green edge on the right side?
Almost any 1080X1920 video seems to have this issue.
A screen recording of the issue is uploaded here https://imgur.com/a/JtUZq4h
Once I manually scale up the texture width, I can see it fills up to the viewport, but it would be nice if I could fix the actual cause. Is it some padding that's part of YUV colorspace? What else could it be?
My model is -1 to 1, filling the entire width
The texture coordinates are also 0 to 1 ratio
float vertices[] = {
-1.0, 1.0f, 0.0f, 0.0, // top left
1.0f, 1.0f, 1.0, 0.0, // top right
-1.0f, -1.0f, 0.0f, 1.0f, // bottom left
1.0f, -1.0f, 1.0f, 1.0f // bottom right
};
Fragment Shader
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
precision highp float;
uniform sampler2D textureY;
uniform sampler2D textureU;
uniform sampler2D textureV;
uniform float alpha;
uniform vec2 texScale;
void main()
{
float y = texture(textureY, TexCoord / texScale).r;
float u = texture(textureU, TexCoord / texScale).r - 0.5;
float v = texture(textureV, TexCoord / texScale).r - 0.5;
vec3 rgb;
//yuv - 709
rgb.r = clamp(y + (1.402 * v), 0, 255);
rgb.g = clamp(y - (0.2126 * 1.5748 / 0.7152) * u - (0.0722 * 1.8556 / 0.7152) * v, 0, 255);
rgb.b = clamp(y + (1.8556 * u), 0,255);
FragColor = vec4(rgb, 1.0);
}
Texture Class
class VideoTexture {
public:
VideoTexture(Decoder *dec) : decoder(dec) {
glGenTextures(1, &texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture1);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[0], decoder->frameQueue.first().height, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
glGenTextures(1, &texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[1], decoder->frameQueue.first().height / 2, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
glGenTextures(1, &texture3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture3);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[2], decoder->frameQueue.first().height / 2, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
}
void Render(Shader *shader, Gui *gui) {
if (decoder->frameQueue.isEmpty()) {
return;
}
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[0], decoder->frameQueue.first().height, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[0]);
glBindTexture(GL_TEXTURE_2D, texture1);
shader->setInt("textureY", 0);
glActiveTexture(GL_TEXTURE1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[1], decoder->frameQueue.first().height / 2, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[1]);
glBindTexture(GL_TEXTURE_2D, texture2);
shader->setInt("textureU", 1);
glActiveTexture(GL_TEXTURE2);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[2], decoder->frameQueue.first().height / 2, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[2]);
glBindTexture(GL_TEXTURE_2D, texture3);
shader->setInt("textureV", 2);
}
~VideoTexture() {
printf("\nVideo texture destructor");
glDeleteTextures(1, &texture1);
glDeleteTextures(1, &texture2);
glDeleteTextures(1, &texture3);
}
private:
GLuint texture1;
GLuint texture2;
GLuint texture3;
GLint internalFormat = GL_RG8;
GLint format = GL_RED;
int currentFrame = 0;
Decoder *decoder;
}
I'm trying to create an opengl program that creates a 2d square, and applies 2 textures on it.
I followed this tutorial: https://learnopengl.com/Getting-started/Textures
This is my fragment shader:
#version 330 core
//in vec3 Color;
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D Texture1;
uniform sampler2D Texture2;
void main()
{
FragColor = mix(texture(Texture1, TexCoord), texture(Texture2, TexCoord), 0.5);
}
This is the code that sends the textures and the uniforms:
GLuint Tex1, Tex2;
int TexWidth, TexHeight, TexNrChannels;
unsigned char* TexData = stbi_load("container.jpg", &TexWidth, &TexHeight, &TexNrChannels, 0);
glGenTextures(1, &Tex1);
glBindTexture(GL_TEXTURE_2D, Tex1);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TexWidth, TexHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, TexData);
glGenerateMipmap(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(Program, "Texture1"), 0);
stbi_image_free(TexData);
TexData = stbi_load("awesomeface.png", &TexWidth, &TexHeight, &TexNrChannels, 0);
glGenTextures(1, &Tex2);
glBindTexture(GL_TEXTURE_2D, Tex2);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TexWidth, TexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, TexData);
glGenerateMipmap(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(Program, "Texture2"), 1);
stbi_image_free(TexData);
And this is the render loop:
while (!glfwWindowShouldClose(window))
{
processInput(window);
// GL render here
glClearColor(0.05f, 0.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, Tex2);
glUseProgram(Program);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
When I run it, only the first texture shows up on the square, and the a argument (last argument) of the mix function in the shader won't make a difference for any value.
I tried activating (glActiveTexture + GlBindTexture) the second texture first in the render loop, and it caused the second texture to be shown exclusively.
How can I make the textures mix together like in the tutorial?
If this approach is wrong, I would like to learn about another way to accomplish the same result.
glUniform1i set a value in the default uniform block of the currently installed program. You have to install the program with glUseProgram, before you can set the value of a uniform variable:
GLint t1_loc = glGetUniformLocation(Program, "Texture1");
GLint t2_loc = glGetUniformLocation(Program, "Texture2");
glUseProgram(Program);
glUniform1i(t1_loc, 0);
glUniform1i(t2_loc, 1);
Alternatively you can use glProgramUniform1i:
glProgramUniform1i(Program, t1_loc, 0);
glProgramUniform1i(Program, t2_loc, 1);
I'm trying to make a skybox that can change with a button.
My solution for this is to bind my skyboxes and load my textures in the following way:
//SkyBox
GLuint skyboxVBO, skyboxVAO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0);
// Load textures
vector<const GLchar*> facesN;
facesN.push_back("SkyBox/night/right.tga");
facesN.push_back("SkyBox/night/left.tga");
facesN.push_back("SkyBox/night/top.tga");
facesN.push_back("SkyBox/night/bottom.tga");
facesN.push_back("SkyBox/night/back.tga");
facesN.push_back("SkyBox/night/front.tga");
GLuint cubemapTextureN = TextureLoading::LoadCubemap(facesN);
vector<const GLchar*> faces;
faces.push_back("SkyBox/day/right.tga");
faces.push_back("SkyBox/day/left.tga");
faces.push_back("SkyBox/day/top.tga");
faces.push_back("SkyBox/day/bottom.tga");
faces.push_back("SkyBox/day/back.tga");
faces.push_back("SkyBox/day/front.tga");
GLuint cubemapTexture = TextureLoading::LoadCubemap(faces);
Then, in the program loop I draw the skybox with the following:
// Draw skybox as last
glDepthFunc(GL_LEQUAL); // Change depth function so depth test passes when values are equal to depth buffer's content
SkyBoxshader.Use();
view = glm::mat4(glm::mat3(camera.GetViewMatrix())); // Remove any translation component of the view matrix
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// skybox cube
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE1);
if (daytime == 1.0f) {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
}
else {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTextureN);
}
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // Set depth function back to default
In theory, whenever I press the button that changes the value of daytime, the skybox should change its textures to cubemapTextureN and, with another press of the button, change back to cubemapTexture. Instead, it just draws the texture that was loaded last, cubemapTexture in this case.
I already tested daytime and it does change, so it's not that.
Also, I tried loading cubemapTextureN after cubemapTexture and by doing so cubemapTextureN gets drawn instead, but still no change. I'm assuming somehow while loading the second textures, the first ones get updated, which shouldn't happen since they have different names, unless i'm understanding it wrong.
Easy solution would be to just make models of the entire skyboxes and work with that, but if possible I want to see if I can use this solution.
Edit:
These are the Shaders for the skybox.
Fragment shader:
#version 330 core
in vec3 TexCoords;
out vec4 color;
uniform samplerCube skybox;
void main()
{
color = texture(skybox, TexCoords);
}
Vertex shader:
#version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;
uniform mat4 projection;
uniform mat4 view;
void main()
{
vec4 pos = projection * view * vec4(position, 1.0);
gl_Position = pos.xyww;
TexCoords = position;
}
And their use:
Shader SkyBoxshader("Shaders/SkyBox.vs", "Shaders/SkyBox.frag");
Also, if this is usefull, this is the header with the definition of TextureLoading, which contains LoadCubemap
#pragma once
// GLEW
#include <GL/glew.h>
// Other Libs
#include "stb_image.h"
// Other includes
#include "Model.h"
#include <vector>
class TextureLoading
{
public:
static GLuint LoadTexture(GLchar *path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Failed to load texture" << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
static GLuint LoadCubemap(vector<const GLchar * > faces)
{
GLuint textureID;
glGenTextures(1, &textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++)
{
unsigned char *data = stbi_load(faces[i], &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
};
I wanted to use 4 textures in my fragment shader. One is a cubemap, one is a rendered texture on a framebuffer and the last two are created from images.
When I try calling them in my fragment shader, I keep getting a different one than the one I called or the same one no matter which one I call (depending on different iterations I did when I was trying to fix it).
This is how I got it
GLuint FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 1024, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
GLenum DrawBuffers[1]={GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
if ( glCheckFramebufferStatus ( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE )
{
glViewport(0, 0, 1024, 1024);
glClearColor( 0.4f, 0.4f, 0.4f, 1.0f );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
ngl::ShaderLib* shader = ngl::ShaderLib::instance();
(*shader)["Normal"]->use();
m_transform.setPosition(0.0f, 0.0f, 0.0f);
loadMatricesToShader();
//drawing the quad
prim->draw("tex");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0,0,1024,768);
// grab an instance of the shader manager
ngl::ShaderLib* shader = ngl::ShaderLib::instance();
( *shader )[ "PBR" ]->use();
glUniform1i(renderedTexture, 1);
//initialize environment map
initEnvironment();
initTexture(2, m_glossMapTex, "images/gloss.png");
glUniform1i(m_glossMapTex, 2);
initTexture(3, m_textMap, "images/alege.png");
glUniform1i(m_textMap, 3);
Also the initTexture and initEnvironment
void NGLScene::initTexture(const GLuint& texUnit, GLuint &texId, const char *filename) {
glActiveTexture(GL_TEXTURE0 + texUnit);
// Load up the image using NGL routine
ngl::Image img(filename);
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D (
GL_TEXTURE_2D,
0,
img.format(),
img.width(),
img.height(),
0,
GL_RGB,
GL_UNSIGNED_BYTE,
img.getPixels());
// Set up parameters for our texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
void NGLScene::initEnvironment() {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glActiveTexture (GL_TEXTURE0);
glGenTextures (1, &m_envTex);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_envTex);
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "images/sky_zneg.png");
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "images/sky_zpos.png");
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "images/sky_ypos.png");
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "images/sky_yneg.png");
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "images/sky_xneg.png");
initEnvironmentSide(GL_TEXTURE_CUBE_MAP_POSITIVE_X, "images/sky_xpos.png");
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_AUTO_GENERATE_MIPMAP, GL_TRUE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLfloat anisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
// Set our cube map texture to on the shader so we can use it
ngl::ShaderLib *shader=ngl::ShaderLib::instance();
shader->use("PBR");
shader->setUniform("envMap", 0);
}
void NGLScene::initEnvironmentSide(GLenum target, const char *filename) {
// Load up the image using NGL routine
ngl::Image img(filename);
glTexImage2D (
target,
0,
img.format(),
img.width(),
img.height(),
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
img.getPixels()
);
}
I feel like I have a gap in my knowledge of texture units and setting up uniforms, or maybe there is something wrong with my uniforms in my fragment shader.
In your code there is a misunderstanding, how glUniform1i has to be used. If a values is assigned to a uniform, the the uniform has to be identified by the uniform location index. See Uniform (GLSL)
The fist parameter of glUniform1i has to be the location of the uniform and not the named texture object.
The location of a uniform can be set explicit, in shader by a Layout Qualifier
e.g.
GLSL
layout(location = 7) uniform sampler2D u_gloss;
C++
initTexture(2, m_glossMapTex, "images/gloss.png");
glUniform1i(7, 2); // uniform location 7
If the location of the uniform is not set by a layout qualifier, then the uniform location is set automatically when the program is linked. You can ask for this location by glGetUniformLocation:
e.g.
GLSL
uniform sampler2D u_gloss;
C++
GLuint program_obj = ... ; // ( *shader )[ "PBR" ]->???
GLint gloss_location = glGetUniformLocation(program_obj , "u_gloss");
initTexture(2, m_glossMapTex, "images/gloss.png");
glUniform1i(gloss_location, 2);
I'm currently working in a deferred shading and I created a class which manages the FBOs and draw the buffers on the screen.
This is how it looks so far:
FBORender::FBORender(float screenWidth, float screenHeight) :
_screenWidth(screenWidth),
_screenHeight(screenHeight),
ProgramManager("defVertexShader.txt", "defFragShader.txt")
{
CreateProgram();
_vbo[0] = 0;
_vbo[1] = 0;
_vao = 0;
BuildQuad();
BuildVAO();
glGenFramebuffers(1, &_fbo);
glGenRenderbuffers(1, &_depthBuffer);
// Bind the depth buffer
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, (int)_screenWidth, (int)_screenHeight);
// Generate and bind the texture for diffuse
glGenTextures(1, &_diffuseBuffer);
glBindTexture(GL_TEXTURE_2D, _diffuseBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)_screenWidth, (int)_screenWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Generate and bind the texture for positions
glGenTextures(1, &_positionBuffer);
glBindTexture(GL_TEXTURE_2D, _positionBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, (int)_screenWidth, (int)_screenWidth, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Generate and bind the texture for normals
glGenTextures(1, &_normalBuffer);
glBindTexture(GL_TEXTURE_2D, _normalBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (int)_screenWidth, (int)_screenWidth, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Bind the FBO so that the next operations will be bound to it.
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
// Attach the textures to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _diffuseBuffer, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _positionBuffer, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, _normalBuffer, 0);
GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
{
printf("DeferredLighting::Init: FrameBuffer incomplete: 0x%x\n", fboStatus);
exit(1);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FBORender::~FBORender()
{
glDeleteTextures(1, &_normalBuffer);
glDeleteTextures(1, &_positionBuffer);
glDeleteTextures(1, &_diffuseBuffer);
glDeleteFramebuffers(1, &_fbo);
glDeleteVertexArrays(1, &_vao);
glDeleteBuffers(2, _vbo);
}
void FBORender::Start()
{
// Bind the FBO and set the viewport to the proper size
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glViewport(0, 0, (int)_screenWidth, (int)_screenWidth);
// Clear the render targets
GLenum windowBuffClear[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, windowBuffClear);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLenum windowBuffOpaque[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, windowBuffOpaque);
}
void FBORender::Draw(const glm::mat4 &Projection, const glm::mat4 &ModelView)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_ModelViewProjection = Projection * ModelView;
_ModelView = ModelView;
glDisable(GL_DEPTH_TEST);
Bind();
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _normalBuffer);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _positionBuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _diffuseBuffer);
glBindVertexArray(_vao);
LoadUniformVariables();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
UnBind();
}
void FBORender::Stop()
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
void FBORender::BuildQuad()
{
_coordinates[0] = 1.0f;
_coordinates[1] = 1.0f;
_coordinates[2] = 0.0f;
_coordinates[3] = -1.0f;
_coordinates[4] = 1.0f;
_coordinates[5] = 0.0f;
_coordinates[6] = 1.0;
_coordinates[7] = -1.0f;
_coordinates[8] = 0.0f;
_coordinates[9] = -1.0f;
_coordinates[10] = -1.0f;
_coordinates[11] = 0.0f;
_uv[0] = 1.0f;
_uv[1] = 1.0f;
_uv[2] = 0.0f;
_uv[3] = 1.0f;
_uv[4] = 1.0f;
_uv[5] = 0.0f;
_uv[6] = 0.0f;
_uv[7] = 0.0f;
}
void FBORender::BuildVAO()
{
// Generate and bind the vertex array object
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
// Generate and bind the vertex buffer object
glGenBuffers(2, _vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), _coordinates, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(float), _uv, GL_STATIC_DRAW);
LoadAttributeVariables();
glBindVertexArray(0);
}
void FBORender::LoadUniformVariables()
{
// OpenGL Matrices
GLuint ModelViewProjection_location = glGetUniformLocation(GetProgramID(), "mvMatrix");
glUniformMatrix4fv(ModelViewProjection_location, 1, GL_FALSE, glm::value_ptr(_ModelView));
// Texture buffers
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _diffuseBuffer);
GLint Diffuse_location = glGetUniformLocation(GetProgramID(), "tDiffuse");
glUniform1i(Diffuse_location, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _positionBuffer);
GLint Position_location = glGetUniformLocation(GetProgramID(), "tPosition");
glUniform1i(Position_location, 0);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _normalBuffer);
GLint Normal_location = glGetUniformLocation(GetProgramID(), "tNormals");
glUniform1i(Normal_location, 0);
}
void FBORender::LoadAttributeVariables()
{
// Vertex Attributes
GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
glEnableVertexAttribArray(VertexPosition_location);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[0]);
glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
GLuint TextureCoord_Location = glGetAttribLocation(GetProgramID(), "uvCoord");
printf("%d \n", TextureCoord_Location);
glEnableVertexAttribArray(TextureCoord_Location);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[1]);
glVertexAttribPointer(TextureCoord_Location, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
And those are my shaders:
#version 410 core
uniform mat4 mvMatrix;
in vec4 vPosition;
in vec2 uvCoord;
smooth out vec2 texCoord;
void main(void)
{
texCoord = uvCoord;
gl_Position = vPosition * mvMatrix;
}
#version 410 core
uniform sampler2D tDiffuse;
uniform sampler2D tPosition;
uniform sampler2D tNormals;
in vec2 texCoord;
out vec4 fragColor;
void main( void )
{
vec4 image = texture( tDiffuse, texCoord.st );
vec4 position = texture( tPosition, texCoord.st );
vec4 normal = texture( tNormals, texCoord.st );
fragColor.xyz = vec3(0.5f, 0.5f, 0.5f);
}
The problems is that when I try to set the attribute variables the one called uvCoord is not found. I think this may be due to an optimization, however, if that's so, what am I'm doing wrong or how should I do it? The uvCoord is used in the fragment shader.
The uvCoord is used in the fragment shader.
No it is not. Your output is a constant color. As a result, the texture fetches before are all eliminated by a decent compiler, as is the texCoord varying. This ultimately results in the elimination of the input attribute, which simply does not affect the output of the program in any way. This is allowed by the GL spec. Your attribute is just not considered active, but only active attributes have locations.