I have an example of a compute shader generating a texture which a fragment shader then renders on to a quad which takes up the whole window.
In the fragment shader code, I see a uniform sampler2D, but how is the output from the compute shader actually passed to the fragment shader? Is it just by virtue of being bound? Wouldn't a better practice be to explicitly bind the texture (via a uniform or some other method) to the fragment/vertex shaders?
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
// Include GLEW
#include <GL/glew.h>
//Glut
#include <GL/glut.h>
const GLchar* computeSource =
"#version 430 core\n"
"\n"
"layout (local_size_x = 32, local_size_y = 16) in;\n"
"\n"
"layout (rgba32f) uniform image2D output_image;\n"
"void main(void)\n"
"{\n"
" imageStore(output_image,\n"
" ivec2(gl_GlobalInvocationID.xy),\n"
" vec4(vec2(gl_LocalInvocationID.xy) / vec2(gl_WorkGroupSize.xy), 0.0, 0.0));\n"
"}\n";
const GLchar* vertexSource =
"#version 430 core\n"
"\n"
"in vec4 vert;\n"
"\n"
"void main(void)\n"
"{\n"
" gl_Position = vert;\n"
"}\n";
const GLchar* fragmentSource =
"#version 430 core\n"
"\n"
"layout (location = 0) out vec4 color;\n"
"\n"
"uniform sampler2D output_image;\n"
"\n"
"void main(void)\n"
"{\n"
" color = texture(output_image, vec2(gl_FragCoord.xy) / vec2(textureSize(output_image, 0)));\n"
"}\n";
GLuint vao;
GLuint vbo;
GLuint mytexture;
GLuint shaderProgram;
GLuint computeProgram;
void checkError(int line)
{
GLint err;
do
{
err = glGetError();
switch (err)
{
case GL_NO_ERROR:
//printf("%d: No error\n", line);
break;
case GL_INVALID_ENUM:
printf("%d: Invalid enum!\n", line);
break;
case GL_INVALID_VALUE:
printf("%d: Invalid value\n", line);
break;
case GL_INVALID_OPERATION:
printf("%d: Invalid operation\n", line);
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
printf("%d: Invalid framebuffer operation\n", line);
break;
case GL_OUT_OF_MEMORY:
printf("%d: Out of memory\n", line);
break;
default:
printf("%d: glGetError default case. Should not happen!\n", line);
}
} while (err != GL_NO_ERROR);
}
void display()
{
glUseProgram(computeProgram);
glBindImageTexture(0, mytexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glDispatchCompute(8, 16, 1);
glBindTexture(GL_TEXTURE_2D, mytexture);
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glFlush();
glutSwapBuffers();
}
void reshape(int width,int height)
{
double w2h = (height>0) ? (double)width/height : 1;
// Set viewport as entire window
glViewport(0,0, width,height);
}
int main(int argc, char** argv)
{
// Window Setup
glutInitWindowSize(640, 400);
glutInitWindowPosition (140, 140);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInit(&argc, argv);
glutCreateWindow( "OpenGL Application" );
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
// X Y Z A
-1.0f, -1.0f, 0.5f, 1.0f,
1.0f, -1.0f, 0.5f, 1.0f,
1.0f, 1.0f, 0.5f, 1.0f,
-1.0f, 1.0f, 0.5f, 1.0f,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
checkError(__LINE__);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
checkError(__LINE__);
GLuint computeShader;
computeProgram = glCreateProgram();
computeShader = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(computeShader, 1, &computeSource, NULL);
glCompileShader(computeShader);
glAttachShader(computeProgram, computeShader);
glLinkProgram(computeProgram);
glGenTextures(1, &mytexture);
glBindTexture(GL_TEXTURE_2D, mytexture);
glTexStorage2D(GL_TEXTURE_2D, 8, GL_RGBA32F, 256, 256);
checkError(__LINE__);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "color");
glLinkProgram(shaderProgram);
checkError(__LINE__);
glutMainLoop();
return 0;
}
The main reason this is working is that uniform variables in shaders have a default value of 0. From the GLSL 4.5 spec, section 4.3.5:
All uniform variables are read-only and are initialized externally either at link time or through the API. The link-time initial value is either the value of the variable's initializer, if present, or 0 if no initializer is present.
The next part you need to understand is that the value of a sampler variable is the texture unit you want to sample from. Very similarly, the value of an image variable is the image unit used for the image access.
Putting these two pieces together, since you don't set values for these uniform variables, the sampler in the fragment shader will access the texture bound to texture unit 0. The image in the compute shader will access the image bound to image unit 0.
Fortunately for you, this is exactly what you need:
Since you never set the active texture unit with glActiveTexture(), this call:
glBindTexture(GL_TEXTURE_2D, mytexture);
will bind the texture to texture unit 0, which means that it will be sampled in your fragment shader.
In your call that binds the image:
glBindImageTexture(0, mytexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
you pass 0 as the first argument, which specifies the image unit you want to bind to. As a result, the compute shader will access this image.
IMHO, it's good style to always set the values of the uniform variables, even if the default might be sufficient. This makes the code more readable, and setting the uniform values will be essential once you use more than one texture/image. So for clarity, I would have something like this in your code:
GLint imgLoc = glGetUniformLocation(computeProgram, "output_image");
glUniform1i(imgLoc, 0);
...
GLint texLoc = glGetUniformLocation(shaderProgram, "output_image");
glUniform1i(texLoc, 0);
Note that the glUniform1i() calls need to be made while the corresponding program is active.
Related
From what I understand, OpenGL always accepts coordinates from -1.0 to 1.0. When the viewport changes, when a window is resized, the distances between points are not preserved.
For example, when the viewport width is 400, the distance between points (0.5,0.0) and (-0.5,0.0) is 200, and when the width is 600, it is 300.
I need to draw a shape (of a scrollbar or something similar), that shouldn't change its width, when the window is resized. I've seen on other posts, that people calculate the floating point coordinates using the window width, but that introduces the problem of floating point accuracy, which can lead to the shape being nearly constant size, but sometimes not exactly.
How to make a shape, that will stay exactly the same width, no matter the width of the window?
The code I'm running is really just a simple triangle appearing on the screen that can be resized. The code is nonetheless a bit long.
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
void onSize (GLFWwindow* window, int width, int height)
{
glViewport (0,0,width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
GLFWwindow* window = glfwCreateWindow(800, 600, "hello", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetWindowSizeCallback(window, onSize);
glewExperimental = GL_TRUE;
glewInit();
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "error with vertex shader compilation\n" << infoLog << std::endl;
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "error with fragment shader compilation\n" << infoLog << std::endl;
}
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "error with linking program\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f, // Left
0.5f, -0.5f, 0.0f, // Right
0.5f, 0.5f, 0.0f // Top
};
GLuint 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(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
So how do I make it so that this triangle has constant width and height regardless of resizing the window?
My answer is related to the phrase in your question: "I need to draw a shape [...], that shouldn't change its width, when the window is resized."
You have to use Orthographic projection.
Add a Uniform variable of type mat4 to the vertex shader and multiply the vertex coordinate with the matrix uniform:
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 u_projection;
void main()
{
gl_Position = u_projection * vec4(position, 1.0);
}
Define the vertices in pixel unit:
GLfloat vertices[] = {
-100.0f, -100.0f, 0.0f, // Left
100.0f, -100.0f, 0.0f, // Right
100.0f, 100.0f, 0.0f // Top
};
Get the current width and height of the viewport and set the matrix using glm::ortho:
GLint proj_loc = glGetUniformLocation(shaderProgram, "u_projection");
while (!glfwWindowShouldClose(window))
{
int width, height;
glfwGetWindowSize(window, &width, &height);
// [...]
glUseProgram(shaderProgram);
glm::mat4 projection = glm::ortho(
-width/2.0f, width/2.0f, -height/2.0f, height/2.0f, -1.0f, 1.0f);
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(projection));
// [...]
}
I'm trying to use glVertexAttribFormat and glVertexAttribBinding to create two triangles, but it doesn't work. I followed the description of how to do this in the question here (Render one VAO containing two VBOs). I don't really knnow what to try. I am new to OpenGL and all descriptions of glVertexAttribFormat appear to assume you already know OpenGL.
This is my code:
#include <glad/glad.h>
#include <glfw/glfw3.h>
#include <iostream>
void adjustViewportToWindowSize(GLFWwindow* window, int width, int height);
void checkEsc(GLFWwindow* window);
int main(void)
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Tab name", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glViewport(0, 0, 800, 600); //size of GL rendering window.
glfwSetFramebufferSizeCallback(window, adjustViewportToWindowSize);
const char* vertexShaderSource = "#version 430 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 430 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
//Attaching shaders to program
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//Can delete shader objects after they are linked
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
float triangleVertices1[] = {
-0.50f, 0.0f, 0.0f,
-0.25f, 0.5f, 0.0f,
0.00f, 0.0f, 0.0f
};
float triangleVertices2[] = {
0.0f, 0.0f, 0.0f,
0.25f, 0.5f, 0.0f,
0.5f, 0.0f, 0.0f
};
unsigned int aVBO[2], VAO2;
glGenVertexArrays(1, &VAO2);
glBindVertexArray(VAO2);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); //format setup without a buffer
glVertexAttribBinding(0, 0);
glBindVertexArray(0);
//Bind Buffers to data next
glGenBuffers(2, aVBO);
glBindBuffer(GL_ARRAY_BUFFER, aVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices1), triangleVertices1, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, aVBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices2), triangleVertices2, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
while (!glfwWindowShouldClose(window))
{
checkEsc(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO2);
glBindVertexBuffer(0, aVBO[0], 0, 3*sizeof(float));
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexBuffer(0, aVBO[1], 0, 3*sizeof(float));
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
//Clear up
glDeleteVertexArrays(1, &VAO2);
glDeleteBuffers(1, aVBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
void adjustViewportToWindowSize(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void checkEsc(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
Can also be accessed on github: https://github.com/Ritzerk/OpenGLSelfStudy
In addition to setting up the buffer bindings, you also have to enable the vertex attribute in the shader. To do so, you have to call glEnableVertexAttribArray during VAO setup:
glBindVertexArray(VAO2);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(0, 0);
//Enable input in shader
glEnableVertexAttribArray(0);
glBindVertexArray(0);
I am trying to implement polygon selection through clicking by first drawing triangle IDs to an off-screen framebuffer and then reading back pixel values at clicked positions via glReadPixels. I am passing ID as unsigned integer to each vertex (and I confirmed that the buffer is correct from apitrace) and outputting it as uvec4 in fragment shader. I set up the framebuffer as RGBA8UI texture (also confirmed units to be correct from apitrace). There is no opengl error and also checked that framebuffer is complete.
The problem is that the output image where the IDs should be always has a value of 255. The area covered by the triangles are modified from the glClear value but they are not (id, 0, 0, 0) but always (255, 0, 0, 0). The exception is those with ID of 0. It seems like somewhere in the shader, the ID is converted to 255 if the ID is not 0. Is this the expected behavior from the code below? Am I doing something wrong?
Vertex buffer:
x (float), y (float), z (float), tx (float), ty (float), id (unsigned int)
Vertex shader:
#version 330 core
// Input
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in uint id;
// Output (Varying)
out vec2 v_texCoord;
flat out uint v_id;
// Uniform
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
void main()
{
v_texCoord = texCoord;
v_id = id;
gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);
}
Fragment shader:
#version 330 core
// Input (Varying)
in vec2 v_texCoord;
flat in uint v_id;
// Output
layout(location = 0) out uvec4 color;
void main()
{
color = uvec4(v_id, 0, 0, 0);
}
GL_VERSION is 3.3.0 NVIDIA 419.35 and I have updated the driver yesterday.
-- Edit --
I was down-voted for lack of information so I created a separate project that just shows my point above with the rest of the code below:
#include <glad/glad.h> // Must be included before GLFW header
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 800;
int main()
{
// glfw: initialize and configure
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
std::cout << glGetString(GL_VERSION) << std::endl;
// Vertex and fragment shaders
GLuint shader = glCreateProgram();
{
GLint isSuccess = false;
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Vertex shader
{
const GLchar* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec2 position;\n"
"layout(location = 1) in uint id;\n"
"flat out uint v_id;\n"
"void main() {v_id = id; gl_Position = vec4(position.x, position.y, 0.0, 1.0);}\n";
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isSuccess);
std::cout << "Vertex shader compile status: " << isSuccess << std::endl;
}
// Fragment shader
{
const GLchar* fragmentShaderSource =
"#version 330 core\n"
"layout(location = 0) out uvec4 color;\n"
"flat in uint v_id;\n"
"void main() {color = uvec4(v_id, 0, 0, 0);}\n";
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isSuccess);
std::cout << "Fragment shader compile status: " << isSuccess << std::endl;
}
glAttachShader(shader, vertexShader);
glAttachShader(shader, fragmentShader);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &isSuccess);
std::cout << "Shader link status: " << isSuccess << std::endl;
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
// Vertex Buffer
GLuint vertexBuffer;
{
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
GLfloat data[] = {
// x y id
-1.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f
};
GLuint* data2 = ((GLuint *)data);
data2[2] = 0;
data2[5] = 0;
data2[8] = 0;
data2[11] = 1;
data2[14] = 1;
data2[17] = 1;
std::cout << "Size of GLuint: " << sizeof(GLuint) << std::endl;
std::cout << "Size of GLfloat: " << sizeof(GLfloat) << std::endl;
std::cout << "Size of vertex buffer: " << sizeof(data) << std::endl;
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Vertex Array
GLuint vertexArray;
{
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 1, GL_UNSIGNED_INT, GL_FALSE, 3 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// Texture for framebuffer
GLuint texture;
glGenTextures(1, &texture);
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
}
// Framebuffer
GLuint framebuffer;
{
GLenum completenessStatus;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
std::cout << "Framebuffer status: " << (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Clear
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GLenum error = glGetError();
std::cout << "No error: " << (error == GL_NO_ERROR) << std::endl;
// Draw
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
{
glDisable(GL_DITHER);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(vertexArray);
glActiveTexture(GL_TEXTURE0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEnable(GL_DITHER);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteProgram(shader);
glDeleteBuffers(1, &vertexBuffer);
glDeleteVertexArrays(1, &vertexArray);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &texture);
return 0;
}
Output:
3.3.0 NVIDIA 419.35
Vertex shader compile status: 1
Fragment shader compile status: 1
Shader link status: 1
Size of GLuint: 4
Size of GLfloat: 4
Size of vertex buffer: 72
Framebuffer status: 1
No error: 1
Framebuffer is RGBA8UI:
Vertices are correct:
Triangle with ID of 0 is colored as (0, 0, 0, 0) as expected:
Area outside triangle is (255, 255, 255, 255) as expected (glClearColor is white):
Triangle with ID of 1 is colored as (255, 0, 0, 0). It should be (1, 0, 0, 0):
The same issue occurs for ID > 1. Why is this the case? How can I make it so that the color is (ID, 0, 0, 0) as shown in the fragment shader?
You have to use glVertexAttribIPointer (focus on I), when defining the array of generic vertex attribute data, for the vertex attribute in uint id;.
When vertex attribute data are defined by glVertexAttribPointer, then they will be converted to floating point values.
See OpenGL 4.6 API Core Profile Specification; 10.2. CURRENT VERTEX ATTRIBUTE VALUES; page 344
The VertexAttribI* commands specify signed or unsigned fixed-point values
that are stored as signed or unsigned integers, respectively. Such values are referred to as pure integers.
...
All other VertexAttrib* commands specify values that are converted directly to the internal floating-point representation.
I want to create 2 triangles with different colors that they change to wireframe mode when I press "Tab" my problem is that I have two VBO and two shaders programs but it only compile one triangle
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>
bool flag;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
const char* vertex_shader =
"#version 110 \n"
"attribute vec3 in_position; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(in_position.x, in_position.y, in_position.z, 1.0);\n"
"} \n";
const char* fragment_shader =
"#version 110 \n"
"void main (void) \n"
"{ \n"
" gl_FragColor = vec4(0.7,0.3,0.3, 1.0); \n"
"} \n";
const char* vertex_shader1 =
"#version 110 \n"
"attribute vec3 in_position; \n"
"void main() \n"
"{ \n"
" gl_Position1 = vec4(in_position.x, in_position.y, in_position.z, 1.0);\n"
"} \n";
const char* fragment_shader1 =
"#version 110 \n"
"void main (void) \n"
"{ \n"
" gl_FragColor1 = vec4(0.4,0.7,0.3, 1.0); \n"
"} \n";
int main()
{
//Initialize GLFW
if (!glfwInit()) //if GLFW is not initialized correctly, exit
{
std::cout << "Failed to initialize GLFW" << std::endl;
return -1;
}
//Create the window
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL) //if the window was not created correctly, exit
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
//Specify OpenGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
//Make the context that was created the current one
glfwMakeContextCurrent(window);
//Initialize GLEW
if (glewInit() != GLEW_OK) //if GLEW was not initialized correctly, exit
{
std::cout << "Failed to initialize GLEW" << std::endl;
glfwTerminate();
return -1;
}
//Set the viewport size changing function
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//Rectangle vertices, to uncomment select the area and press Ctr+K,Ctr+U
float vertices[] =
{
// first triangle
0.55f, 0.5f, 0.0f, // top right
0.55f, -0.5f, 0.0f, // bottom right
-0.45f, 0.5f, 0.0f, // top left
};
float vertices1[] =
{
// second triangle
0.45f, -0.5f, 0.0f, // bottom right
-0.55f, -0.5f, 0.0f, // bottom left
-0.55f, 0.5f, 0.0f // top left
};
//Create the triangle VBO
GLuint triangle_vbo;
glGenBuffers(1, &triangle_vbo); //generate a unique buffer ID
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo); //bind the VBO to the context
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//copy user-defined data into the currently bound buffer
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind the VBO
//Create a vertex shader object
GLuint vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER); //ID of the shader object
glShaderSource(vertexShader, 1, &vertex_shader, NULL); //attach the shader source code to the shader object
glCompileShader(vertexShader);//compile the shader
//Create a fragment shader object
GLuint fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
//Create a shader program object
GLuint shaderProgram;
shaderProgram = glCreateProgram(); //create the program ID
glAttachShader(shaderProgram, vertexShader); //attach the shaders to the program
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram); // link the program
//Delete the vertex andd fragment shader objects
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//Create the triangle VBO
GLuint triangle_vbo1;
glGenBuffers(1, &triangle_vbo1); //generate a unique buffer ID
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo1); //bind the VBO to the context
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW);//copy user-defined data into the currently bound buffer
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind the VBO
//Create a vertex shader object
GLuint vertexShader1;
vertexShader1 = glCreateShader(GL_VERTEX_SHADER); //ID of the shader object
glShaderSource(vertexShader1, 1, &vertex_shader1, NULL); //attach the shader source code to the shader object
glCompileShader(vertexShader1);//compile the shader
//Create a fragment shader object
GLuint fragmentShader1;
fragmentShader1 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader1, 1, &fragment_shader1, NULL);
glCompileShader(fragmentShader1);
//Create a shader program object
GLuint shaderProgram1;
shaderProgram1 = glCreateProgram(); //create the program ID
glAttachShader(shaderProgram1, vertexShader1); //attach the shaders to the program
glAttachShader(shaderProgram1, fragmentShader1);
glLinkProgram(shaderProgram1); // link the program
//Delete the vertex andd fragment shader objects
glDeleteShader(vertexShader1);
glDeleteShader(fragmentShader1);
//Rendering loop
while(!glfwWindowShouldClose(window))
{
//Process input
processInput(window);
//Clear the buffers
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //dark green
//glClearColor(0.5f, 0.0f, 0.0f, 1.0f); //maroon
glClear(GL_COLOR_BUFFER_BIT);
if(flag==true)
{
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
}
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//Render objects...
glUseProgram(shaderProgram); //use a shader program
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo); //bind the VBO
//tell OpenGL how to read and assign the VBO to the attribute
GLint in_pos = glGetAttribLocation(shaderProgram, "in_position");
glVertexAttribPointer(in_pos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
glEnableVertexAttribArray(in_pos);
glUseProgram(shaderProgram);
glUseProgram(shaderProgram1);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo1);
GLint in_pos1 = glGetAttribLocation(shaderProgram1, "in_position");
glVertexAttribPointer(in_pos1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
glEnableVertexAttribArray(in_pos1);
glDrawArrays(GL_TRIANGLES, 0, 6); //draw the triangle
//Swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
glfwSwapBuffers(window);
glfwPollEvents();
}
//Terminate GLFW before exit
glfwTerminate();
return 0;
}
//Viewport size changing function
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
//Input
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if(glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS)
flag=true;
else flag=false;
}
The Tab switch changes to wireframe mode as I long keep pressing the "Tab". I want to change only with one click and when click again to change to default
The 2nd vertex shader and fragment shader won't compile, because gl_Position1 and gl_FragColor1 are not valid output variables. You have to use gl_Position and gl_FragColor. See Vertex Shader and Fragment Shader
const char* vertex_shader1 =
"#version 110 \n"
"attribute vec3 in_position; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(in_position.x, in_position.y, in_position.z, 1.0);\n"
"} \n";
const char* fragment_shader1 =
"#version 110 \n"
"void main (void) \n"
"{ \n"
" gl_FragColor = vec4(0.4,0.7,0.3, 1.0); \n"
"} \n";
I recommend to use glGetShaderiv to verify if the shader code was compiled successfully.
e.g.
GLint status;
glGetShaderiv(vertex_shader1, GL_COMPILE_STATUS, &status);
Use glGetProgramiv with the GL_LINK_STATUS parameter to check if the program was linked successfully.
You have 2 vertex buffers with each 3 vertices, so you have to call glDrawArrays(GL_TRIANGLES, 0, 3); twice and not one time glDrawArrays(GL_TRIANGLES, 0, 6);.
Bind the buffer, define and enable the array of generic vertex attribute data and then draw the triangle:
glUseProgram(shaderProgram);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo); //bind the VBO
GLint in_pos = glGetAttribLocation(shaderProgram, "in_position");
glVertexAttribPointer(in_pos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
glEnableVertexAttribArray(in_pos);
glDrawArrays(GL_TRIANGLES, 0, 3); //draw the 1st triangle
glUseProgram(shaderProgram1);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo1);
GLint in_pos1 = glGetAttribLocation(shaderProgram1, "in_position");
glVertexAttribPointer(in_pos1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
glEnableVertexAttribArray(in_pos1);
glDrawArrays(GL_TRIANGLES, 0, 3); //draw the 2nd triangle
See the preview:
I have my OpenGL - Hello Triangle demo here but I cant make it work. Does anybody know where the problem can be? I am able to compile and run the code but triangle does not show up. It only opens window with grey background.
#include <glew/glew.h>
#include <glfw/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
GLFWwindow* window = glfwCreateWindow(800, 800, "Anton Tutorials - Hello Triangle", NULL, NULL);
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window.\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE; // Needed in core profile
const GLenum err = glewInit();
if (err != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
return -1;
}
// Enable depth-testing
glEnable(GL_DEPTH_TEST);
// Depth-testing interprets a smaller value as "closer"
glDepthFunc(GL_LESS);
//Defining vertices for triangle
GLfloat points[] = {
0.0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f
};
//Vertex buffer object creation
GLuint vbo = 0;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
//Vertex attribute object creation
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//Loading shaders - Vertex shader
const char* vertex_shader =
"#version 410\n"
"in vec3 vp; "
"void main() {"
" gl_Position = vec4 (vp, 1.0);"
" }";
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertex_shader, NULL);
glCompileShader(vs);
//Loading shaders - Fragment shader
const char* fragment_shader =
"#version 410\n"
"out vec4 frag_colour; "
"void main() {"
" frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);"
" }";
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragment_shader, NULL);
glCompileShader(fs);
//Attach shaders
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fs);
glAttachShader(shader_programme, vs);
glLinkProgram(shader_programme);
//RenderLoop
while (!glfwWindowShouldClose(window))
{
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shader_programme);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwPollEvents();
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
You need to enable the 'vp' attribute.
Earlier in your code, you say this:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
If the layout location of your vp shader variable changes, this might now longer be valid.
It would be better to do this:
GLint vpAttr = glGetAttribLocation(shader_programme, "vp");
glVertexAttribPointer(vpAttr, 3, GL_FLOAT, GL_FALSE, 0, NULL);
Then enable the attribute:
glEnableVertexAttribArray(vpAttr);
Otherwise, you should explicitly set the layout location of vp in vertex shader, so you can confidently use 0:
"layout (location=0) in vec3 vp;"