Changing shaders dynamically in OpenGL application - c++

I want to change color of objects in my program with shaders - fragment shader to be precise.
I have two shader programs: box, triangle (names are random - just for easier reference). For both programs I use this same vertex shader:
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
vec3 pos;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
}
and then i am using my box_shader program:
box_shader.Use();
// Create camera transformation
view = camera.GetViewMatrix();
glm::mat4 projection;
projection = glm::perspective(camera.Zoom, (float)WIDTH/(float)HEIGHT, 0.1f, 100.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(box_shader.Program, "model");
GLint viewLoc = glGetUniformLocation(box_shader.Program, "view");
GLint projLoc = glGetUniformLocation(box_shader.Program, "projection");
// Pass the matrices to the shader
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
later in the program I'd like to use triangle_shader program. What I was trying is:
triangl_shader.Use()
DrawTraingles();
So I don't call again glGetUniformLocation, instead use those created earlier. Unfortunately with this I don't see triangles drawn with DrawTraingles(), although, when I don't switch shader program they appear.
For my shaders loading and use I use this class: learnopengl so everything is there regarding Use() function.
Can someone tell me what I should do to make use of different shaders?
EDIT:
What I've figured out was add glGetUniformLocations after packet_shader.Use(), so it looks now like this:
packet_shader.Use();
// Get the uniform locations
modelLoc = glGetUniformLocation(packet_shader.Program, "model");
viewLoc = glGetUniformLocation(packet_shader.Program, "view");
projLoc = glGetUniformLocation(packet_shader.Program, "projection");
// Pass the matrices to the shader
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
Although, I am not sure if this is the best idea in the matter of performance. Can someone tell me is it ok?

Uniforms are per-program state in the GL. So there are two issues here:
The uniform locations might be completely different in each program. For each program, you'll have to query the uniform locations, and have to store them separately for late use.
The uniform values have to be set for each program seperately. Even if you consider two programs "sharing" the same uniform, this is not the case. All the glUniform*() setters affect only the program currently in use. Since each of your program has its own model, view and projection uniform, you have to set these for each program, everytime they change. Currently, it looks like you never set those for the second program, so they are left at ther initial defaults of all zeros.
If you want to share uniforms between different programs, you might consider looking into Uniform Buffer Objects (UBOs).

Related

Why does my program translate all of my vertices?

I have two classes with their own model coordinates, colors, etc. I also have two shader programs that are logically the same. First I execute one shader program, edit the uniforms with the traditional view and projection matrices, and then I call the class to edit the model matrix uniquely, and then draw it's primitives. Immediately afterwards, I do the exact same thing, but with the second shader program, edit the uniforms again, and call the second class to draw it's primitives and it's own unique model matrix coordinates.
In the second class, I translate the model matrix each iteration, but not in the first class. For some reason it translates the model matrix in the first class as well, and I dont know why?
Source code:
//First shader program, update view and proj matrix, and have first class draw it's vertices
executable.Execute();
GLuint viewMatrix = glGetUniformLocation(executable.getComp(), "viewMatrix");
glUniformMatrix4fv(viewMatrix, 1, GL_FALSE, glm::value_ptr(freeView.getFreeView()));
GLuint projMatrix = glGetUniformLocation(executable.getComp(), "projectionMatrix");
glUniformMatrix4fv(projMatrix, 1, GL_FALSE, glm::value_ptr(projectionMatrix.getProjectionMatrix()));
temp.useClass(executable);
//Second Shader program, update view and proj matrix, and have second class draw it's vertices
executable2.Execute();
viewMatrix = glGetUniformLocation(executable2.getComp(), "viewMatrix");
glUniformMatrix4fv(viewMatrix, 1, GL_FALSE, glm::value_ptr(freeView.getFreeView()));
projMatrix = glGetUniformLocation(executable2.getComp(), "projectionMatrix");
glUniformMatrix4fv(projMatrix, 1, GL_FALSE, glm::value_ptr(projectionMatrix.getProjectionMatrix()));
temp2.useClass(executable2);
VertexShader:
#version 330 core
layout(location = 0) in vec3 positions;
layout(location = 1) in vec3 colors;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
out vec3 color;
void main()
{
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(positions, 1.0f);
color = colors;
}
The second vertex shader is logically the same, with just different variable names, and the fragment shader just outputs color.
useClass function (from class one):
glBindVertexArray(tempVAO);
glm::mat4 modelMat;
modelMat = glm::mat4();
GLuint modelMatrix = glGetUniformLocation(exe.getComp(), "modelMatrix");
glUniformMatrix4fv(modelMatrix, 1, GL_FALSE, glm::value_ptr(modelMat));
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
useClass function (from class two):
glBindVertexArray(tempVAO);
for(GLuint i = 0; i < 9; i++)
{
model[i] = glm::translate(model[i], gravity);
GLuint modelMatrix = glGetUniformLocation(exe.getComp(), "modelMatrix");
glUniformMatrix4fv(modelMatrix, 1, GL_FALSE, glm::value_ptr(model[i]));
glDrawArrays(GL_POINTS, 0, 1);
}
glBindVertexArray(0);
Both classes have data protection, and I just don't understand how translating the model matrix in one class, makes the model matrix in another class get translated as well, when using two shader programs? When I use one shader program for both classes, the translating works out fine, but not so much when I use two shader programs(one for each class)...
EDIT: After working on my project a little more, I figure out that the same problem happens when I compile and link two different shader programs with the same exact vertex and fragment shader, and just use each shader program before I draw from each class. So now the question I have is more along the lines of: Why does using two identical shader programs in between draws cause all of the vertices/model matrices to get translated?
I figured out what the problem was. Basically, since there is not really a way to directly exit the execution of a shader, my program was getting confused when I passed shaders getting executed through functions into other parts of the program. For some reason the program was thinking two shader programs were getting executed at the same time, hence why the model matrix was not getting reset consistently. To fix this issue, I limited the scope of each individual shader. Instead of having shaders executed in the same function and then passed through to other classes, I put each shader in the respective class that it gets used in.

Simple GL fragment shader behaves strangely on newer GPU

I am tearing my hair out at this problem! I have a simple vertex and fragment shader that worked perfectly (and still does) on an old Vaio laptop. It's for a particle system, and uses point sprites and a single texture to render particles.
The problem starts when I run the program on my desktop, with a much newer graphics card (Nvidia GTX 660). I'm pretty sure I've narrowed it down to the fragment shader, as if I ignore the texture and simply pass inColor out again, everything works as expected.
When I include the texture in the shader calculations like you can see below, all points drawn while that shader is in use appear in the center of the screen, regardless of camera position.
You can see a whole mess of particles dead center using the suspect shader, and untextured particles rendering correctly to the right.
Vertex Shader to be safe:
#version 150 core
in vec3 position;
in vec4 color;
out vec4 Color;
uniform mat4 view;
uniform mat4 proj;
uniform float pointSize;
void main() {
Color = color;
gl_Position = proj * view * vec4(position, 1.0);
gl_PointSize = pointSize;
}
And the fragment shader I suspect to be the issue, but really can't see why:
#version 150 core
in vec4 Color;
out vec4 outColor;
uniform sampler2D tex;
void main() {
vec4 t = texture(tex, gl_PointCoord);
outColor = vec4(Color.r * t.r, Color.g * t.g, Color.b * t.b, Color.a * t.a);
}
Untextured particles use the same vertex shader, but the following fragment shader:
#version 150 core
in vec4 Color;
out vec4 outColor;
void main() {
outColor = Color;
}
Main Program has a loop processing SFML window events, and calling 2 functions, draw and update. Update doesn't touch GL at any point, draw looks like this:
void draw(sf::Window* window)
{
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
sf::Texture::bind(&particleTexture);
for (ParticleEmitter* emitter : emitters)
{
emitter->useShader();
camera.applyMatrix(shaderProgram, window);
emitter->draw();
}
}
emitter->useShader() is just a call to glUseShader() using a GLuint pointing to a shader program that is stored in the emitter object on creation.
camera.applyMatrix() :
GLuint projUniform = glGetUniformLocation(program, "proj");
glUniformMatrix4fv(projUniform, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
...
GLint viewUniform = glGetUniformLocation(program, "view");
glUniformMatrix4fv(viewUniform, 1, GL_FALSE, glm::value_ptr(viewMatrix));
emitter->draw() in it's entirity:
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Build a new vertex buffer object
int vboSize = particles.size() * vboEntriesPerParticle;
std::vector<float> vertices;
vertices.reserve(vboSize);
for (unsigned int particleIndex = 0; particleIndex < particles.size(); particleIndex++)
{
Particle* particle = particles[particleIndex];
particle->enterVertexInfo(&vertices);
}
// Bind this emitter's Vertex Buffer
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// Send vertex data to GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), &vertices[0], GL_STREAM_DRAW);
GLint positionAttribute = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(positionAttribute);
glVertexAttribPointer(positionAttribute,
3,
GL_FLOAT,
GL_FALSE,
7 * sizeof(float),
0);
GLint colorAttribute = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colorAttribute);
glVertexAttribPointer(colorAttribute,
4,
GL_FLOAT,
GL_FALSE,
7 * sizeof(float),
(void*)(3 * sizeof(float)));
GLuint sizePointer = glGetUniformLocation(shaderProgram, "pointSize");
glUniform1fv(sizePointer, 1, &pointSize);
// Draw
glDrawArrays(GL_POINTS, 0, particles.size());
And finally, particle->enterVertexInfo()
vertices->push_back(x);
vertices->push_back(y);
vertices->push_back(z);
vertices->push_back(r);
vertices->push_back(g);
vertices->push_back(b);
vertices->push_back(a);
I'm pretty sure this isn't an efficient way to do all this, but this was a piece of coursework I wrote a semester ago. I'm only revisiting it to record a video of it in action.
All shaders compile and link without error. By playing with the fragment shader, I've confirmed that I can use gl_PointCoord to vary a solid color across particles, so that is working as expected. When particles draw in the center of the screen, the texture is drawn correctly, albeit in the wrong place, so that is loaded and bound correctly as well. I'm by no means a GL expert, so that's about as much debugging as I could think to do myself.
This wouldn't be annoying me so much if it didn't work perfectly on an old laptop!
Edit: Included a ton of code
As turned out in the comments, the shaderProgram variable which was used for setting the camera-related uniforms did not depend on the actual program in use. As a result, the uniform locations were queried for a different program when drawing the textured particles.
The uniform location assignment is totally implementation specific, nvidia for example tends to assign them by the alphabetical order of the uniform names, so view's location would change depending if tex is actually present (and acttively used) or not. If the other implementation just assigns them by the order they appear in the code or some other scheme, things might work by accident.

Shaders not outputting anything when attribute location != 0

I have the following extremely simple vertex shader, when I render with it I get a blank screen:
#version 110
layout(location = 1) attribute vec3 position;
uniform mat4 modelview_matrix;
uniform mat4 projection_matrix;
void main() {
vec4 eye = modelview_matrix * vec4(position, 1.0);
gl_Position = projection_matrix * eye;
}
However, changing
layout(location = 1) attribute vec3 position; to
layout(location = 0) attribute vec3 position;
allows me to render correctly. Here's my rendering function:
glUseProgram(program);
GLenum error;
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUniformMatrix4fv(
modelview_uniform, 1, GL_FALSE, glm::value_ptr(modelview));
glUniformMatrix4fv(
projection_uniform, 1, GL_FALSE, glm::value_ptr(projection));
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glVertexAttribPointer(
position_attribute,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0);
glEnableVertexAttribArray(position_attribute);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
glDrawElements(
GL_TRIANGLES,
monkey_mesh.indices.size(),
GL_UNSIGNED_INT,
(void*)0);
glDisableVertexAttribArray(position_attribute);
glutSwapBuffers();
I obtain position_attribute through a call to glGetAttribLocation(program, "position");. It contains the correct value in both cases (1 in the first case, 0 in the second).
Is there something I'm doing wrong? I'm sure I'm able to render when location == 0 only because I'm lucky and the data is written there by sheer luck but I can't figure out for the life of me what step I'm missing.
What you are seeing is not possible. GLSL version 1.10 does not support layout syntax at all. So your compiler should have rejected the shader. Therefore, either your compiler is not rejecting the shader and is therefore broken, or you are not loading the shader you think you are.
If it still doesn't work when using GLSL version 3.30 or higher (the first core version to support layout(location=#) syntax for attribute indices), then what you're seeing is the result of a different bug. Namely, the compatibility profile implicitly states that, to render with vertex arrays, you must either use attribute zero or gl_Vertex. The core profile has no such restrictions. However, this restriction was in GL for a while, so some implementations will still enforce it, even on the core profile where it doesn't exist.
So just use attribute zero. Or possibly switch to the core profile if you're not already using it (though I'd be surprised if an implementation actually implements the distinction correctly. Generally, it'll either be too permissive in compatibility or too restrictive in core).

How do i display 2 or more objects in openGL (model - view - projection matrices and shaders)

It's all ok when i want to draw one object, for example a cube. I create vertices for cube, i create the buffer, i create the MVP matrix and send it to shader and it works nice.
But, what to do when i want to draw 2 or more objects, for example both cube and a triangle? I believe that View and Projection matrices should be same both for triangle and cube, i only need different Model matrix, right?
So that means that i will have two MVPs?
//Example (using GLM):
glm::mat4 MVPC = Projection * View * ModelCube;
glm::mat4 MVPT = Projection * View * ModelTriangle;
So what do i do with those two now? This is the vertex shader that works good for cube
//vertex shader
#version 330 core
layout(location = 0) in vec3 verticesCube;
uniform mat4 MVPC;
void main(){
gl_Position = MVPC * vec4(verticesCube,1);
}
And what should i do with MVPT (triangle) in shader, i tried messing around with different things, but i can't get it to work, i can't display both the cube and the triangle at the same time.
The confusion comes from thinking that the shader controls multiple vertex arrays at once, when it should be thought of as a universal entity. A vertex array is passed to the shader, then the object is drawn. And the process is repeated.
For example, let's say we assign the variable matrixID to the uniform MVP:
// get handle for our "MVP" uniform
GLuint matrixID = glGetUniformLocation(programID, "MVP");
When we're ready to draw an object, we set matrixID to the object's MVP:
glUniformMatrix4fv(matrixID, 1, GL_FALSE, &cubeMVP[0][0]);
Then bind the vertex buffer, set the attribute pointer, and draw it:
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, cubeVerteciesBuffer);
glVertexAttribPointer(
0, // shader layout location
3,
GL_FLOAT,
GL_FALSE,
0,
(void *)0
);
glDrawArrays(GL_TRIANGLES, 0, 12*3); // draw cube
Now we move on to the triangle and repeat the process - set matrixID to the object's MVP, bind the vertex buffer, set the attribute pointer, and draw it:
glUniformMatrix4fv(matrixID, 1, GL_FALSE, &triMVP[0][0]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, triangleVerteciesBuffer);
glVertexAttribPointer(
0, // shader layout location
3,
GL_FLOAT,
GL_FALSE,
0,
(void *)0
);
glDrawArrays(GL_TRIANGLES, 0, 3); // draw triangle
The corresponding vertex shader code:
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertecies_modelspace;
uniform mat4 MVP;
void main(){
gl_Position = MVP * vec4(vertecies_modelspace, 1);
}
OpenGL is not a scene graph. It draws things according to the current state and then forgets about it.
So if you want to draw different geometries, with different transformations just set the corresponding transformation matrix (uniform), draw the object and repeat this for each object you want to draw. After geometry has been drawn, the following operations will have no further effect on it, other than it might be overdrawn.
An alternative that may also work, would be to do the 'ModelViewProjection' matrix calculation in the vertex shader. You can do this by making uniform model, view, and projection matrix variables in the vertex shader. You can then globally calcluate view and projection matrices, and send them to the shader. You can then just calculate the model matrix for your cube and triangle (or whatever objects you need to render) individually, and send those model matrices to the shader as well.
View and projection matrix calculations; this could be in a separate 'camera' class:
glm::mat4 viewMatrix = glm::lookAt(
glm::vec3(0, -5, 0), // camera location in world
glm::vec3(0, 0, 0), // point camera is looking at
glm::vec3(0, 1, 0) // orientation of camera, change 1 to -1 to flip camera upside down
);
glm::mat4 projectionMatrix = glm::perspective(35.0f, displayWidth / displayHeight, 0.1f, 100.0f);
// send view and projection matrices to the shader
glUseProgram(shaderProgram);
GLint viewMatrixId = glGetUniformLocation(shaderProgram, "view");
GLint projectionMatrixId = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(viewMatrixId, 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(projectionMatrixId, 1, GL_FALSE, &projectionMatrix[0][0]);
glUseProgram(0);
Model matrix calculation; this code can go in a separate class, and you can instantiate it for each object that you want to render:
// this can go after where you initialize your cube or triangle vertex information
glUseProgram(shaderProgram);
modelMatrixId = glGetUniformLocation(shaderProgram, "model"); //modelMatrixId can be a global GLint
glUniformMatrix4fv(modelMatrixId, 1, GL_FALSE, &modelMatrix[0][0]); //modelMatrix can be a global glm::mat4
glUseProgram(0);
//use this for every render frame
glUseProgram(shaderProgram);
glUniformMatrix4fv(modelMatrixId, 1, GL_FALSE, &modelMatrix[0][0]);
// code to bind vertices and draw you objects goes here
glUseProgram(0);
New vertex shader:
//vertex shader
#version 330 core
layout(location = 0) in vec3 vertices;
uniform mat4 model, view, projection;
void main(){
gl_Position = projection * view * model * vec4(vertices, 1.0);
}
Have two arrays of vertices. Lets say array1 for cube, array2 for circle.
Create 2 vaos and 2 vbos. vao1 and vbo1 for cube. vao2 and vbo2 for circle.
Bind vao1, bind vbo1, fill up vbo1 buffer with array1. glUserProgram(program) which is a program for shaders, set up vertexattripointer.
call glDrawArray()
Do the same thing for other vao and vbo.

OpenGL Vertex Shader Runtime Issues (not using VBOs or textures)

I have the following vertex shader:
uniform mat4 uMVP;
attribute vec4 aPosition;
attribute vec4 aNormal;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
varying vec4 vPrimaryColor;
void main() {
gl_Position = uMVP * aPosition;
vPrimaryColor = vec4(1.0, 1.0, 1.0, 1.0);
vTexCoord = aTexCoord;
}
And the following fragment shader:
uniform sampler2D sTex;
varying vec2 vTexCoord;
varying vec4 vPrimaryColor;
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
Note that while I have a vTexCoord and vPrimaryColor, neither of which are used in the fragment shader. (The reason why they are there is because they eventually will be).
Now, I also set uMVP to be the identity matrix for now, and draw using the following code:
// Load the matrix
glUniformMatrix4fv(gvMVPHandle, 1, false, &mvPMatrix.matrix[0][0]);
// Draw the square to be textured
glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gFullScreenQuad);
glEnableVertexAttribArray(gvPositionHandle);
glVertexAttribPointer(gvTexCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, gFullScreenQuad);
glDrawArrays(GL_QUADS, 0, 4);
where the square is:
const GLfloat PlotWidget::gFullScreenQuad[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f};
So, when I run this program, I get a black screen. Which does not seem like you would expect. However, when I change the line in the shade:
vTexCoord = aTexCoord;
To
vTexCoord = vec2(1.0, 1.0);
It works perfectly. So I would assume the problem with the code is with that line of code, but I can't think of anything in opengl that would cause this. Also, I'm using Qt for this project, which means this class is using the QGLWidget. I've never had this issue with OpenGL ES 2.0.
Any suggestions?
I'm sorry for the vague title, but I don't even know what class of problem this would be.
Are you checking glGetShaderInfoLog and glGetProgramInfoLog during your shader compilation? If not then I would recommend that as the first port of call.
Next thing to check would be your the binding for the texture coordinates. Are the attributes are being set up correctly? Is the data valid?
Finally, start stepping through your code with liberal spraying of glGetError calls. It wil almost certainly fail on glDrawArrays which won't help you much, but that's usually when the desparation sets in for me!
OR
You could try gDEBugger. I use it mainly to look for bottlenecks and to make sure I'm releasing OpenGL resources properly so can't vouch for the debugger, but it's worth a shot.
Apparently you need to actually use the whole use glEnableVertexAttribArray if it's getting passed into the fragment shader. I have no idea why though. But changing the drawing code to this:
glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gFullScreenQuad);
glEnableVertexAttribArray(gvPositionHandle);
glVertexAttribPointer(gvTexCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, gFullScreenQuad);
glEnableVertexAttribArray(gvTexCoordHandle);
glDrawArrays(GL_QUADS, 0, 4);
made it work.
Same problem, different cause.
For some devices the automatic variable linking in glLinkProgram does not work as specified.
Make sure things are done in the following order:
glCreateProgram
glCreateShader && glCompileShader for both shaders
glBindAttribLocation for all attributes
glLinkProgram
Step 3 can be repeated later at any time to rebind variables to different buffer slots - however changes only become effective after another call to glLinkgProgram.
or short: whenever you call glBindAttribLocation make sure a glLinkProgram calls comes after.