Texture on quad rendered using vertex buffer objects only half transparent - opengl

I'm converting some code from OpenGL 1.3 to OpenGL ES 1.1. It's a 2D game, so it mostly comes down to rendering textures onto quads. There is no immediate mode in OpenGL ES, so I had to use vertex buffer objects instead.
But it seems like only one of the two triangles making up each quad handles transparency. Here's a screenshot:
Here's how I render textured quads right now, which causes that:
glBindTexture2D(GL_TEXTURE_2D, id);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
const GLfloat texture_coordinates[] = {0, 0,
0, 1,
1, 1,
1, 0};
glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);
const GLfloat vertices[] = {0, 0,
0, height,
width, height,
width, 0};
glVertexPointer(2, GL_FLOAT, 0, vertices);
const GLubyte indices[] = {0, 1, 2,
0, 2, 3};
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_BYTE, indices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
And here is how I used to render textured quads using immediate mode, which works fine:
glBindTexture2D(GL_TEXTURE_2D, id);
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(0, 0);
glTexCoord2i(1, 0);
glVertex2i(width, 0);
glTexCoord2i(1, 1);
glVertex2i(width, height);
glTexCoord2i(0, 1);
glVertex2i(0, height);
glEnd();
Below is an example program reproducing the issue.
You can compile it on Linux like this:
g++ `pkg-config --cflags --libs sdl gl libpng` reproduce.cpp
And on Mac OS X like this:
clang++ -framework OpenGL `pkg-config --cflags --libs sdl libpng` reproduce.cpp
Here's a 512x256 transparent PNG image you can save as "transparent.png":
#include <cmath>
#include <cstdio>
#include <iostream>
#include <png.h>
#include <SDL.h>
#include <SDL_main.h>
#include <SDL_opengl.h>
#include <stdexcept>
#include <sstream>
#define USE_VBO 1
struct Pixmap {
int width;
int height;
const unsigned char* data;
GLenum format;
};
Pixmap load_png(const std::string& path)
{
FILE* const file = fopen(path.c_str(), "rb");
if (!file)
throw std::runtime_error("Unable to open " + path);
png_byte header[8];
fread(header, 1, 8, file);
const bool is_png = !png_sig_cmp(header, 0, 8);
if (!is_png)
throw std::runtime_error(path + " is not a PNG");
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (!png)
throw std::runtime_error("Failed to create png struct");
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, (png_infopp) NULL, (png_infopp) NULL);
throw std::runtime_error("Failed to create png info struct");
}
png_infop info_end = png_create_info_struct(png);
if (!info_end) {
png_destroy_read_struct(&png, &info, (png_infopp) NULL);
throw std::runtime_error("Failed to create png info struct");
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, &info_end);
throw std::runtime_error("Error from libpng");
}
png_init_io(png, file);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
int bit_depth;
int color_type;
png_uint_32 image_width, image_height;
png_get_IHDR(png, info, &image_width, &image_height, &bit_depth,
&color_type, NULL, NULL, NULL);
png_read_update_info(png, info);
GLenum format;
switch (color_type) {
case PNG_COLOR_TYPE_RGBA:
format = GL_RGBA;
break;
case PNG_COLOR_TYPE_RGB:
format = GL_RGB;
break;
default:
png_destroy_read_struct(&png, &info, &info_end);
std::ostringstream message_stream;
message_stream << "Unsupported PNG color type: " << color_type;
throw std::runtime_error(message_stream.str());
}
const int row_bytes = png_get_rowbytes(png, info);
png_byte* image_data = new png_byte[row_bytes * image_height];
png_bytep* row_pointers = new png_bytep[image_height];
for (unsigned int i = 0; i < image_height; i++)
row_pointers[i] = image_data + i * row_bytes;
png_read_image(png, row_pointers);
png_destroy_read_struct(&png, &info, &info_end);
delete[] row_pointers;
fclose(file);
Pixmap pixmap;
pixmap.width = image_width;
pixmap.height = image_height;
pixmap.data = image_data;
pixmap.format = format;
return pixmap;
}
GLuint create_texture(Pixmap pixmap)
{
GLuint id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, pixmap.format, pixmap.width,
pixmap.height, 0, pixmap.format, GL_UNSIGNED_BYTE,
pixmap.data);
return id;
}
void draw_texture(const GLuint id, const int width, const int height)
{
glBindTexture(GL_TEXTURE_2D, id);
#if USE_VBO
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
const GLfloat texture_coordinates[] = {0, 0,
0, 1,
1, 1,
1, 0};
glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);
const GLfloat vertices[] = {0, 0,
0, height,
width, height,
width, 0};
glVertexPointer(2, GL_FLOAT, 0, vertices);
const GLubyte indices[] = {0, 1, 2,
0, 2, 3};
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_BYTE, indices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#else
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(0, 0);
glTexCoord2i(1, 0);
glVertex2i(width, 0);
glTexCoord2i(1, 1);
glVertex2i(width, height);
glTexCoord2i(0, 1);
glVertex2i(0, height);
glEnd();
#endif
}
int main(int argc, char* argv[])
{
const int width = 512;
const int height = 256;
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_SetVideoMode(width, height, 0, SDL_OPENGL);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, height, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
try {
Pixmap pixmap = load_png("transparent.png");
GLuint texture = create_texture(pixmap);
draw_texture(texture, pixmap.width, pixmap.height);
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
SDL_GL_SwapBuffers();
SDL_Event event;
for (;;) {
SDL_WaitEvent(&event);
if (event.type == SDL_QUIT)
return 0;
}
return 0;
}

Seeing you use indexed drawing with a simple triangle strip for a quad made me wonder, and in fact this is your problem. Your index array looks like you want to draw two indexed triangles and not a single triangle strip. So you draw a triangle strip with 6 vertices and thus 4 triangles, which means additional triangles that wrap somehow behind your other two and cause double drawing, which then results in the darker parts.
So the easiest solution would be to change GL_TRIANGLE_STRIP to GL_TRIANGLES, but maybe reorder your vertices/indices a bit, as otherwise you are drawing your triangles in clockwise order, whereas your 1.3 quad example uses counter-clockwise order (it may be that this is irrelevant in your case, but that would be the wrong approach in the first place, never ignore your ordering).
But you know what, it's a triangle strip for two triangles, which needs just 4 vertices. So there isn't any need for any index array at all, just use good old glDrawArrays and draw the vertices in order. But reorder them a bit (triangle strips use a zig-zag pattern, so order them from left to right, top to bottom):
const GLfloat texture_coordinates[] = {0, 1,
0, 0,
1, 1,
1, 0};
glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);
const GLfloat vertices[] = {0, height,
0, 0,
width, height,
width, 0};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Related

