Trouble displaying texture in OpenGL ES with C++ - c++

I'm trying to write a small, portable 2D engine for iOS to learn C++ and OpenGL. Right now I'm trying to display a texture that I've loaded in. I've been successful displaying a texture when loading in the with CoreGraphic libraries, but now I'm trying to load the file in C++ with fread and libpng and all I see is a white box.
My texture is 64x64 so it's not a power of 2 problem. Also I have enabled GL_TEXTURE_2D.
The first block of code is used to load the png, convert the png to image data, and load the data into OpenGL.
void AssetManager::loadImage(const std::string &filename)
{
Texture texture(filename);
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_bytep *row_pointers = NULL;
int bitDepth, colourType;
FILE *pngFile = fopen(std::string(Game::environmentData.basePath + "/" + filename).c_str(), "rb");
if(!pngFile)
return;
png_byte sig[8];
fread(&sig, 8, sizeof(png_byte), pngFile);
rewind(pngFile);//so when we init io it won't bitch
if(!png_check_sig(sig, 8))
return;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
if(!png_ptr)
return;
if(setjmp(png_jmpbuf(png_ptr)))
return;
info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr)
return;
png_init_io(png_ptr, pngFile);
png_read_info(png_ptr, info_ptr);
bitDepth = png_get_bit_depth(png_ptr, info_ptr);
colourType = png_get_color_type(png_ptr, info_ptr);
if(colourType == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
/*if(colourType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
png_set_gray_1_2_4_to_8(png_ptr);*/
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
if(bitDepth == 16)
png_set_strip_16(png_ptr);
else if(bitDepth < 8)
png_set_packing(png_ptr);
png_read_update_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &texture.width, &texture.height,
&bitDepth, &colourType, NULL, NULL, NULL);
int components = GetTextureInfo(colourType);
if(components == -1)
{
if(png_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return;
}
GLubyte *pixels = (GLubyte *)malloc(sizeof(GLubyte) * (texture.width * texture.height * components));
row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * texture.height);
for(int i = 0; i < texture.height; ++i)
row_pointers[i] = (png_bytep)(pixels + (i * texture.width * components));
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, NULL);
// make it
glGenTextures(1, &texture.name);
// bind it
glBindTexture(GL_TEXTURE_2D, texture.name);
GLuint glcolours;
switch (components) {
case 1:
glcolours = GL_LUMINANCE;
break;
case 2:
glcolours = GL_LUMINANCE_ALPHA;
break;
case 3:
glcolours = GL_RGB;
break;
case 4:
glcolours = GL_RGBA;
break;
}
glTexImage2D(GL_TEXTURE_2D, 0, components, texture.width, texture.height, 0, glcolours, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(pngFile);
free(row_pointers);
free(pixels);
textures.push_back(texture);
}
Here is the code for my Texture class:
class Texture
{
public:
Texture(const std::string &filename) { this->filename = filename; }
unsigned int name;
unsigned int size;
unsigned int width;
unsigned int height;
std::string filename;
};
Here is how I setup my view:
void Renderer::Setup(Rectangle rect, CameraType cameraType)
{
_viewRect = rect;
glViewport(0,0,_viewRect.width,_viewRect.height);
if(cameraType == Renderer::Orthographic)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(_viewRect.x,_viewRect.width,_viewRect.y,_viewRect.height,kZNear,kZFar);
glMatrixMode(GL_MODELVIEW);
}
else
{
GLfloat size = kZNear * tanf(DegreesToRadians(kFieldOfView) / 2.0);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustumf(-size, size, -size / (_viewRect.width / _viewRect.height), size / (_viewRect.width / _viewRect.height), kZNear, kZFar);
glMatrixMode(GL_MODELVIEW);
}
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
glEnable(GL_BLEND);
}
Now here is where I draw the texture:
void Renderer::DrawTexture(int x, int y, Texture &texture)
{
GLfloat vertices[] = {
x, _viewRect.height - y, 0.0,
x, _viewRect.height - (y + texture.height), 0.0,
x + texture.width, _viewRect.height - y, 0.0,
x + texture.width, _viewRect.height - (y + texture.height), 0.0
};
static const GLfloat normals[] = {
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0
};
GLfloat texCoords[] = {
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glLoadIdentity();
glTranslatef(0.0, 0.0, -3.0);
glBindTexture(GL_TEXTURE_2D, texture.name);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glNormalPointer(GL_FLOAT, 0, normals);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
UPDATE:
I changed the function I use to generate a texture. It now create a random test texture. I still see a white texture no matter what. Gonna keep digging into how I'm renderering.
Here's the new function:
void AssetManager::CreateNoisyTexture(const char * key)
{
Texture texture(key, 64, 64);
const unsigned int components = 4;
GLubyte *pixels = (GLubyte *)malloc(sizeof(GLubyte) * (texture.width * texture.height * components));
GLubyte *pitr1 = pixels;
GLubyte *pitr2 = pixels + (texture.width * texture.height * components);
while (pitr1 != pitr2) {
*pitr1 = rand() * 0xFF;
*(pitr1 + 1) = rand() * 0xFF;
*(pitr1 + 2) = rand() * 0xFF;
*(pitr1 + 3) = 0xFF;
pitr1 += 4;
}
glGenTextures(1, &texture.name);
glBindTexture(GL_TEXTURE_2D, texture.name);
glTexImage2D(GL_TEXTURE_2D, 0, components, texture.width, texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
free(pixels);
printf("Created texture with key: %s name: %d", texture.key, texture.name);
textures.push_back(texture);
}

Ok I figured it out. The problem was that I was using the wrong internal pixel format.
glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width, texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
Should be:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
I needed to pass in GL_RGBA instead of 4. In the docs I read this:
internalFormat - Specifies the number of color components in the texture. Must be 1, 2, 3, or 4, or one of the following symbolic constants:
I figured it didn't matter but I guess it does. One other thing to note, I figured this out by using glGetError() to figure out where the error was occurring. When I call glTexImage2D() the error was GL_INVALID_OPERATION.
Thanks for your help IronMensan!
UPDATE:
So the reason why I couldn't send 4 is because it is not allowed and internalFormat and format need to be the same in the OpenGL ES 1.1 spec. You can only send GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, or GL_LUMINANCE_ALPHA. Lesson learned.

Related

OpenGL drawing textured quad as a solid colour

I would like to generate a texture every update loop and display it on a quad. I've looked at numerous tutorials and can only find segments of code instead of complete examples.
First I define the texture and create the data.
glGenTextures(1, &textureID);
auto *data = new unsigned char[CHUNK_SIZE * CHUNK_SIZE * 4];
// Element* elements[4096];
for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * 4; i += 4) {
Element *element = elements[i / 4];
if (element != nullptr) {
data[i] = element->r * 255;
data[i + 1] = element->g * 255;
data[i + 2] = element->b * 255;
data[i + 3] = element->a * 255;
} else {
data[i] = 0;
data[i + 1] = 0;
data[i + 2] = 0;
data[i + 3] = 0;
}
}
Then I draw it.
glPushMatrix();
glTranslatef(0.5f + chunkX * CHUNK_SIZE, 0.5f + chunkY * CHUNK_SIZE, 0);
glEnable(GL_TEXTURE);
glBindTexture(GL_TEXTURE_2D, textureID);
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_RGBA8, CHUNK_SIZE, CHUNK_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBegin(GL_QUADS);
glTexCoord2d(0, 0);
glVertex2d(0, 0);
glTexCoord2d(1, 0);
glVertex2d(CHUNK_SIZE, 0);
glTexCoord2d(1, 1);
glVertex2d(CHUNK_SIZE, CHUNK_SIZE);
glTexCoord2d(0, 1);
glVertex2d(0, CHUNK_SIZE);
glEnd();
glPopMatrix();
The quad is rendering in the correct positions but it is solid red.
What is the correct way of generating a texture at runtime and drawing it?
The texture wasn't displaying as I was enabling GL_TEXTURE instead of GL_TEXTURE_2D.
glEnable(GL_TEXTURE_2D);
This made the textures render however they were faded and seemingly affecting other textures. After adding glColor3f(1,1,1); before rendering the quads it fixed this.
glPushMatrix();
glTranslatef(0.5f + chunkX * CHUNK_SIZE, 0.5f + chunkY * CHUNK_SIZE, 0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, whiteTextureID);
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_RGBA8, CHUNK_SIZE, CHUNK_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glColor3f(1,1,1);
glBegin(GL_QUADS);
glTexCoord2d(0, 0);
glVertex2d(0, 0);
glTexCoord2d(1, 0);
glVertex2d(CHUNK_SIZE, 0);
glTexCoord2d(1, 1);
glVertex2d(CHUNK_SIZE, CHUNK_SIZE);
glTexCoord2d(0, 1);
glVertex2d(0, CHUNK_SIZE);
glEnd();
glPopMatrix();

How to create bitmap font texture in OpenGL?

So I was trying to learn OpenGL from learnopengl.com and I'm currently on the topic of text rendering and I thought about combining scalable font textures in my program to one big texture, but for some reason, I get an exception bc of glCopyImageSubData(...).
First I try to measure how big the texture should be and then copy textures I already created to one big texture, and I was playing with this function for quite a while now but I can't find a solution.
Here is the original code with texture used for each face.
I tried to create fbo and attach a texture to it, but after research, I found this function that was far more clear to me so I decided to use it instead.
So I added xoffset to Character structure:
struct Character {
GLuint textureID;
glm::ivec2 size;
glm::ivec2 bearing;
GLuint advance;
GLuint xoffset;
};
I add this offset to each face in the first for loop:
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x,
font_texture_width
};
font_texture_width += character.size.x;
characters.insert(std::pair<GLchar, Character>(c, character));
And then I try to paste each face texture to fontTexture:
glGenTextures(1, &fontTexture);
glBindTexture(GL_TEXTURE_2D, fontTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,
font_texture_width, 100, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
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);
std::cout << glGetError();
for (GLubyte c = 0; c < 128; ++c)
glCopyImageSubData( characters[c].textureID, GL_TEXTURE_2D, 0, 0, 0, 0,
fontTexture, GL_TEXTURE_2D, 0, characters[c].xoffset, 0, 0,
characters[c].size.x, characters[c].size.y, 0);
Here is modyfied renderText function:
void renderText(Shader &s, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)
{
s.use();
s.setVec3("textColor", color);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
std::string::const_iterator c;
glBindTexture(GL_TEXTURE_2D, fontTexture);
for (c = text.begin(); c != text.end(); ++c)
{
Character ch = characters[*c];
GLfloat xpos = x + ch.bearing.x * scale;
GLfloat ypos = y - (ch.size.y - ch.bearing.y) * scale;
GLfloat w = ch.size.x * scale;
GLfloat h = ch.size.y * scale;
GLfloat vertices[6][4] = {
{ xpos + characters[*c].xoffset, ypos + h, 0.0, 0.0 },
{ xpos + characters[*c].xoffset, ypos, 0.0, 1.0 },
{ xpos + w + characters[*c].xoffset, ypos, 1.0, 1.0 },
{ xpos + characters[*c].xoffset, ypos + h, 0.0, 0.0 },
{ xpos + w + characters[*c].xoffset, ypos, 1.0, 1.0 },
{ xpos + w + characters[*c].xoffset, ypos + h, 1.0, 0.0 }
};
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6) * scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Exception is being thrown where I call glCopyImageSubData.
Here is my whole program: https://pastebin.com/vAeeX3Xh
EDIT:
Now I figured out that it would be better to use glTexSubImage2D instead, so this is how it looks like (instead of this block of code with glCopyImageSubData)
glGenTextures(1, &fontTexture);
glBindTexture(GL_TEXTURE_2D, fontTexture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width * 250,
face->glyph->bitmap.rows * 250,
0,
GL_RED,
GL_UNSIGNED_BYTE,
NULL
);
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);
for (GLubyte c = 0; c < 128; ++c)
{
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl;
continue;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, characters[c].xoffset, 0, characters[c].size.x, characters[c].size.y, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
}
Now when I render this texture it looks like this:
https://imgur.com/a/A0gDy6T

OpenGL rendering multiple textures on sprites

I'm learning OpenGL from the MakingGamesWithBen series and I'm writing a simple asteroid shooter based on his engine. I have created a system that randomly positions the asteroid sprites with random sizing, and selects a random texture path from an std::vector, and passes the path to the asteroid constructor. The sprites are drawn, however only the first texture is drawn. I've read that I need to bind those textures and switch to the relevant glActiveTexture; from my code below, how would I go about this?
void MainGame::prepareTextures() {
//compile shaders and get Texlocations
initShaders("Shaders/background.vert", "Shaders/background.frag");
GLint TexLoc = _colorProgram.getUniformLocation("bgTexture");
glActiveTexture(GL_TEXTURE0);
}
m_asteroid[i].draw():
glm::vec4 uv(0.0f, 0.0f, 1.0f, 1.0f);
//convert m_imgNum to string and remove trailing zeros
std::string strImgNum = std::to_string(m_imgNum);
strImgNum.erase(strImgNum.find_last_not_of('0') + 1, std::string::npos);
//construct filpath
std::string filePath = m_dir + strImgNum + ".png";
static Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
Engine::Color color;
color.r = 255;
color.g = 255;
color.b = 255;
color.a = 255;
glm::vec4 posAndSize = glm::vec4(m_posX, m_posY, m_width, m_height);
spriteBatch.Draw(posAndSize, uv, texture.id, 0.0f, color);
Engine::ResourceManager::GetTexture():
GLTexture texture = {};
unsigned char *imageData = stbi_load(filePath.c_str(), &width, &height, &bitsPerPixel, forceBpp);
if (imageData == NULL) {
const char *loadError = stbi_failure_reason();
stbi_image_free(imageData);
fatalError(loadError);
}
//Create the texture in opengl
glGenTextures(1, &(texture.id));
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(imageData);
glBindTexture(GL_TEXTURE_2D, 0);
texture.width = width;
texture.height = height;
return texture;
renderbatch():
void SpriteBatch::renderbatch() {
glBindVertexArray(m_vao);
for (unsigned int i = 0; i < m_renderBatches.size(); i++) {
glBindTexture(GL_TEXTURE_2D, m_renderBatches[i].texture);
glDrawArrays(GL_TRIANGLES, m_renderBatches[i].offset, m_renderBatches[i].numVertices);
}
glBindVertexArray(0);
}
I can provide any other code/clarification that may be needed!
Usually when not all textures are showing up, it's probably has something to do with uploading the texture by OpenGL. You could try to debug this by checking if your textures are all uploaded properly;
std::uint32_t textureId = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4).id;
std::cout << "texture id, should not be 0 :" << textureId << std::endl;
This could happen if you called this function not from a thread with OpenGL context.
EDIT:
Is there any reason to use a static object here?
static Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
try changing that to just
Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
UPDATE:
I just replaced your spitebatch.cpp/.h with the ones from Ben's github and put a simple test in your MainGame.cpp;
m_spriteBatch.begin();
asteroids.draw(m_spriteBatch);
ship.draw(m_spriteBatch);
m_spriteBatch.end();
m_spriteBatch.renderBatch();
_colorProgram.unuse();
_window.SwapBuffers();
and I can render the two uploaded textures properly;
HTH.

