Getting textures to work in OpenGL 3.2 - c++

I have been staring at this code for a while now with no luck. I'm working on integrating librocket into my own project (the library isn't that important to the question) and part of that requires writing a renderer class. I've been trying to do just that but can't get the textures to display. The vertex color and position work fine.
I'm using OpenGL3.2.
I've temporarily modified the code to try to draw a single quad. The only parameter being used is the texture parameter, which is just a GLuint cast to another type.
There's a good chance that I'm missing something stupid, but I can't see it. Hopefully another set of eyes will help. Feel free to ask for more code/info.
// Called by Rocket when it wants to render geometry that it does not wish to optimise.
void SDLRenderInterface::RenderGeometry(Rocket::Core::Vertex* vertices, int num_vertices, int* indices, int num_indices, const Rocket::Core::TextureHandle texture, const Rocket::Core::Vector2f& translation)
{
GLuint program;
GLuint vertexBuffer;
GLuint indexBuffer;
GLuint vertexPosLoc = 0;
GLuint vertexColorLoc = 0;
GLuint vertexTexCoordLoc = 0;
GLuint texSamplerLoc = 0;
GLuint translationLoc = 0;
GLuint viewDimLoc = 0;
int offset = 8;
int vertexCount = 4;
float vertexData[] = {-0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
-0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0};
int indexData[] = {0,1,2,0,2,3};
int indexCount = 6;
// Populate vertex buffer
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*offset*vertexCount,
vertexData, GL_STATIC_DRAW);
// Populate index buffer
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * indexCount,
indexData, GL_STATIC_DRAW);
program = shaderManager->getProgram(2, "rocketTex.vert",
"rocketTex.frag");
glUseProgram(program);
// Set up the texture
texSamplerLoc = glGetUniformLocation(program, "texSampler");
vertexTexCoordLoc = glGetAttribLocation(program, "vertexTexCoord");
if(texSamplerLoc == -1)
{
std::cerr << "Error: cannot find texture location." << std::endl;
return;
}
if(vertexTexCoordLoc == -1)
{
std::cerr << "Error: cannot find texture coord location."
<< std::endl;
return;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, (GLuint) texture);
glUniform1i(texSamplerLoc, 0);
// Set up the per vertex texture coords
glEnableVertexAttribArray(vertexTexCoordLoc);
glVertexAttribPointer(vertexTexCoordLoc, 2, GL_FLOAT, GL_FALSE,
offset * sizeof(float),
(void*) (sizeof(float) * 6));
// Set up uniforms
translationLoc = glGetUniformLocation(program, "translation");
viewDimLoc = glGetUniformLocation(program, "viewDimensions");
if(translationLoc == -1)
{
std::cerr << "Error: cannot find translation location."
<< std::endl;
return;
}
if(viewDimLoc == -1)
{
std::cerr << "Error: cannot find viewDim location."
<< std::endl;
return;
}
glUniform2f(translationLoc, 0,0);
glUniform2f(viewDimLoc, 1,1);
// Set up per-vertex attributes
vertexPosLoc = glGetAttribLocation(program, "vertexPosition");
vertexColorLoc = glGetAttribLocation(program, "vertexColor");
if(vertexPosLoc == -1)
{
std::cerr << "Error: cannot find vertex position location."
<< std::endl;
return;
}
if(vertexColorLoc == -1)
{
std::cerr << "Error: cannot find vertex color location."
<< std::endl;
return;
}
glEnableVertexAttribArray(vertexPosLoc);
glEnableVertexAttribArray(vertexColorLoc);
glVertexAttribPointer(vertexPosLoc, 2, GL_FLOAT, GL_FALSE,
offset * sizeof(float), 0);
glVertexAttribPointer(vertexColorLoc, 4, GL_FLOAT, GL_TRUE,
offset * sizeof(float),
(void*) (sizeof(float) * 2));
// Draw the geometry
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(vertexPosLoc);
glDisableVertexAttribArray(vertexColorLoc);
glDisableVertexAttribArray(vertexTexCoordLoc);
glDeleteBuffers(1, &vertexBuffer);
glDeleteBuffers(1, &indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glUseProgram(0);
}
Vertex Shader:
#version 120
uniform vec2 translation;
uniform vec2 viewDimensions;
attribute vec2 vertexPosition;
attribute vec4 vertexColor;
attribute vec2 vertexTexCoord;
varying vec2 texCoord;
varying vec4 fragColor;
void main(void)
{
vec2 ndcPos = ((vertexPosition + translation)/(viewDimensions));
texCoord = vertexTexCoord;
fragColor = vertexColor;
gl_Position = vec4(ndcPos, 0.0, 1.0);
}
Fragment Shader:
#version 120
uniform sampler2D texSampler;
varying vec2 texCoord;
varying vec4 fragColor;
void main(void)
{
vec4 objectColor = texture2D(texSampler, texCoord);
gl_FragColor = vec4((objectColor * fragColor).xyz, 1.0);
}