OpengGL Array Texture shows up distorted

This is my texture
This is how it is showing up
I am only getting this distorted result when using array textures.
Do I need to create mipmaps?
I am using the wrong image type?
bmp's might bot be an idea for images but I am using for the time being for their simplicity.
Code
#include <iostream>
using namespace std;
#include <cstdlib>
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
GLFWwindow* window;
#define WINDOW_HEIGHT 768
#define WINDOW_WIDTH 1024
#include "common/shaders.h"
#include "common/texture.h"
#include "common/controls.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
static void error_callback(int error, const char* description);
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
int main(void)
{
if (!glfwInit()) {
fprintf( stderr, "Failed to initialize GLFW\n" );
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE , GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Ortho", NULL, NULL);
if (!window) {
fprintf(stderr, "Failed to create window\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glfwSetErrorCallback(error_callback);
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetCursorPos(window, WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
if (GL_EXT_texture_array){
fprintf(stderr, "GL_EXT_texture_array\n");
}
glClearColor(0.0f, 0.0f, 255.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
GLuint vai; // vertex array id
glGenVertexArrays(1, &vai);
glBindVertexArray(vai);
char vert[] = "shaders/triangles.vert";
char frag[] = "shaders/triangles.frag";
GLuint program = load_shaders(vert,frag);
GLuint matrix_id = glGetUniformLocation(program, "MVP");
GLuint texture_id = glGetUniformLocation(program, "material");
GLsizei width = 16;
GLsizei height = 16;
GLsizei layerCount = 2;
GLsizei mipLevelCount = 1;
unsigned char* data = bmp_data("tiles.bmp");
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_id);
//GLubyte texels[32] =
//{
////Texels for first image.
//0, 0, 0, 255,
//255, 0, 0, 255,
//0, 255, 0, 255,
//0, 0, 255, 255,
////Texels for second image.
//255, 255, 255, 255,
//255, 255, 0, 255,
//0, 255, 255, 255,
//255, 0, 255, 255,
//};
//glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount);
//glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 16);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGB8, width, height, layerCount);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
delete [] data;
GLuint vbo;
GLuint uvbo;
glGenBuffers(1, &vbo);
glGenBuffers(1, &uvbo);
// prep tile
GLfloat vertex[] = {
0, 1, 0,
1, 1, 0,
1, 0, 0,
0, 1, 0,
0, 0, 0,
1, 0, 0,
};
GLfloat uv[] = {
0.0f , 16.0f,
16.0f, 16.0f,
16.0f, 0.0f ,
0.0f , 16.0f,
0.0f , 0.0f ,
16.0f, 0.0f ,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, uvbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(uv), uv, GL_STATIC_DRAW);
glUseProgram(program);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
computeMatricesFromInputs();
glm::mat4 ProjectionMatrix = getProjectionMatrix();
glm::mat4 ViewMatrix = getViewMatrix();
glm::mat4 ModelMatrix = glm::mat4(1.0);
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
glUniformMatrix4fv(matrix_id, 1, GL_FALSE, &MVP[0][0]);
glUniform1i(texture_id, 0);
// render tile
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
glBindBuffer(GL_ARRAY_BUFFER, uvbo);
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,(void*)0);
glDrawArrays(GL_TRIANGLES, 0, 2*3);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glDeleteProgram(program);
glDeleteVertexArrays(2, &vai);
glfwTerminate();
exit(EXIT_SUCCESS);
}
static void error_callback(int error, const char* description){
fputs(description, stderr);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, 1);
}
fragment shader
#version 400 core
in vec2 uv; // Interpolated values from the vertex shaders
out vec3 color; // Ouput data
uniform sampler2DArray material; // Values that stay constant for the whole mesh.
void main(){
color = texture(material,vec3(uv,0)).rgb;
}
vert shader
#version 400 core
layout(location = 0) in vec3 vertex_pos;
layout(location = 1) in vec2 vertex_uv;
out vec2 uv;
uniform mat4 MVP;
void main(){
gl_Position = MVP * vec4(vertex_pos,1);
uv = vertex_uv;
}
I ended up paying to get someone to solve this problem for me.
I needed to update my texture vertex's. Also I didn't need to call glPixelStorei so I remove that.
GLfloat vertex[] = {
0, 1, 0,
1, 1, 0,
1, 0, 0,
0, 1, 0,
0, 0, 0,
1, 0, 0,
};
A texture files boundaries (uv coordinates) are from 0.0 to 1.0 on the x and y axis. If you want to map the whole texture to a quad you set the four corners to the quad's four corners. The following image is a good example:
http://www.c-jump.com/bcc/common/Talk3/OpenGL/Wk07_texture/const_images/textureMap.png

I can't display a texture on a quad

I am trying to wrap a texture on a quad.
All I see is a white rectangle:
To load the texture I used freeimage.
I need help in order to fix this very simple demo:
#include <GL/glut.h>
#include <GL/gl.h>
#include <FreeImage.h>
#include <stdio.h>
GLfloat coordinates[] =
{
-0.5, 0.5, 1,
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0
};
GLfloat texCoords[] =
{
0, 1,
0, 0,
1, 0,
1, 1
};
BYTE* data;
FIBITMAP* bitmap;
GLuint texture;
void initGlutCallbacks();
void initGL();
void onReshape(int w, int h);
void display();
FIBITMAP* loadTexture(const char* fileName);
int main(int argc, char** argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(512, 512);
glutInitWindowPosition(64, 64);
glutCreateWindow("arrays");
initGlutCallbacks();
initGL();
// texture
bitmap = loadTexture("rufol.png");
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
data = FreeImage_GetBits(bitmap);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
GL_RGBA8, GL_UNSIGNED_BYTE,
data
);
// enable arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// specifying data for the arrays
glVertexPointer
(
3, GL_FLOAT, 0, coordinates
);
glTexCoordPointer
(
2, GL_FLOAT, 0, texCoords
);
glutMainLoop();
}
void initGlutCallbacks(){
glutReshapeFunc(onReshape);
glutDisplayFunc(display);
}
void initGL(){
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glEnable ( GL_TEXTURE_2D );
}
void onReshape(int w, int h){
}
void display(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDrawArrays(GL_QUADS, 0, 4);
glFlush();
glutSwapBuffers();
}
FIBITMAP* loadTexture(const char* fileName){
FIBITMAP *bitmap = FreeImage_Load(FIF_PNG, "rufol.png");
if(bitmap == 0) printf("error loading the image\n");
FIBITMAP *fbitmap = FreeImage_ConvertTo32Bits(bitmap);
FreeImage_Unload(bitmap);
return fbitmap;
}
As you can see I am not even using perspective. Also lighting is not enabled(I don't know if it is required to display textures). I have tested a very similar code but using colors for each vertex instead of texture coordinates and it worked. So I think it might be something wrong when loading the image.
Have you tried using GL_RGBA instead of GL_RGBA8 as second parameter (format)?

Need some hand-holding to render, display and save an image

I've previously asked how I might be able to display my colour buffer and also save it to disk, and the answer I got was that I should;
Render to a FBO and use glReadPixels() to slurp images out of that instead of the front buffer.
How can I generate a screenshot when glReadPixels is empty?
However, I've read a bit about framebuffers and am still totally confused, so I thought I would ask about how to do this on SO. My code does something like this:
/* Callback handler for window re-paint event */
void display()
{
glClear(GL_COLOR_BUFFER_BIT); //Clear the color buffer
glMatrixMode(GL_MODELVIEW); //To operate on the model-view matrix
// do some rendering
glFlush(); // display
}
when I want to save the image at any point, I run this:
std::unique_ptr<RGBA2D> GrabScreen()
{
//we get the width/height of the screen into this array
GLint screen[4];
//get the width/height of the window
glGetIntegerv(GL_VIEWPORT, screen);
GLint width = screen[2];
GLint height = screen[3];
std::unique_ptr<RGBA2D> pixels(new RGBA2D(height, width * 4));
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels->data());
return std::move(pixels);
}
Note that RGBA2D is a 2D eigen vector object (not important). This all works fine, except that it only saves the image if it is being displayed. I want to be able to run my program on a unix machine without a display. I want to render to an FBO. How do I do this?
Not hand-holding, but I can hopefully point you in the right direction.
You will use glGenFramebuffers and glBindFramebuffer to create and bind a Framebuffer Object (FBO).
Then you have a choice if you want to render to a texture or to a renderbuffer. For your purpose, either one will work. The renderbuffer is easier, IMHO. Use glGenRenderbuffers, glBindRenderbuffer and glRenderbufferStorage to set up your color renderbuffer.
Then you attach your color renderbuffer to the FBO, using glFramebufferRenderbuffer.
If you need a depth buffer, repeat the previous two steps to create and attach another renderbuffer used as the depth buffer for your FBO rendering.
Then do your rendering, and grab the frame with glReadPixels.
All these calls are documented in the man pages at www.opengl.org. If you search for the keywords and some of the function names, you should also be able to find some full code samples.
Recently I had a small discussion on the Wayland devel maillist where I wanted to demonstrate how FBOs don't get updated when the X-Server managing the GPU is not holding the VT. Anyway, for demonstration purposes I hacked a quick and dirty program from various sources I had around, that renders to an FBO in a loop and writes the created picture out to a file. It's not optimized for performance, but does what you're interested in, so I drop the source code here (note that the malloc for the readout buffer misses a paired free, so there's a nongrowing memory leak in there)
// framebuffer continuous dump demonstrator
//
// build:
// c++ -o test_fbo test_fbo.cpp -lm -lGL -lGLU -lglut -lGLEW
#include <GL/glew.h>
#include <GL/glut.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
using namespace std;
namespace render
{
int width, height;
float aspect;
void init();
void reshape(int width, int height);
void display();
int const fbo_width = 512;
int const fbo_height = 512;
GLuint fb, color, depth;
void *dumpbuf;
int dumpbuf_fd;
};
void idle();
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutCreateWindow("FBO test");
glutDisplayFunc(render::display);
glutReshapeFunc(render::reshape);
glutIdleFunc(idle);
glewInit();
render::init();
glutMainLoop();
return 0;
}
void idle()
{
glutPostRedisplay();
}
void CHECK_FRAMEBUFFER_STATUS()
{
GLenum status;
status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
/* choose different formats */
break;
default:
/* programming error; will fail on all hardware */
throw "Framebuffer Error";
}
}
namespace render
{
float const light_dir[]={1,1,1,0};
float const light_color[]={1,0.95,0.9,1};
void init()
{
glGenFramebuffers(1, &fb);
glGenTextures(1, &color);
glGenRenderbuffers(1, &depth);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGB8,
fbo_width, fbo_height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbo_width, fbo_height);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
GLint red_bits, green_bits, blue_bits, alpha_bits;
glGetIntegerv(GL_RED_BITS, &red_bits);
glGetIntegerv(GL_GREEN_BITS, &green_bits);
glGetIntegerv(GL_BLUE_BITS, &blue_bits);
glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
fprintf(stderr, "FBO format R%dG%dB%dA%d\n",
(int)red_bits,
(int)green_bits,
(int)blue_bits,
(int)alpha_bits );
CHECK_FRAMEBUFFER_STATUS();
dumpbuf_fd = open("/tmp/fbodump.rgb", O_CREAT|O_SYNC|O_RDWR, S_IRUSR|S_IWUSR);
assert(-1 != dumpbuf_fd);
dumpbuf = malloc(fbo_width*fbo_height*3);
assert(dumpbuf);
}
void reshape(int width, int height)
{
render::width=width;
render::height=height;
aspect=float(width)/float(height);
glutPostRedisplay();
}
void prepare()
{
static float a=0, b=0, c=0;
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glViewport(0,0,fbo_width, fbo_height);
glClearColor(1,1,1,0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glLightfv(GL_LIGHT0, GL_POSITION, light_dir);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glTranslatef(0,0,-5);
glRotatef(a, 1, 0, 0);
glRotatef(b, 0, 1, 0);
glRotatef(c, 0, 0, 1);
glutSolidTeapot(0.75);
a=fmod(a+0.1, 360.);
b=fmod(b+0.5, 360.);
c=fmod(c+0.25, 360.);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0,0,fbo_width,fbo_height,GL_RGB,GL_UNSIGNED_BYTE,dumpbuf);
lseek(dumpbuf_fd, SEEK_SET, 0);
write(dumpbuf_fd, dumpbuf, fbo_width*fbo_height*3);
}
void intermediary()
{
}
void final()
{
static float a=0, b=0, c=0;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0,0, width, height);
glClearColor(1.,1.,1.,0.);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-5);
glRotatef(b, 0, 1, 0);
b=fmod(b+0.5, 360.);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, color);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
float cube[][5]=
{
{-1, -1, -1, 0, 0},
{ 1, -1, -1, 1, 0},
{ 1, 1, -1, 1, 1},
{-1, 1, -1, 0, 1},
{-1, -1, 1, -1, 0},
{ 1, -1, 1, 0, 0},
{ 1, 1, 1, 0, 1},
{-1, 1, 1, -1, 1},
};
unsigned int faces[]=
{
0, 1, 2, 3,
1, 5, 6, 2,
5, 4, 7, 6,
4, 0, 3, 7,
3, 2, 6, 7,
4, 5, 1, 0
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 5*sizeof(float), &cube[0][0]);
glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), &cube[0][3]);
glCullFace(GL_BACK);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glCullFace(GL_FRONT);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
void display()
{
prepare();
intermediary();
final();
glutSwapBuffers();
}
}