Problems with Frambuffer Object - limitation related to my screen resolution

I need to have an image as output of my code (dimension: 3507x3281) composed by two triangles (upper-left corner and lower-right corner), each triangle will give me one different color/image on my output.
I read both images and bound them with 2 textures (texture_1 and texture_2).
Afterwards I created both triangles related to their respective image.
I created and bound the Framebuffer Object and attached the textures. Finally, I saved the content to a image file (.ppm).
PROBLEM:
My result (output image) is coherent and geometrically correct. However, only part of my output image is visible. The part that it is visible has 1366x768 as dimension, which is exactely my screen resolution. Am I missing something on the code?
Is there anything related to the options "GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER and GL_FRAMEBUFFER"? Only "GL_DRAW_FRAMEBUFFER" gives the coherent result.
Am I really using the Framebuffer? Because, I read that since you use the FBO this kind of problem does not appear.
CODE:
#include <windows.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/freeglut.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cstdio>
#include <string>
/// Definitions
#define checkImageWidth 500 /// for image - texture 1
#define checkImageHeight 500
#define width 2464 /// for image - texture 2
#define height 1648
static GLuint texture_1, texture_2;
static GLubyte checkImage[checkImageHeight][checkImageWidth][3];
GLuint fb = 1; ///Frame-buffer Object
/// Headers
GLuint raw_texture_load();
void FBO_2_PPM_file(int output_width, int output_height);
/// Functions
GLuint raw_texture_load()
{
/// image used for texture 1
unsigned char *data;
FILE *file;
// open texture data
file = fopen("C:\\Dataset\\image_1.raw", "rb");
if (file == NULL) return 0;
// allocate buffer
data = (unsigned char*) malloc(width * height * 3);
// read texture data
fread(data, width * height * 3, 1, file);
fclose(file);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/// Texture 1
// allocate a texture name
glGenTextures(1, &texture_1);
// select our current texture
glBindTexture(GL_TEXTURE_2D, texture_1);
// select modulate to mix texture with color for shading
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_DECAL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_DECAL);
// when texture area is small, bilinear filter the closest mipmap
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// when texture area is large, bilinear filter the first mipmap
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// texture should tile
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// build our texture mipmaps
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
// free buffer
free(data);
/// image used for texture 2
FILE *image_4tx2;
if( (image_4tx2 = fopen("C:\\dataset\\image2.ppm","r")) == NULL )
{
printf("\n Problem with input for texture 2\n");
system("pause");
exit(1);
}
int i, j, max_ND, n_col, n_lin, Red, G, B;
char s1[20];
/// Read header
fscanf(image_4tx2,"%s",&s1);
fscanf(image_4tx2,"%s %s %s %s",&s1, &s1, &s1, &s1);
fscanf(image_4tx2,"%d %d",&n_col, &n_lin);
fscanf(image_4tx2,"%d",&max_ND);
for (i = 0; i < checkImageWidth; i++)
{
for (j = 0; j < checkImageHeight; j++)
{
fscanf(image_4tx2,"%d %d %d ",&Red, &G, &B);
checkImage[i][j][0] = (GLubyte) Red;
checkImage[i][j][1] = (GLubyte) G;
checkImage[i][j][2] = (GLubyte) B;
}
}
fclose(image_4tx2);
/// Texture 2
glGenTextures(2, &texture_2);
glBindTexture(GL_TEXTURE_2D, texture_2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, checkImageWidth, checkImageHeight,
0, GL_RGB, GL_UNSIGNED_BYTE, checkImage);
return texture_1;
}
void render()
{
GLdouble COORD_1[3], COORD_2[3], COORD_3[3], COORD[3];
/// Output image size
double col_siz_double = 3507.0;
double row_siz_double = 3281.0;
int col_siz_output_image = 3507;
int row_siz_output_image = 3281;
/// Aspect ratio
double ratio_col_row = col_siz_double/row_siz_double;
double aspect_E, aspect_N;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(ratio_col_row>=0.0)
{
aspect_E = 1.0;
aspect_N = 1.0/ratio_col_row;
glOrtho(0.0, 1.0*aspect_E, 0.0, 1.0*aspect_N, 0.0, 1.0);
}
else
{
aspect_E = 1.0/ratio_col_row;
aspect_N = 1.0;
glOrtho(0.0, 1.0*aspect_E, 0.0, 1.0*aspect_N, 0.0, 1.0);
}
gluLookAt( 0.0, 0.0, 1.0, /* eye */
0.0, 0.0, 0.0, /* center */
0, 1, 0); /* up */
/// Viewport
glViewport(0,0,col_siz_output_image,row_siz_output_image);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
/// Triangle 1 (upper-left half of the output)
glBindTexture(GL_TEXTURE_2D, texture_1);
glBegin(GL_TRIANGLES);
glTexCoord2d(0.0, 0.0);
COORD[0]= 0.0*aspect_E;
COORD[1]= 0.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glTexCoord2d(1.0, 1.0);
COORD[0]= 1.0*aspect_E;
COORD[1]= 1.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glTexCoord2d(0.0, 1.0);
COORD[0]= 0.0*aspect_E;
COORD[1]= 1.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glEnd();
/// Triangle 2 (low-right half of the output)
glBindTexture(GL_TEXTURE_2D, texture_2);
glBegin(GL_TRIANGLES);
glTexCoord2d(0.0, 0.0);
COORD[0]= 0.0*aspect_E;
COORD[1]= 0.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glTexCoord2d(1.0, 1.0);
COORD[0]= 1.0*aspect_E;
COORD[1]= 1.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glTexCoord2d(1.0, 0.0);
COORD[0]= 1.0*aspect_E;
COORD[1]= 0.0*aspect_N;
COORD[2]= 1.0;
glVertex3d(COORD[0], COORD[1], COORD[2]);
glEnd();
glFlush();
glDisable(GL_TEXTURE_2D);
static const GLenum draw_buffers[] =
{
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1
};
/// Generating and Biding the Framebuffer Object (FBO)
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
///Attaching both 2D texture to the FBO as color attachments
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, draw_buffers[0], GL_TEXTURE_2D, texture_2, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, draw_buffers[1], GL_TEXTURE_2D, texture_1, 0);
/// Specifying a list of color buffers to be drawn into
glDrawBuffers(2, draw_buffers);
/// Checking the FBO status
GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if( status != GL_FRAMEBUFFER_COMPLETE)
{
printf("\n\nFramebuffer status: %s\n\n",status);
system("pause");
}
/// Saving the results from the FBO (fb) to a PPM image file
FBO_2_PPM_file(col_siz_output_image,row_siz_output_image);
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
texture_1 = raw_texture_load();
render();
}
void FBO_2_PPM_file(int output_width, int output_height)
{
FILE *output_image;
/// READ THE PIXELS VALUES from FBO AND SAVE TO A .PPM FILE
int i, j, k;
unsigned char *pixels = (unsigned char*)malloc(output_width*output_height*4);
/// READ THE CONTENT FROM THE FBO
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT1);
glReadPixels(0, 0, output_width, output_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
output_image = fopen("C:\\Dataset\\output.ppm", "wt");
if(output_image == NULL)
{
printf("Problem # output file!\n\n");
system("pause");
}
fprintf(output_image,"P3\n");
fprintf(output_image,"# Created by Ricao\n");
fprintf(output_image,"%d %d\n",output_width,output_height);
fprintf(output_image,"255\n");
k = 0;
for(i=output_height-1; i>-1; i--)
{
for(j=0; j<output_width; j++)
{
k = (i*output_width)+j;
/// saving only RGB
fprintf(output_image,"%u %u %u ",(unsigned int)pixels[4*k],(unsigned int)pixels[(4*k)+1],
(unsigned int)pixels[(4*k)+2]);
}
fprintf(output_image,"\n");
}
free(pixels);
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(3507, 3281);
glutCreateWindow("Do not close this window!");
glutHideWindow();
/// Checking GLEW library
glewExperimental=TRUE;
GLenum err=glewInit();
if(err!=GLEW_OK)
{
printf("glewInit failed, aborting.");
printf("\tError: %s\n\n",glewGetErrorString(err));
system("pause");
}
if (GLEW_EXT_framebuffer_object != GL_TRUE)
{
printf("\n\n\t ** Error! GLEW_EXT_framebuffer_object != GL_TRUE \n\n");
system("pause");
}
/// To check the max size of the Framebuffer
int dims[2];
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &dims[0]);
///Executing the rendering process
init();
return 0;

