I have an image in a GL_TEXTURE_2D. From here, I need to move this texture onto the framebuffer object. Can someone please point me to an example that uses a 2d texture that is filled with data and places it onto the FBO? I am not trying to render to a texture, but from a texture.
Here is a simple FBO demonstration.
You can find the entire code here on github (the code is based on this sample program from the OpenGL SuperBible).
The program does the following
Draws a red square onto a green background into a FBO. The backing store for the FBO is a 2D texture.
Draw a square onto a blue screen but map the texture from step 1 to the square. This maps the entire scene from the previous step onto the square.
So the theory when using a framebuffer,
(Initialisation) Attach textures to an off-screen framebuffer.
and then there are 2 programs (or 2 passes)
In program 1, render into the framebuffer (which renders into the textures)
In program 2, now render to the window (screen) and use the texture(s) from the first program as input.
So the output of the first program is the input for the second program.
That means the vertex shaders for both stages will be the same. As there is no mathes (rotation) and no lighting effects, all the vertex shader does is pass the input to the output (i.e. the coordinates).
But each stage will have slightly different fragment shader.
The 1st fragment shader outputs a constant color for each pixel. In the source code the output of the 1st fragment shader is configured to the FBO and the FBO uses a texture to store the color data.
The texture generated in the 1st fragment shader is used as input in the 2nd fragment shader. In the 2nd fragment shader, the color is determined by the texture (which is the output of the 1st fragment shader).
So here is the vertex shader. Note the input to the vertex shader is the coordinates (vp) and the texture coordinates. The texcoords are needed by the 2 program's fragment shader to know where to map the texture. In the vertex shader the texcoord's are simply passed from the input to the output.
#version 130
in vec3 vp;
in vec2 texcoord;
out vec2 outtexcoord;
void main () {
gl_Position = vec4 (vp, 1.0);
outtexcoord = texcoord;
}
This is the 1st program's fragment shader. Every pixel is drawn red.
#version 130
in vec2 outtexcoord;
out vec4 frag_colour;
void main () {
frag_colour = vec4 (1.0, 0.0, 0.0, 0.0); //everything red
}
And the 2nd programs fragment shader. Note the sampler2D which is the input texture (generated by the 1st program). Also the outtexcoord are the texture coordinates from the vertex shader. The output color (frag_color) is determined by the texture.
#version 130
uniform sampler2D tex;
in vec2 outtexcoord;
out vec4 frag_color;
void main () {
frag_color = texture(tex, outtexcoord);
}
And here is the C program (I compiled with g++, see the makefile in the github link).
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "shader_utils.h" //see github link for details
void Initialize();
void InitGL();
void InitProgramFBO();
void InitProgramScreen();
void InitBuffer();
void InitFBO();
void Loop();
void RenderToFBO();
void RenderToScreen();
void Shutdown();
void OnWindowResize(GLFWwindow* window, int width, int height);
GLFWwindow* window;
int screenWidth = 640;
int screenHeight = 480;
GLuint render2FBOProgram;
GLuint render2ScreenProgram;
GLuint vao;
GLuint vbo;
GLuint fbo;
GLuint color_texture;
int main() {
Initialize();
Loop();
Shutdown();
return 0;
}
void Initialize() {
InitGL();
InitProgramScreen();
InitProgramFBO();
InitBuffer();
InitFBO();
}
void InitGL() {
glfwInit();
window = glfwCreateWindow(screenWidth, screenHeight, "FBO Demo", NULL, NULL);
glfwMakeContextCurrent(window);
glewInit();
glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
}
void InitProgramFBO() {
GLuint vs;
GLuint fs;
render2FBOProgram = create_program("vs.glsl", "fbo.fs.glsl", vs, fs);
glDeleteShader(vs);
glDeleteShader(fs);
}
void InitProgramScreen() {
GLuint vs;
GLuint fs;
render2ScreenProgram = create_program("vs.glsl", "screen.fs.glsl", vs, fs);
glDeleteShader(vs);
glDeleteShader(fs);
}
void InitBuffer() {
//define the square made up of 2 triangles
static const GLfloat points[] = {
//x y z texcoord u and v
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f
};
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//create buffer for points
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
//tell opengl how to find the coordinate data
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLubyte*)NULL);
glEnableVertexAttribArray(0);
//tell opengl how to find the texcoord data
glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid *)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
}
void InitFBO() {
//create a framebuffer
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//create a texture as the backing store for the framebuffer
glGenTextures(1, &color_texture);
glBindTexture(GL_TEXTURE_2D, color_texture);
glTexStorage2D(GL_TEXTURE_2D, 9, GL_RGBA8, 512, 512); //1 = mipmap levels
//mip map filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//attach the texture as the color attachment of the framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0);
//tell opengl to draw into the color attachment
static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, draw_buffers);
}
void Loop() {
//glBindVertexArray(vao);
//glBindBuffer(GL_ARRAY_BUFFER, vbo);
while (!glfwWindowShouldClose(window)) {
RenderToFBO();
RenderToScreen();
glfwSwapBuffers(window);
glfwPollEvents();
if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE)) {
glfwSetWindowShouldClose(window, 1);
}
}
}
void RenderToFBO() {
static const GLfloat green[] = { 0.0f, 1.0f, 0.0f, 1.0f }; //texture background is green
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, 512, 512); //set view port to texture size
glClearBufferfv(GL_COLOR, 0, green);
glUseProgram(render2FBOProgram);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void RenderToScreen() {
static const GLfloat blue[] = { 0.0f, 0.0f, 1.0f, 1.0f }; //screen background is blue
glViewport(0, 0, screenWidth, screenHeight);
glClearBufferfv(GL_COLOR, 0, blue);
glBindTexture(GL_TEXTURE_2D, color_texture);
glUseProgram(render2ScreenProgram);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Shutdown() {
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteProgram(render2FBOProgram);
glDeleteProgram(render2ScreenProgram);
glfwTerminate();
}
// a call-back function
void OnWindowResize(GLFWwindow* window, int width, int height) {
screenWidth = width;
screenHeight = height;
glViewport(0, 0, screenWidth, screenHeight);
}
Related
Recently I've found the following link about simulating fluids on the GPU. I decided to try to implement it using OpenGL but I got stuck on how to apply successive shaders.
For example, I'd need to apply the advection shader, take its output and apply it into the difuse shader (from the link: "The operator is defined as the composition of operators for advection, diffusion, force application, and projection") but I can't figure out how. I've re-read learn opengl framebuffers tutorial but I'm still lost.
Right now I'm trying to do the following, to test communication between shaders:
1- load texture;
2- apply kernel 1 (inverse shader);
3- apply kernel 2 (advect shader);
Step 1 is done only once, while steps 2 and 3 are done while the windows is open.
Here is the shader code:
/////////////////////////////
// read image - shader.frag
/////////////////////////////
#version 330 core
out vec4 FragColor;
in vec2 uv;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, uv);
}
/////////////////////////////
// color inversion shader
/////////////////////////////
#version 330 core
out vec4 FragColor;
in vec2 uv;
const float offset = 1.0 / 300.0;
void main()
{
FragColor = vec4(vec3(1.0 - texture(screenTexture, uv)), 1.0);
}
/////////////////////////////
// advect shader
/////////////////////////////
#version 330 core
out vec4 FragColor;
in vec2 uv;
uniform float dt;
uniform float sigma_x_inv;
uniform sampler2D u;
uniform sampler2D x;
void main()
{
// change texture2D to texture
vec2 pos = uv - dt * sigma_x_inv * texture(u, uv).xy;
FragColor = texture(x, pos);
}
/////////////////////////////
// vertex shader
/////////////////////////////
#version 330 core
layout (location = 0) in vec3 aPos;
out vec2 uv;
void main()
{
gl_Position = vec4(aPos, 1.0);
uv = aPos.xy;
}
Here is part of my code, mostly taken from here (learn opengl). The screen is just black. If I use one framebuffer(like shown in learn opengl) I can pass data to one shader just fine - I managed to see the inversion and advection shaders working in isolation - but when I add a new frame buffer the screen just goes black. I've also tried using one framebuffer for the two shaders but it didn't work.
Also, how could I pass two textures to one shader? The advection shader, for example, needs to textures, representing two different vector fields. Bellow is my code.
#include "include/glad/glad.h"
#include <GLFW/glfw3.h>
#include "include/shader.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "include/stb_image.h"
#include <glm/glm.hpp>
#include <chrono>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
unsigned int loadTexture(char const * path);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
unsigned int s_width = SCR_WIDTH;
unsigned int s_height = SCR_HEIGHT;
float sigma_x_inv = 1.0f / SCR_WIDTH;
const float dt = 1/60;
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);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
Shader shader("shaders/shader.vert", "shaders/shader.frag");
Shader advectShader("shaders/shader.vert", "shaders/advect.frag");
Shader inversionShader("shaders/shader.vert", "shaders/inversion.frag");
Shader boundaryShader("shaders/shader.vert", "shaders/boundary.frag");
Shader divergenceShader("shaders/shader.vert", "shaders/divergence.frag");
Shader gradientShader("shaders/shader.vert", "shaders/gradient.frag");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float planeVertices[] = { // vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// positions // texCoords
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
float quadVertices[] =
{
// vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// positions // texCoords
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
// plane VAO
unsigned int planeVAO, planeVBO;
glGenVertexArrays(1, &planeVAO);
glGenBuffers(1, &planeVBO);
glBindVertexArray(planeVAO);
glBindBuffer(GL_ARRAY_BUFFER, planeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), &planeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
// screen quad VAO
unsigned int quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
// load and create a texture
// -------------------------
unsigned int texture1, texture2;
// texture 1
// ---------
stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
unsigned int blackTexture = loadTexture("textures/black.png");
unsigned int awesomeTexture = loadTexture("textures/awesomeface.png");
shader.use();
shader.setInt("texture1", 0);
// framebuffer configuration
// -------------------------
unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// create a color attachment texture
unsigned int textureColorbuffer;
glGenTextures(1, &textureColorbuffer);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0,
GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, textureColorbuffer, 0);
// framebuffer configuration
// -------------------------
unsigned int framebuffer2;
glGenFramebuffers(1, &framebuffer2);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
// create a color attachment texture
unsigned int textureColorbuffer2;
glGenTextures(1, &textureColorbuffer2);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0,
GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, textureColorbuffer2, 0);
// create a renderbuffer object for depth and
// stencil attachment (we won't be sampling these)
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
// use a single renderbuffer object for both a depth AND stencil buffer.
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
// now actually attach it
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
// now that we actually created the framebuffer and added all
// attachments we want to check if it is actually complete now
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// tell opengl for each sampler to which texture
// unit it belongs to (only has to be done once)
// -------------------------------------------------------------------------------------------
// advectShader.use(); // don't forget to activate/use the shader before setting uniforms!
// // either set it manually like so:
// advectShader.setInt("u", 0);
// // or set it via the texture class
// advectShader.setInt("x", 1);
divergenceShader.use();
divergenceShader.setFloat("sigma_x_inv", sigma_x_inv);
boundaryShader.use();
boundaryShader.setFloat("scale", 1.0f);
shader.use();
// Program start time
auto start = std::chrono::system_clock::now();
// render
// ------
// bind to framebuffer and draw scene as we normally would to color texture
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glEnable(GL_DEPTH_TEST); // enable depth testing (is disabled for rendering screen-space quad)
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
shader.use();
glBindVertexArray(planeVAO);
glBindTexture(GL_TEXTURE_2D, awesomeTexture);
glDrawArrays(GL_TRIANGLES, 0, 6);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// ----
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
inversionShader.use();
glBindVertexArray(planeVAO);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer2);
glDrawArrays(GL_TRIANGLES, 0, 6);
// test boy
// -----------------------------
// now bind back to default framebuffer and draw a quad plane
// with the attached framebuffer color texture
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST);
// glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
// glClear(GL_COLOR_BUFFER_BIT);
// density step
// -----------------------------
// boundaryShader.use();
// boundaryShader.setFloat("scale", 1.0f);
// boundaryShader.setVec2("offset", 0.5, 0.5);
// pass current time to shader
advectShader.use();
auto now = std::chrono::system_clock::now();
std::chrono::duration<float> diff = now - start;
advectShader.setFloat("dt", diff.count());
advectShader.setFloat("sigma_x_inv", 1000 * 1 / s_width);
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
// needs to check how to get shader
// velocity step
// -----------------------------
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &planeVAO);
glDeleteVertexArrays(1, &quadVAO);
glDeleteBuffers(1, &planeVBO);
glDeleteBuffers(1, &quadVBO);
glDeleteFramebuffers(1, &framebuffer);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
s_width = width;
s_height = height;
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
// utility function for loading a 2D texture from file
// ---------------------------------------------------
unsigned int loadTexture(char const * path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
I wrote a simple OpenGL program to sample a 256x256 PNG texture and render it on a rectangle in a window. I relied heavily on a tutorial for this. When I followed the tutorial almost directly (making minimal changes so as to use SDL2 for [x]), the texture did not render correctly and seemed to be somehow scaled down by a factor of 2. Here is the first version of the source code, as well as a screenshot of the results:
// Link statically with GLEW
#define GLEW_STATIC
// Headers
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_opengl.h>
// Shader sources
const GLchar* vertexSource = R"glsl(
#version 150 core
in vec2 position;
in vec2 color;
out vec2 Color;
void main()
{
Color = color;
gl_Position = vec4(position, 0.0, 1.0);
}
)glsl";
const GLchar* fragmentSource = R"glsl(
#version 150 core
in vec2 Color;
out vec4 outColor;
uniform sampler2D tex;
void main()
{
outColor = texture(tex,Color);
}
)glsl";
int main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_Window* window = SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_GLContext context = SDL_GL_CreateContext(window);
glViewport(0, 0, 800, 600);
// Initialize GLEW
glewExperimental = GL_TRUE;
glewInit();
// Create Vertex Array Object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create a Vertex Buffer Object and copy the vertex data to it
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
// x y u v
-0.5f, 0.5f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, 1.0f // Bottom-left
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Create an element array
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
// Create and compile the vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
// Create and compile the fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
SDL_Surface* surface = IMG_Load("../textures/16x16-sb-ascii.png");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
SDL_FreeSurface(surface);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool running = true;
while (running)
{
SDL_Event windowEvent;
while (SDL_PollEvent(&windowEvent))
{
switch (windowEvent.type)
{
case SDL_QUIT:
running = false;
break;
}
}
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw a rectangle from the 2 triangles using 6 indices
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Swap buffers
SDL_GL_SwapWindow(window);
}
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
SDL_Quit();
return 0;
}
Initial results
For reference, here is the actual texture file:
Original texture file
As you can see, the texture appears to have been quadrupled in the different quadrants of the rectangle, with the lower two copies inexplicably being thoroughly corrupted.
As one might expect, normalizing all the texture coordinates to 0.5 produces a single copy of the texture at the intended scale, although many strange yellow/blue artifacts are still visible, as are some very subtle noisy artifacts around the edges:
GLfloat vertices[] = {
// x y u v
-0.5f, 0.5f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.5f, 0.0f, // Top-right
0.5f, -0.5f, 0.5f, 0.5f, // Bottom-right
-0.5f, -0.5f, 0.0f, 0.5f // Bottom-left
};
Results after scaling down the coordinates.
I'm doing this on a Dell laptop which came with Ubuntu 18.04.4 LTS pre-installed. My graphics card is an Intel UHD Graphics 630 (Coffeelake 3x8 GT2).
Running glxinfo | grep "OpenGL version" gives the following result: OpenGL version string: 3.0 Mesa 19.2.8.
Any help with either the scaling or the blue/yellow artifacts would be greatly appreciated. Thank you!
The generated texture image is a luminance/alpha image and has just one color channel and one alpha channel. Thus you have to use the format GL_RG when you specify the 2 dimensional texture image by glTexImage2D:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, surface->w, surface->h, 0,
GL_RG, GL_UNSIGNED_BYTE, surface->pixels);
Either set a Swizzle mask to read the green and blue color from the red color channel and the alpha value from the green color channel:
GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_GREEN};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
or use Swizzling to get the red color channel and the alpha channel when you do the texture lookup in the fragment shader:
#version 150 core
in vec2 Color;
out vec4 outColor;
uniform sampler2D tex;
void main()
{
outColor = texture(tex,Color).rrrg;
}
I am having some strange behavior when trying to draw a texture in OpenGL. Currently all this program does for me is draw the background color with no indication of a texture being drawn. I have just moved from Visual Studio (where this code produces the correct output) to compiling in the command prompt. This code should color the background and draw one texture in the center of the screen.
I am concerned that I may have supplied the incorrect libraries for compilation since as far as I am concerned everything I am doing is the same. Different libraries, however, always said that they were incompatible.
Main code:
#define GLEW_STATIC
#include <GL/glew.h> // window management library
#include <GL/glfw3.h>
#include <GL/glm.hpp>
#include <GL/gtc/matrix_transform.hpp> //
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int main(int argc, char** argv){
//Initialize GLFW and GLEW...
//Setup and combine shaders...
GLint vertex_att = glGetAttribLocation(program, "vertex");
glVertexAttribPointer(vertex_att, 2, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), 0);
glEnableVertexAttribArray(vertex_att);
GLint color_att = glGetAttribLocation(program, "color");
glVertexAttribPointer(color_att, 3, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), (void *) (2 *sizeof(GLfloat)));
glEnableVertexAttribArray(color_att);
GLint tex_att = glGetAttribLocation(program, "uv");
glVertexAttribPointer(tex_att, 2, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), (void *) (5 *sizeof(GLfloat)));
glEnableVertexAttribArray(tex_att);
glUseProgram(program);
GLuint texture;
glGenTextures(1, &texture);
setthisTexture(texture, "./black.png");
// Create geometry of the square
int size = CreateSquare();
while (!glfwWindowShouldClose(window)){
// Clear background
glClearColor(viewport_background_color_g[0],
viewport_background_color_g[1],
viewport_background_color_g[2], 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//set displacement - 'program' being the shader program
int matrixLocation = glGetUniformLocation(program, "x");
glm::mat4 translate = glm::mat4();
translate = glm::translate(translate, glm::vec3(0.0f, 0.0f, 0.0f));
glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, &translate[0][0]);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);
glfwPollEvents();
glfwSwapBuffers(window);
}
}
Vertex Shader:
#version 130
in vec2 vertex;
in vec3 color;
in vec2 uv;
out vec2 uv_interp;
// Uniform (global) buffer
uniform mat4 x;
// Attributes forwarded to the fragment shader
out vec4 color_interp;
void main(){
vec4 t;
t = vec4(vertex, 0.0, 1.0);
gl_Position = x*t;
color_interp = vec4(color, 1.0);
uv_interp = uv;
}
Fragment Shader:
#version 130
in vec4 color_interp;
in vec2 uv_interp;
uniform sampler2D onetex;
void main(){
vec4 color = texture2D(onetex, uv_interp);
gl_FragColor = vec4(color.r,color.g,color.b,color.a);
if(gl_FragColor.a < 0.9){
discard;
}
}
setthisTexture:
void setthisTexture(GLuint w, const char *fname)
{
glBindTexture(GL_TEXTURE_2D, w);
int width, height, nrChannels;
unsigned char* image = stbi_load(fname, &width, &height, &nrChannels, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
stbi_image_free(image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
CreateSquare:
int CreateSquare(void) {
// The face of the square is defined by four vertices and two triangles
// Number of attributes for vertices and faces
// const int vertex_att = 7; // 7 attributes per vertex: 2D (or 3D) position (2), RGB color (3), 2D texture coordinates (2)
// const int face_att = 3; // Vertex indices (3)
GLfloat vertex[] = {
// square (two triangles)
// Position Color Texcoords
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f // Bottom-left
};
GLuint face[] = {
0, 1, 2, // t1
2, 3, 0 //t2
};
GLuint vbo, ebo;
// Create buffer for vertices
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
// Create buffer for faces (index buffer)
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(face), face, GL_STATIC_DRAW);
// Return number of elements in array buffer
return sizeof(face);
}
For the use of glVertexAttribPointer either a named GL_ARRAY_BUFFER buffer object has to be bound or a pointer to the vertex data has to be passed.
In your case this means, that
int size = CreateSquare();
has to be done before
GLint vertex_att = glGetAttribLocation(program, "vertex");
glVertexAttribPointer(vertex_att, 2, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), 0);
glEnableVertexAttribArray(vertex_att);
.....
Note in the function CreateSquare, the named buffer object vbo is bound:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
which is used by the glVertexAttribPointer calls.
See OpenGL 4.6 API Compatibility Profile Specification; 10.3.9 Vertex Arrays in Buffer Objects; page 409:
A buffer object binding point is added to the client state associated with each
vertex array type and index. The commands that specify the locations and organizations
of vertex arrays copy the buffer object name that is bound to ARRAY_-
BUFFER to the binding point corresponding to the vertex array type or index being
specified. For example, the VertexAttribPointer command copies the value of
ARRAY_BUFFER_BINDING (the queriable name of the buffer binding corresponding
to the target ARRAY_BUFFER) to the client state variable VERTEX_ATTRIB_-
ARRAY_BUFFER_BINDING for the specified index.
I'm trying to create a 2D game using OpenGL. My helper libraries are GLFW, GLEW, SOIL (for images) and glm (for maths).
I've managed to draw a rectangle with a very simple texture on it. But when I try to move this rectangle it frequently has a little flicker (the timer between flickers is almost always the same), and the faster I move it, the more visible it becomes.
Another problem I have is I'm working on a laptop, and it renders fine with my integrated graphics (fine as in it works, it still stutters) but when I execute my program with my Nvidia graphics card it just shows my clearcolor, and nothing else, which is extremely odd. My sprite translation happens in the runCallback code (called in the main loop) and is just a multiplication of matrices. The result matrix is then fetched and used in the draw code. In the drawCallback theres just the DrawSprite function being called. Also I should note I'm using OpenGL 3.3 .
I'm sorry in advance for the rather large amount of code, but after extensive testing and trying a multitude of things i have no idea where my mistake lies...
If you would like to help me but need any more information, i will provide.
UPDATE:
Problem with Nvidia graphics card resolved, it was due to a wrong uniform parameter. But the stutter remains.
IMAGE LOADING CODE
SD_Texture::SD_Texture(const char * fileName)
{
texture = new GLuint;
unsigned char* data = SOIL_load_image(fileName, &width, &height, 0, SOIL_LOAD_RGB);
glGenTextures(1, (GLuint*)texture);
glBindTexture(GL_TEXTURE_2D, *((GLuint*)(texture)));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(data);
}
void SD_Texture::Bind()
{
glBindTexture(GL_TEXTURE_2D, *(GLuint*)texture);
}
VAO SETUP CODE
void SD_Window::SetupVAO()
{
GLfloat vertices[] =
{
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5, 0.0f, 1.0, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (GLvoid*)nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (GLvoid*)(3 * sizeof(GLfloat)));
glBindVertexArray(0);
}
DRAW CODE
void SD_Window::DrawSprite(SD_Sprite * sprite)
{
glActiveTexture(GL_TEXTURE0);
sprite->GetTexture()->Bind();
glUniformMatrix4fv(transformUniform, 1, GL_FALSE, glm::value_ptr(ortho * sprite->GetTransform()));
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
MAIN LOOP CODE
void SD_Window::TakeControl(void (*runCallback)(float delta), void (*drawCallback)())
{
double currentTime;
double oldTime = 0.0f;
while (!ShouldClose())
{
currentTime = glfwGetTime();
glClear(GL_COLOR_BUFFER_BIT);
drawCallback();
glfwSwapBuffers(window);
runCallback(currentTime - oldTime);
oldTime = currentTime;
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
}
VERTEX SHADER
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
out vec2 sCoord;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(position, 1.0f);
sCoord = vec2(texCoord.x, 1.0f - texCoord.y);
}
FRAGMENT SHADER
#version 330 core
in vec2 sCoord;
out vec4 color;
uniform sampler2D sTexture;
void main()
{
color = texture(sTexture, sCoord);
}
I've been trying to get texturing working under opengl 3.1 on an intel HD graphics 2000/3000 graphics card running on ubuntu 13.04. The issue i'm running into is textures either dont load and the basic triangle i'm trying to texture comes up black, or some color from the texture will get loaded but not the entire image. I get the same outcomes using a raw image file as the source or loading a jpeg with libjpeg.
My shaders are as follows:
Vertex shader
#version 130
in vec3 vert;
in vec2 vertTextCoord;
out vec2 fragTexCoord;
void main(){
fragTexCoord = vertTextCoord;
gl_Position = vec4(vert,1);
}
Fragment shader
#version 130
uniform sampler2D tex;
in vec2 fragTexCoord;
out vec4 finalColor;
void main() {
finalColor = texture(tex, fragTexCoord);
}
code for creating the texture
glGenTextures( 1, &texture);
glBindTexture( GL_TEXTURE_2D, texture);
imgdata image_data = loadRaw("texture.bmp", 256, 256);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE,image_data.data);
and the render function
void display(void){
glClearColor( 1.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT );
//load program to use
glUseProgram(shaderprogram);
GLint uniform = glGetUniformLocation(shaderprogram, "tex");
if(uniform == -1){
throw std::runtime_error(std::string("program uniform not found: tex"));
}
// bind the texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(uniform, 0);
//load vertex array to use
glBindVertexArray(cubeVAO);
//draw triangle
glDrawArrays(GL_TRIANGLES, 0, 3);
//unbind for next pass
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers();
}
the geometry and texture coordinates
GLfloat data[] = {
//X Y Z U V
0.0f, 0.8f, 0.0f, 0.5f, 1.0f,
-0.8f, -0.8f, 0.0f, 0.0f, 0.0f,
0.8f, -0.8f, 0.0f, 1.0f, 0.0f
};
VBO and VAO being setup
glGenVertexArrays(1, &cubeVAO);
glBindVertexArray(cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertprog);
glVertexAttribPointer(vertprog, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), NULL);
glEnableVertexAttribArray(verttexprog);
glVertexAttribPointer(verttexprog, 2, GL_FLOAT, GL_TRUE, 5*sizeof(GLfloat), (const GLvoid*)(3*sizeof(GLfloat)));
glBindVertexArray(0);
You have not shown the code where you determine the value of verttexprog. From the code you have currently, I have to assume that verttexprog (this is a terrible variable name, by the way) is uninitialized.
You should initialize verttexprog to glGetAttribLocation (program, "vertTextCoord"); after you link your program. Likewise, do not query uniform locations each frame, the only time they change is after you (re-)link a GLSL program.
Your texture is being loaded correctly, but your texture coordinates are not. If you want to see this in action, you can replace:
finalColor = texture(tex, fragTexCoord);
with:
finalColor = texture(tex, gl_FragCoord.st);
This is not the behavior you want, but it is a great way to show that your texture is loaded fine.