What are the steps necessary to render my scene to a Framebuffer Object(FBO) and then render that FBO to the screen?

I've got a fairly complicated scene with many GL_POINTS that I need to render. The scene will be largely static, so I'd like to render it to a Framebuffer Object and then only update that FBO when my scene actually changes. I'd then like to render the FBO to the screen each frame.
I've found examples that render an FBO into a texture. I've found examples that render an FBO into a RenderBuffer (still not quite sure what that is). I'm not sure what the steps are to achieve this. Do I need to render to a texture and the draw the texture to the screen?
Can you please enumerate the steps (ideally even in pseudocode or actual code) to render my scene to an FBO and then draw that FBO to the screen.
draw() is sufficient for a placeholder for my own drawing functions.
I provide a minimal FBO example just for this
Basically the steps are: Create FBO with depth renderbuffer and color texture attachment. To render to FBO unbind the target texture, bind FBO, render to FBO. Unbind FBO, bind texture, render.
#include <GL/glew.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
void init();
void display();
int const fbo_width = 512;
int const fbo_height = 512;
GLuint fb, color, depth;
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutCreateWindow("FBO test");
glutDisplayFunc(display);
glutIdleFunc(glutPostRedisplay);
glewInit();
init();
glutMainLoop();
return 0;
}
void CHECK_FRAMEBUFFER_STATUS()
{
GLenum status;
status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
/* choose different formats */
break;
default:
/* programming error; will fail on all hardware */
fputs("Framebuffer Error\n", stderr);
exit(-1);
}
}
float const light_dir[]={1,1,1,0};
float const light_color[]={1,0.95,0.9,1};
void init()
{
glGenFramebuffers(1, &fb);
glGenTextures(1, &color);
glGenRenderbuffers(1, &depth);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGBA,
fbo_width, fbo_height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbo_width, fbo_height);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
CHECK_FRAMEBUFFER_STATUS();
}
void prepare()
{
static float a=0, b=0, c=0;
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glViewport(0,0, fbo_width, fbo_height);
glClearColor(1,1,1,0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glLightfv(GL_LIGHT0, GL_POSITION, light_dir);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glTranslatef(0,0,-5);
glRotatef(a, 1, 0, 0);
glRotatef(b, 0, 1, 0);
glRotatef(c, 0, 0, 1);
glutSolidTeapot(0.75);
a=fmod(a+0.1, 360.);
b=fmod(b+0.5, 360.);
c=fmod(c+0.25, 360.);
}
void final()
{
static float a=0, b=0, c=0;
const int win_width = glutGet(GLUT_WINDOW_WIDTH);
const int win_height = glutGet(GLUT_WINDOW_HEIGHT);
const float aspect = (float)win_width/(float)win_height;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0,0, win_width, win_height);
glClearColor(1.,1.,1.,0.);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-5);
glRotatef(b, 0, 1, 0);
b=fmod(b+0.5, 360.);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, color);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
float cube[][5]=
{
{-1, -1, -1, 0, 0},
{ 1, -1, -1, 1, 0},
{ 1, 1, -1, 1, 1},
{-1, 1, -1, 0, 1},
{-1, -1, 1, -1, 0},
{ 1, -1, 1, 0, 0},
{ 1, 1, 1, 0, 1},
{-1, 1, 1, -1, 1},
};
unsigned int faces[]=
{
0, 1, 2, 3,
1, 5, 6, 2,
5, 4, 7, 6,
4, 0, 3, 7,
3, 2, 6, 7,
4, 5, 1, 0
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 5*sizeof(float), &cube[0][0]);
glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), &cube[0][3]);
glCullFace(GL_BACK);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glCullFace(GL_FRONT);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
void display()
{
prepare();
final();
glutSwapBuffers();
}
Here is alternative example which does not require textures:
// copy framebuffer
if(fboUsed)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT,
0, 0, screenWidth, screenHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
Replace variables in blit with your own.
Apperently frame buffer 0 is front buffer.
fboId is your frame buffer number.