Opengl texturing (with mimap or not) from a FBO

I must be missing something obvious in using FBO :
I call TMyForm::Init() once at the start of my application :
class TMyForm
{ ...
private:
Gluint mTextureId, mFboId;
int mWidth, mHeight;
}
void TMyForm::Init()
{
mWidth = 1920;
mHeight = 1080;
...
// create a texture object
glEnable(GL_TEXTURE_2D);
glClearColor ( 0.0, 0.0, 0.0, 1.0 );
glGenTextures(1, &mTextureId);
glBindTexture(GL_TEXTURE_2D, mTextureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create a framebuffer object
glGenFramebuffersEXT(1, &mFboId);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFboId);
// attach the texture to FBO color attachment point
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mTextureId, 0);
// switch back to window-system-provided framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
And in the Draw() function I send the buffer to a card (the rendered scene is ok) and to a preview window (all black or all white depending if I switch back to default window context or the fbo context) :
void TMyForm::Draw()
{
// set rendering destination to FBO
glEnable(GL_TEXTURE_2D);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFboId);
//---
glViewport(-1920, -1080, 1920 * 2, 1080 * 2);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(double(-iWidth)*viewport_ratio, double(iWidth)*viewport_ratio, double(-iHeight), double(iHeight), 1000.0, 100000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Render();
glFlush();
//--- 1 : send to card
delete[] mBufferPlayout;
mBufferPlayout = NULL;
try
{
mBufferPlayout = new GLubyte [mWidth * mHeight * 4];
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0,0, mWidth, mHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, mBufferPlayout);
DisplayFrame(mBufferPlayout);
}
catch (TSCDbException &e) { ShowMessage(GetLastError()); }
//--- 2 : Texturing to the preview window :
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, mTextureId);
//glGenerateMipmapEXT(GL_TEXTURE_2D);
int iWidthPreview = mWidth / 2; // ie 960
int iHeightPreview = mHeight / 2; // ie 540
glViewport(-iWidthPreview, -iHeightPreview, iWidthPreview * 2, iHeightPreview * 2);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(double(-iWidthPreview), double(iWidthPreview), double(-iHeightPreview), double(iHeightPreview), 1000.0, 100000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -50000.0);
mxIncrust0 = 0.0; myIncrust0 = 0.0;
mxIncrust1 = iWidthPreview; myIncrust1 = iHeightPreview;
glBegin(GL_QUADS);
glColor4ub(255,255,255,255);
glTexCoord2d(0.0, 0.0); glVertex2d(mxIncrust0, myIncrust0);
glTexCoord2d(0.0, 1.0); glVertex2d(mxIncrust0, myIncrust1);
glTexCoord2d(1.0,1.0); glVertex2d(mxIncrust1, myIncrust1);
glTexCoord2d(1.0,0.0); glVertex2d(mxIncrust1, myIncrust0);
glEnd();
glDisable(GL_TEXTURE_2D);
glFlush();
//---
SwapBuffers(ghDC);
}
So my problem is the "part 2" which does not do what I wish : texturing the fbo content to the current window.
I tried changing the "glTexCoord2d" with no success.