So, I finally figured it out. jozxyqk's advice for testing the texture coords confirmed my suspicions that the texture coordinates were off (every vertex was getting the same coordinate). The problem ended up being that I was calling glVertexAttribDivisor(attributeLoc, 1) in another part of my code and never setting it back to per vertex, so it was affecting my other shaders. Thinking about the design of OpenGL, it makes sense that this would be necessary.
Glad that's settled!

Related

Opengl shader bitwise operation not working in Windows

I use an integer to use as a "filter" and pass it to a geometric shader and use a bitwise operation to get the bit values. It works in macOS, but not in Windows. To show my point, I used and modified the tutorial code in the geometric shader part in the learnopengl.com found at
https://learnopengl.com/Advanced-OpenGL/Geometry-Shader
Based on the tutorial code, I added the following code in the main.cpp. (I added enabledFaces[] and VFO, and passed them to the shaders.) I only set one bit to each enabledFaces[] integer for simplicity.
int enabledFaces[] = {
1 << 0, 1 << 1, 1 << 2, 1 << 3
};
unsigned int VBO, VAO, VFO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &VFO);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), &points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float)));
glBindVertexArray(VFO);
glBindBuffer(GL_ARRAY_BUFFER, VFO);
glBufferData(GL_ARRAY_BUFFER, sizeof(enabledFaces), &enabledFaces, GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 1, GL_INT, GL_FALSE, sizeof(int), 0);
glBindVertexArray(0);
In the vertex shader as a pass-through:
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in int vEnabledFaces;
out int gEnabledFaces;
out VS_OUT {
vec3 color;
} vs_out;
void main() {
vs_out.color = aColor;
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
gEnabledFaces = vEnabledFaces;
}
And the geometric shader (added the if statement with the gEnabledFaces):
##version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
in int gEnabledFaces[];
in VS_OUT {
vec3 color;
} gs_in[];
out vec3 fColor;
void build_house(vec4 position)
{
fColor = gs_in[0].color; // gs_in[0] since there's only one input vertex
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:bottom-left
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top
fColor = vec3(1.0, 1.0, 1.0);
EmitVertex();
EndPrimitive();
}
void main() {
if ( (gEnabledFaces[0] & 0x01) != 0 || (gEnabledFaces[0] & 0x04) != 0)
build_house(gl_in[0].gl_Position);
}
No change in the fragment shader:
#version 330 core
out vec4 FragColor;
in vec3 fColor;
void main()
{
FragColor = vec4(fColor, 1.0);
}
Due to the if statement in the main() in the geometric shader, two houses (the first and the third polygons) out of the 4 polygons should be displayed. It works correctly on Mac, but nothing is displayed in Windows. If I remove the if statement in Windows, all polygons display correctly. Would someone please explain why this does not work in Windows and how to fix it? Thank you.
As suggested by G.M., the use of glVertexAttribIPointer solves the problem.
But I use Qt and unfortunately, it seems that glVertexAttribIPointer is not available. So I changed the glVertexAttribPointer to float type instead of an integer type. So,
Changing from
glVertexAttribPointer(2, 1, GL_INT, GL_FALSE, sizeof(int), 0);
to
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(float), 0);
Then it works in Windows, in spite of the fact that the passing variables (in the C++ and also in shaders) are all integer type. Strange but it works.