Optimize OpenGL Fragment Shader

I have a vertex/fragment shader that draws a rectangle performing many calculations based on a texture. (It blends pixels, modifies them, etc). The thing is that each rectangle and the pixels it contains will not change. I only move the entire rectangle(s) around and zoom them.
Is there any way to optimize the fragment shader since the rectangles do not really need to be recomputed?
So, if I understood you correctly you compute those rectangles once and then want to reuse them? This kind of task is solved by rendering to a texture and then use the generated textures further on.
Render to texture is easiest done through Framebuffer Objects.
EDIT: A simple example for using FBO to render to texture
// test_fbo_teapot.cpp
#include <GL/glew.h> // Uses GLEW for extension loading
#include <GL/glut.h> // Uses GLUT as framework
// Check those are on your system for compilation
// and if not please install them.
#include <cmath>
#include <iostream>
using namespace std;
namespace render
{
int width, height;
float aspect;
void init();
void reshape(int width, int height);
void display();
int const fbo_width = 512;
int const fbo_height = 512;
GLuint fb, color, depth;
};
void idle();
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutCreateWindow("FBO test");
glutDisplayFunc(render::display);
glutReshapeFunc(render::reshape);
glutIdleFunc(idle);
glewInit();
render::init();
glutMainLoop();
return 0;
}
void idle()
{
glutPostRedisplay();
}
void CHECK_FRAMEBUFFER_STATUS()
{
GLenum status;
status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
/* choose different formats */
break;
default:
/* programming error; will fail on all hardware */
throw "Framebuffer Error";
}
}
namespace render
{
float const light_dir[]={1,1,1,0};
float const light_color[]={1,0.95,0.9,1};
void init()
{
glGenFramebuffers(1, &fb);
glGenTextures(1, &color);
glGenRenderbuffers(1, &depth);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGBA,
fbo_width, fbo_height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbo_width, fbo_height);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
CHECK_FRAMEBUFFER_STATUS();
}
void reshape(int width, int height)
{
render::width=width;
render::height=height;
aspect=float(width)/float(height);
glutPostRedisplay();
}
void prepare()
{
static float a=0, b=0, c=0;
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
glViewport(0,0,fbo_width, fbo_height);
glClearColor(1,1,1,0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glLightfv(GL_LIGHT0, GL_POSITION, light_dir);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glTranslatef(0,0,-5);
glRotatef(a, 1, 0, 0);
glRotatef(b, 0, 1, 0);
glRotatef(c, 0, 0, 1);
glutSolidTeapot(0.75);
a=fmod(a+0.1, 360.);
b=fmod(b+0.5, 360.);
c=fmod(c+0.25, 360.);
}
void intermediary()
{
}
void final()
{
static float a=0, b=0, c=0;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0,0, width, height);
glClearColor(1,1,1,1);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-5);
glRotatef(b, 0, 1, 0);
b=fmod(b+0.5, 360.);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, color);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
float cube[][5]=
{
{-1, -1, -1, 0, 0},
{ 1, -1, -1, 1, 0},
{ 1, 1, -1, 1, 1},
{-1, 1, -1, 0, 1},
{-1, -1, 1, -1, 0},
{ 1, -1, 1, 0, 0},
{ 1, 1, 1, 0, 1},
{-1, 1, 1, -1, 1},
};
unsigned int faces[]=
{
0, 1, 2, 3,
1, 5, 6, 2,
5, 4, 7, 6,
4, 0, 3, 7,
3, 2, 6, 7,
4, 5, 1, 0
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 5*sizeof(float), &cube[0][0]);
glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), &cube[0][3]);
glCullFace(GL_BACK);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glCullFace(GL_FRONT);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
void display()
{
prepare();
intermediary();
final();
glutSwapBuffers();
}
};