OpenGL camera movement program vertex shader issue

So, I'm a beginner learning graphics programmer. I'm working on a program for camera movement. I think there's something wrong with the vertex shader. The program runs with no errors but the screen is completely blank. Here is the vertex shader I'm using:
#version 330
in vec4 vPosition;
out vec4 vColor;
uniform mat4 model_view;
uniform mat4 projection;
void main()
{
vec4 pos = projection * model_view * vPosition / vPosition.w;
gl_Position = pos;
vColor = vPosition;
}
If I switch the shader back to basic version:
#version 330
in vec4 vPosition;
out vec4 vColor;
void
main()
{
gl_Position = vPosition;
vColor = vPosition;
}
The program runs and renders a triangle successfully. So, I'm pretty sure the error is with the shader.
The shader is called in the initialize function:
void initialize(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0); // white background
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
// Load shaders and use the resulting shader program
GLuint program = InitShader("res/shaders/vshader21.glsl", "res/shaders/fshader21.glsl");
model_view = glGetUniformLocation(program, "model_view");
projection = glGetUniformLocation(program, "projection");
glUseProgram(program);
// Initialize the vertex position attribute from the vertex shader
GLuint loc = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
}
the 'points' in glBufferData is as follows:
const int WIDTH = 500, HEIGHT = 500;
/* Positions */
vec4 points[] = {
vec4(0.5,0.5, 1, 1),
vec4(-0.5,0.5, 1, 1),
vec4(0.5,-0.5, 1, 1) ,
vec4(-0.5,-0.5, 1, 1)
};
model_view and projection are of GLuint type in main application and global.
I set the uniform variables (position, model_view) in the display functions.
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); // clear the window
glPointSize(20.0);
// Projection transformation parameters
GLfloat left = -1.0, right = 1.0;
GLfloat bottom = -1.0, top = 1.0;
GLfloat zNear = 0, zFar = 3.0;
mat4 p = Ortho(left, right, bottom, top, zNear, zFar);
glUniformMatrix4fv(projection, 1, GL_TRUE, p);
vec4 eye(0.0, 0.0, -1.0, 1.0);
vec4 at(0.0, 0.0, 0.0, 1.0);
vec4 up(0.0, 1.0, 0.0, 0.0);
mat4 mv = LookAt(eye, at, up);
glUniformMatrix4fv(model_view, 1, GL_TRUE, mv);
glDrawArrays(GL_TRIANGLES, 0, 3); // draw the points
glFlush();
}
What could possibly be going wrong?
The explicit division by the .w component is superfluous.
vec4 pos = projection * model_view * vPosition / vPosition.w;
vec4 pos = projection * model_view * vPosition;
Note, the Perspective divide is automatically performed after clipping.
Since the vector is multiplied to the uniforms form the right, you do not have to transpose the matrices:
glUniformMatrix4fv(projection, 1, GL_TRUE, p);
glUniformMatrix4fv(projection, 1, GL_FALSE, p);
glUniformMatrix4fv(model_view, 1, GL_TRUE, mv);
glUniformMatrix4fv(model_view, 1, GL_FALSE, mv);
See GLSL Programming/Vector and Matrix Operations

OpenGL Program Does Not Render

I have a Mac running OS X Yosemite with GLFW 3 and OpenGL 4.1. I've got this program:
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
#include <stdio.h>
#include "myglutils.hpp"
int main() {
if (!setupGLFW()) return 1;
setupApple();
GLFWwindow* window = glfwCreateWindow(640, 480, "Perspective", 0, NULL);
if (!window) {
fprintf(stderr, "Failed to create window.\n");
glfwTerminate();
return 0;
}
glfwMakeContextCurrent(window);
if (!setupGLEW()) return 1;
glClearColor(0.2, 0.0, 0.8, 1.0);
const GLfloat vertices[] = {
-1.0, -1.0,
1.0, -1.0,
0.0, 1.0
};
const GLchar* vert_shader =
"#version 410 core\n"
"in vec2 pos;"
"uniform mat4 MVP;"
"void main() {"
" gl_Position = MVP * vec4(pos, 0.0, 1.0);"
"}";
const GLchar* frag_shader =
"#version 410 core\n"
"out vec4 color;"
"void main() {"
" color = vec4(1.0, 0.0, 0.0, 1.0);"
"}";
GLuint shader = getShader(vert_shader, frag_shader);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
GLuint MVPID = glGetUniformLocation(shader, "MVP");
glm::mat4 perspective = glm::perspective(45.0, 4.0 / 3.0, 0.1, 100.0);
glm::mat4 view = glm::lookAt(glm::vec3(4.0, 3.0, 3.0), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
glm::mat4 MVP = perspective * view;
while (!glfwWindowShouldClose(window) && glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS) {
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glUniformMatrix4fv(MVPID, 1, GL_FALSE, &MVP[0][0]);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vao);
glDeleteProgram(shader);
glfwTerminate();
return 0;
}
The functions setupGLFW(), setupApple(), and setupGLEW() aren't the problem. Neither is getShader(). I feel like my error is something silly like forgetting a simple OpenGL function call. What I'm trying to do is draw a triangle at a 3D angle.
Note: this program compiles perfectly, but only displays an empty blue screen.
There are a number of issues in this code. The main one is that you never enable the vertex attribute array. You will need to add a glEnableVertexAttribArray() call in the setup code:
glBindVertexArray(vao);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
Also, the arguments to glDrawArrays() are not correct:
glDrawArrays(GL_TRIANGLES, 0, 6);
The 3rd argument is the number of vertices. Since you only have 3 vertices, it should be:
glDrawArrays(GL_TRIANGLES, 0, 3);
Also, unless this is taken care of in code you did not post, you need to make sure that the attribute location you are using matches the program. You use 0 in the code, but you can't count on the location being zero without any further action. The easiest way to fix this is to use a location layout qualifier in the shader code:
"layout(location=0) in vec2 pos;"
This will set the location to 0, matching your code.

Garbled Triangles from VBO/Shader/MVP Matrix using OpenGL 3.3

Drawing with a regular VBO on older GLSL versions is not a problem but for whatever reason I get this result when using GLSL 3.3.
It should be drawing a 2x2 plane on each axis.
(Lighter colors are closer to the far plane, darker are closer to the near plane)
One of the major changes with 3.3 was that you have to provide uniforms with your Model View Projection matrixes as opposed to using the old provided ones.
I don't know what I'm doing wrong but I'm almost certain it's something to do with the Model View Projection data. Here is the relevant code.
Main draw method
float r = 0.0f;
void display() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glClear(GL_DEPTH_BUFFER_BIT); // And the depth buffer
r += 0.001f;
glUseProgram(program);
glEnable(GL_DEPTH_TEST);
GLuint uniformModel = glGetUniformLocation(program, "model");
GLuint uniformView = glGetUniformLocation(program, "view");
GLuint uniformProjection = glGetUniformLocation(program, "projection");
glm::mat4 projection = glm::perspective(70.0f, 1.0f, 0.0f, 16.0f);
glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));
glm::vec3 eye = glm::vec3(sin(r*0.33)*5.0f,5,cos(r*0.33)*5.0f);
glm::vec3 center = glm::vec3(0.0f,0.0f,0.0f);
glm::vec3 up = glm::vec3(0.0f,0.0f,1.0f);
glm::mat4 view = glm::lookAt(eye, center, up);
glUniformMatrix4fv(uniformView, 1, GL_FALSE, glm::value_ptr(view));
glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f,0.0f,0.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, indices);
glDisableVertexAttribArray(0);
glDisable(GL_DEPTH_TEST);
glUseProgram(0);
glFlush(); // Render now
}
Vertex/Indices Array & Shader
string vert
= "#version 330 core\n"
"layout(location = 0) in vec3 vertex;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main(){\n"
" gl_Position = projection * view * model * vec4(vertex,1.0f);\n"
"}";
string frag
= "#version 330 core\n"
"out vec3 color;\n"
"void main()\n"
"{\n"
" float lin = 1.0 / gl_FragCoord.w;\n"
" float depth = (lin - 0.1) / (16.0 - 0.1);\n"
" color = vec3(depth,depth,1.0f);\n"
"}";
float* data = new float[36] {
-1.0f,-1.0f,0.0f,
1.0f,-1.0f,0.0f,
1.0f,1.0f,0.0f,
-1.0f,1.0f,0.0f,
0.0f,-1.0f,-1.0f,
0.0f,1.0f,-1.0f,
0.0f,1.0f,1.0f,
0.0f,-1.0f,1.0f,
-1.0f,0.0f,-1.0f,
1.0f,0.0f,-1.0f,
1.0f,0.0f,1.0f,
-1.0f,0.0f,1.0f
};
GLuint* indices = new GLuint[18] {
0,1,2,
0,3,2,
4,5,6,
4,7,6,
8,9,10,
8,11,10
};
Init
const int winSize = 1024;
void glInit(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitContextVersion(3,3);
//glutInitContextFlags(GLUT_CORE_PROFILE | GLUT_DEBUG);
glutInitWindowSize(winSize, winSize);
glutInitWindowPosition(25, 25);
glutCreateWindow("Loading...");
glewExperimental = GL_TRUE;
glewInit();
glViewport (0, 0, winSize, winSize);
camera.setPosition(0.0f,0.0f,4.0f);
glGenVertexArrays(1, &vba);
glBindVertexArray(vba);
program = compileShader(vert, frag);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 3*4*3*4, data, GL_STATIC_DRAW);
glEnable(GL_DEPTH_TEST);
while(!bExit) {
string pre;
pre.assign("Test Program - ");
pre.append(std::to_string(fps));
glutSetWindowTitle(pre.c_str());
frames++;
display();
}
}
Turns out I'm an idiot and it was just a typo kind of error.
I define GLuint vbo in the init method AND on the scope of the program as well but the actual value of the vbo is only put into local variable. display() didn't have visibility of the vbo at all.
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
It's always the little things that cause ridiculous bugs. <_>

How to use Multisampling with OpenGL FBOs

I'm trying to enable mutlisampling and alpha-to-coverage for an FBO. Using the default framebuffer, all I have to do is call glEnable(GL_MULTISAMPLE) and glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE). However, I am unable to achieve the same effect using my own FBO.
My goal: Draw the scene to an FBO the same way it would be drawn to the default framebuffer with the above properties. From there I want to be able to use the image as a texture for future passes through a shader.
This works: Code for making an FBO without multisampling/alpha-to-coverage, 1 color attachment, 1 depth attachment:
// Generate the color attachment
glGenTextures(1,&defaultColorAttachment0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,defaultColorAttachment0);
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,screenWidth,screenHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
// Bind the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, defaultColorAttachment0,0);
// Generate the depth attachment
glGenRenderbuffers(1,&defaultDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, defaultDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, defaultDepthBuffer);
This doesn't work. Code trying to make a multisampled FBO:
// Generate the color attachment
glGenTextures(1,&defaultColorAttachment0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, defaultColorAttachment0);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, screenWidth, screenHeight, GL_FALSE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, defaultColorAttachment0,0);
// Generate the depth attachment
glGenRenderbuffers(1,&defaultDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, defaultDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, defaultDepthBuffer);
I have tried looking through the OpenGL wiki on this, although the it's incomplete (various unfinished headings make it look unprofessional). glGetError never complains. I've tried messing around with this, but I either get a black screen or a screen full of garbage pixels.
Main Question: What things do I need to consider/change and where (FBO creation, textures, shaders) in order to get multisampling and alpha-to-coverage to work with an FBO?
You need to allocate a multisampled depth buffer for this to work correctly and give it the same number of samples as your color buffer. In other words, you should be calling glRenderbufferStorageMultisample (...) instead of glRenderbufferStorage (...).
Your FBO should be failing a completeness check the way it is allocated right now. A call to glCheckFramebufferStatus (...) ought to return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE because your depth buffer has exactly 1 sample and your color buffer attachment has 4.
Since you are also using a multisampled texture attachment in this FBO, you should be aware of differences between sampling a single-sampled texture vs. multisampled in GLSL shaders.
Multisampled textures have a special sampler uniform type (e.g. sampler2DMS) and you have to explicitly fetch each sample in the texture by its integer (non-normalized) texel coordinate and sample index using texelFetch (...). This also means that they cannot be filtered or mip-mapped.
You probably do not want a multisampled texture in this case, you probably want to use glBlitFramebuffer (...) to do the MSAA resolve into a single-sampled FBO. If you do this instead you can read the anti-aliased results in your shaders rather than having to fetch each sample and implement the anti-aliasing yourself.
Here is a working example to go along with the accepted answer. It is a modified example of the triangle example from the LearnopenGL tutorials to draw a MSAA custom framebuffer to a quad which is then draw to the default framebuffer (the screen):
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
const char *postProcessvertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec2 position;\n"
"layout (location = 1) in vec2 inTexCoord;\n"
"out vec2 texCoord;\n"
"void main(){\n"
" texCoord = inTexCoord;\n"
" gl_Position = vec4(position.x, position.y, 0.0f, 1.0f);\n"
"}\n\0";
const char *postProcessFragmentShaderSource = "#version 330 core\n"
"out vec4 fragmentColor;\n"
"in vec2 texCoord;\n"
"//notice the sampler\n"
"uniform sampler2DMS screencapture;\n"
"uniform int viewport_width;\n"
"uniform int viewport_height;\n"
"void main(){\n"
" //texelFetch requires a vec of ints for indexing (since we're indexing pixel locations)\n"
" //texture coords is range [0, 1], we need range [0, viewport_dim].\n"
" //texture coords are essentially a percentage, so we can multiply text coords by total size \n"
" ivec2 vpCoords = ivec2(viewport_width, viewport_height);\n"
" vpCoords.x = int(vpCoords.x * texCoord.x); \n"
" vpCoords.y = int(vpCoords.y * texCoord.y);\n"
" //do a simple average since this is just a demo\n"
" vec4 sample1 = texelFetch(screencapture, vpCoords, 0);\n"
" vec4 sample2 = texelFetch(screencapture, vpCoords, 1);\n"
" vec4 sample3 = texelFetch(screencapture, vpCoords, 2);\n"
" vec4 sample4 = texelFetch(screencapture, vpCoords, 3);\n"
" fragmentColor = vec4(sample1 + sample2 + sample3 + sample4) / 4.0f;\n"
"}\n\0";
int main()
{
int width = 800;
int height = 600;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* window = glfwCreateWindow(width, height, "OpenglContext", nullptr, nullptr);
if (!window)
{
std::cerr << "failed to create window" << std::endl;
exit(-1);
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cerr << "failed to initialize glad with processes " << std::endl;
exit(-1);
}
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
int samples = 4;
float quadVerts[] = {
-1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0
};
GLuint postVAO;
glGenVertexArrays(1, &postVAO);
glBindVertexArray(postVAO);
GLuint postVBO;
glGenBuffers(1, &postVBO);
glBindBuffer(GL_ARRAY_BUFFER, postVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(2 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
GLuint msaaFB;
glGenFramebuffers(1, &msaaFB);
glBindFramebuffer(GL_FRAMEBUFFER, msaaFB); //bind both read/write to the target framebuffer
GLuint texMutiSampleColor;
glGenTextures(1, &texMutiSampleColor);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// vertex shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//postprocess vertex shader
unsigned int postProcessVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(postProcessVertexShader, 1, &postProcessvertexShaderSource, NULL);
glCompileShader(postProcessVertexShader);
// postprocess fragment shader
unsigned int postProcessFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(postProcessFragmentShader, 1, &postProcessFragmentShaderSource, NULL);
glCompileShader(postProcessFragmentShader);
// check for shader compile errors
// link shaders
unsigned int postProcessShaderProgram = glCreateProgram();
glAttachShader(postProcessShaderProgram, postProcessVertexShader);
glAttachShader(postProcessShaderProgram, postProcessFragmentShader);
glLinkProgram(postProcessShaderProgram);
// check for linking errors
glDeleteShader(postProcessVertexShader);
glDeleteShader(postProcessFragmentShader);
glUseProgram(postProcessShaderProgram);
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "screencapture"), 0);
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_width"), width);
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_height"), height);
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
unsigned int VBO, VAO;
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, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
bool use_msaa = true;
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
use_msaa = true;
if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS)
use_msaa = false;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (use_msaa) {
glBindFramebuffer(GL_FRAMEBUFFER, msaaFB);
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw our first triangle
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
if (use_msaa) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(postProcessShaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor);
glBindVertexArray(postVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
// cleanup
}
Demo:
Thankyou Matt Stone from the comment section of LearnOpenGL for the working code.
Adding to jackw11111's answer, I wanted to test this sample code in python. Enclosed is my near 1:1 translation to python of code apparently by Matt Stone in comments of LearnOpenGL. Tested on Ubuntu and MacOS.
## Not needed for python.
## #include <glad/glad.h>
# Setup might be something like:
# python3 -m venv venv_msaa
# source venv_msaa/bin/activate
# pip install PyOpenGL glfw numpy
# Note: On a MacOS hidpi screen, the results will vary.
import ctypes
import numpy as np
import glfw
from OpenGL.GL import *
VERTEX_SHADER_SOURCE = """#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
"""
FRAGMENT_SHADER_SOURCE = """#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
"""
POSTPROCESS_VERTEX_SHADER_SOURCE = """#version 330 core
layout (location = 0) in vec2 position;
layout (location = 1) in vec2 inTexCoord;
out vec2 texCoord;
void main(){
texCoord = inTexCoord;
gl_Position = vec4(position.x, position.y, 0.0f, 1.0f);
}
"""
POSTPROCESS_FRAGMENT_SHADER_SOURCE = """#version 330 core
out vec4 fragmentColor;
in vec2 texCoord;
// notice the sampler
uniform sampler2DMS screencapture;
uniform int viewport_width;
uniform int viewport_height;
void main(){
// texelFetch requires a vec of ints for indexing (since we're indexing pixel locations)
// texture coords is range [0, 1], we need range [0, viewport_dim].
// texture coords are essentially a percentage, so we can multiply text coords by total size
ivec2 vpCoords = ivec2(viewport_width, viewport_height);
vpCoords.x = int(vpCoords.x * texCoord.x);
vpCoords.y = int(vpCoords.y * texCoord.y);
// do a simple average since this is just a demo
vec4 sample1 = texelFetch(screencapture, vpCoords, 0);
vec4 sample2 = texelFetch(screencapture, vpCoords, 1);
vec4 sample3 = texelFetch(screencapture, vpCoords, 2);
vec4 sample4 = texelFetch(screencapture, vpCoords, 3);
fragmentColor = vec4(sample1 + sample2 + sample3 + sample4) / 4.0f;
}
"""
def main():
width = 800
height = 600
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
window = glfw.create_window(width, height, "OpenglContext", None, None)
if not window:
print("failed to create window")
sys.exit(-1)
glfw.make_context_current(window);
## Not needed for python.
## if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
## {
## std::cerr << "failed to initialize glad with processes " << std::endl;
## exit(-1);
## }
glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
samples = 4
quadVerts = np.array([
-1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0
], dtype=np.float32)
postVAO = glGenVertexArrays(1)
glBindVertexArray(postVAO)
sizeof_float = ctypes.sizeof(ctypes.c_float) # Complicated way of saying 4
postVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, postVBO)
glBufferData(GL_ARRAY_BUFFER, quadVerts.nbytes, quadVerts.ctypes._as_parameter_, GL_STATIC_DRAW)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof_float, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof_float, ctypes.c_void_p(2 * sizeof_float))
glEnableVertexAttribArray(1)
glBindVertexArray(0)
msaaFB = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, msaaFB); # bind both read/write to the target framebuffer
texMutiSampleColor = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor, 0)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
# vertex shader
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, VERTEX_SHADER_SOURCE)
glCompileShader(vertexShader)
# check for shader compile errors
# fragment shader
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)
glCompileShader(fragmentShader)
# check for shader compile errors
# link shaders
shaderProgram = glCreateProgram()
glAttachShader(shaderProgram, vertexShader)
glAttachShader(shaderProgram, fragmentShader)
glLinkProgram(shaderProgram)
# check for linking errors
glDeleteShader(vertexShader)
glDeleteShader(fragmentShader)
#postprocess vertex shader
postProcessVertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(postProcessVertexShader, POSTPROCESS_VERTEX_SHADER_SOURCE)
glCompileShader(postProcessVertexShader)
# check for shader compile errors
# postprocess fragment shader
postProcessFragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(postProcessFragmentShader, POSTPROCESS_FRAGMENT_SHADER_SOURCE)
glCompileShader(postProcessFragmentShader)
# check for shader compile errors
# link shaders
postProcessShaderProgram = glCreateProgram()
glAttachShader(postProcessShaderProgram, postProcessVertexShader)
glAttachShader(postProcessShaderProgram, postProcessFragmentShader)
glLinkProgram(postProcessShaderProgram)
# check for linking errors
glDeleteShader(postProcessVertexShader)
glDeleteShader(postProcessFragmentShader)
glUseProgram(postProcessShaderProgram)
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "screencapture"), 0)
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_width"), width)
glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_height"), height)
vertices = np.array([
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0
], dtype=np.float32)
VAO = glGenVertexArrays(1)
VBO = glGenBuffers(1)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices.ctypes._as_parameter_, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof_float, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
use_msaa = True
while not glfw.window_should_close(window):
if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
glfw.set_window_should_close(window, True)
if glfw.get_key(window, glfw.KEY_R) == glfw.PRESS:
use_msaa = True
if glfw.get_key(window, glfw.KEY_T) == glfw.PRESS:
use_msaa = False
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
if use_msaa:
glBindFramebuffer(GL_FRAMEBUFFER, msaaFB)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
# draw our first triangle
glUseProgram(shaderProgram)
glBindVertexArray(VAO)
glDrawArrays(GL_TRIANGLES, 0, 3)
glBindVertexArray(0)
if use_msaa:
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glUseProgram(postProcessShaderProgram)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor)
glBindVertexArray(postVAO)
glDrawArrays(GL_TRIANGLES, 0, 6)
glBindVertexArray(0)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
# cleanup
if __name__ == "__main__":
main()