Pass a high amount of textures to shader in OpenGL - c++

I am using OpenGL version string: 4.6 (Compatibility Profile) Mesa 21.3.5. I load objects from an .obj file with matching textures, 51 textures to be exact. To be able to match various textures to various triangles and surfaces, I am adding texture coordinates along with an texture identifier in my vertex array buffer.
So now I use a stride looking like this:
x y z u v r g b id which clarifies with: pos tex col id.
The problem now is that I can generate textures and bind them to corresponding texture units, but I cannot select each texture in my shader in regards to the texture unit id I get from my texture array buffer. To clarify: I generate and bind textures like so:
void loadTextures(void) {
std::cout << "Size: " << this->materialLib.materials.size() << std::endl;
std::cout << "Loading textures" << std::endl;
for (long unsigned int i = 0; i < this->materialLib.materials.size(); i++) {
Material m = this->materialLib.materials[i];
// init
struct TEXTURE *t = (struct TEXTURE*)malloc(sizeof(struct TEXTURE));
t->data = NULL;
t->path = (char*)malloc(sizeof(char) * (m.ambient_map.length() + 1)); // add one to include \0 character
t->texture_int = 0;
strcpy(t->path, m.ambient_map.c_str());
t->data = stbi_load( t->path, &t->width, &t->height, &t->nrChannels, 0);
textures.push_back(t);
std::cout << '\r';
std::cout << i << "/" << this->materialLib.materials.size();
std::cout.flush(); // see wintermute's comment
}
}
void applyTextures(Shader ourShader) {
for (long unsigned int i = 0; i < textures.size(); i++) {
struct TEXTURE *t = textures[i];
glGenTextures( 1, &t->texture_int );
glBindTexture( GL_TEXTURE_2D, t->texture_int );
// set the texture wrapping parameters
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
// set texture filtering parameters
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
// load image, create texture and generate mipmap
//stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
if (t->data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, t->width, t->height, 0, GL_RGB, GL_UNSIGNED_BYTE, t->data);
glGenerateMipmap(GL_TEXTURE_2D);
} else
std::cout << "ERROR::LOAD::TEXTURE " << t->path << " " << i << std::endl;
}
return;
/* This could be optimized by moving this to the above loop but we'll separate for simplicity for now */
GLint gl_textures[MAX_TEXTURES];
ourShader.use();
for (long unsigned int i = 0; i < textures.size() && i < MAX_TEXTURES; i++) {
gl_textures[i] = i;
// set texture unit as a uniform for the fragment shader, could be set by ourShader.setInt()
glUniform1i(glGetUniformLocation(ourShader.ID, "texture" + (1 + i)), i); // 'i' is our texture id, for another texture, use another id
}
// set texture units as a uniform for the fragment shader
glUniform1iv(glGetUniformLocation(ourShader.ID, "textures"), MAX_TEXTURES, gl_textures);
}
void bindTextures(void) {
for (long unsigned int i = 0; i < textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, textures[i]->texture_int);
}
}
This gives me varius uniforms in my fragment shader that I can select like so:
uniform sampler2D texture1;
uniform sampler2D texture2;
...
FragColor = texture(texture2, TexCoord);
I want to be able to select a texture like this:
uniform sampler2D texures;
...
FragColor = texture(textures[id], TexCoord);
What I have tried is to pass the id as the first argument to "texture()" but that gave me an error, I also tried to cast my id as a sampler2D type but that didn't work either.
How can I fix this?

Related

How to use GL_TEXTURE_2D_ARRAY with stb_image

I'm trying to create sprite animation with texture array. Right now I have follow code:
int width, height, depth;
stbi_set_flip_vertically_on_load(true);
byte_t* buffer = stbi_load(R"(.\fire.jpg)",
&width, &height, &depth, STBI_rgb_alpha);
if (buffer == nullptr) {
std::cerr << "Could not read texture" << std::endl;
return EXIT_FAILURE;
}
GLuint texture_id;
const GLenum target = GL_TEXTURE_2D_ARRAY;
SAFE_CALL(glGenTextures(1, &texture_id));
SAFE_CALL(glBindTexture(target, texture_id));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 1));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
const GLsizei tile_w_count = 6, tile_h_count = 6;
const GLsizei total_tiles = tile_w_count * tile_h_count;
const GLsizei tile_w = width / tile_w_count,
tile_h = height / tile_h_count;
std::cout << "Texture WxH: " << width << "x" << height;
std::cout << ", Tile WxH: " << tile_w << "x" << tile_h << std::endl;
SAFE_CALL(glTexStorage3D(target, 1, GL_RGBA8, tile_w, tile_h,
total_tiles));
SAFE_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, width));
SAFE_CALL(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, height));
for (GLsizei i = 0; i < total_tiles; ++i) {
SAFE_CALL(glTexSubImage3D(
GL_TEXTURE_2D_ARRAY,
0,
0, 0, i,
tile_w, tile_h, 1,
GL_RGBA,
GL_UNSIGNED_BYTE,
buffer + (i * tile_w * tile_h * depth)
));
}
Fragment shader:
#version 460 core
in vec2 tex_coords;
out vec4 frag_color;
uniform sampler2DArray texture_0;
uniform int current_frame;
uniform int total_frames;
float actual_layer() {
return max(0, min(total_frames - 1,
floor(current_frame + 0.5)));
}
void main() {
frag_color = texture(texture_0, vec3(tex_coords, actual_layer()));
}
And seems like I incorrectly crop source texture, because when I run my program in Nsight debugger I saw follow:
Texture array:
Original image:
Is it issue with cropping source image, or issue with fragment shader? How to make sprite animation correctly?
You calculate the data offset incorrectly. The correct way would be:
Convert i to a 2-d index of the tile:
int ix = i % tile_w_count;
int iy = i / tile_w_count;
The x and y coordinates of its top-left pixel would be at
int x = ix*tile_w;
int y = iy*tile_h;
The offset can be calculated then by:
buffer + 4*(y*width + x)
Note that you shall use 4 instead of depth because stb returns the number of channels that was found in the file rather the number of the channels returned.

Cannot create/ sample 3D Texture (Qt/ OpenGL)

I am trying to display a layer in a 3D texture created from some 3D data, but all points sampled are always black (I guess my texture creation/ allocation is failing somehow). It is being rendered on a plane using window coords. I have checked my data, it is a vector with the correct values. glEnable(GL_TEXTURE_3D) has been called earlier. Any clues why this would fail?
Function that creates the texture:
bool VolumeRender::setVolumeData(QOpenGLShaderProgram *program, vector<unsigned short> v, int x, int y, int z){
voxels.resize(v.size(), 0);
cout << "Processing texture" << endl;
unsigned short sMax = 0;
unsigned short sMin = 32768;
for (unsigned int i =0; i< voxels.size(); i++){
sMax = max(sMax, v[i]);
sMin = min(sMin, v[i]);
} for (unsigned int i =0; i< voxels.size(); i++) voxels[i] = (v[i] - sMin)/(float)(sMax-sMin);
cout << "Loading 3D texture" << endl;
gl.glActiveTexture(GL_TEXTURE0);
gl.glGenTextures(1, &volumeTexture);
gl.glBindTexture(GL_TEXTURE_3D, volumeTexture);
gl.glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
gl.glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
gl.glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
gl.glTexImage3D(GL_TEXTURE_3D, 0, GL_RED, x, y, z, 0, GL_RED, GL_FLOAT, &voxels[0]);
gl.glBindTexture(GL_TEXTURE_3D, 0);
program->bind();
program->setUniformValue("VOXELS", 0);
program->release();
voxelsLoaded = true;
return true;
}
Simple fragment shader:
#version 330 core
uniform sampler3D VOXELS;
uniform vec2 SIZE;
out vec4 color;
void main(){
vec2 coords = (gl_FragCoord.xy - 0.5) / SIZE;
vec3 texcoords = vec3(coords, 0.5);
color = texture(VOXELS, texcoords);
}
glEnable(GL_TEXTURE_…) has no effect when using shaders. It's a relic from the fixed function pipeline era. On the other hand the texture must be actually bound when drawing.
In your code you have
gl.glBindTexture(GL_TEXTURE_3D, 0);
program->bind();
program->setUniformValue("VOXELS", 0);
program->release();
Now since this is in initialization code, it's unclear if you actually understand the consequences of these lines. So let's break it down:
gl.glBindTexture(GL_TEXTURE_3D, 0);
This means that texture 0 (which with shaders is the nil texture, but in old and busted OpenGL-1.0 it actually could be sampled form) is bount to texture unit 0. From there on, when trying to sample from texture unit 0, it will not sample anything.
program->bind();
program->setUniformValue("VOXELS", 0);
program->release();
Set the sampler uniform with name "VOXELS" to sample from texture unit 0. Whatever texture is bound to that texture unit at the moment of calling the draw function, that texture will be sampled from.
Somewhere in your program you're making a draw call. You didn't show us where. But in order for your drawing to actually sample from your texture you have to bind your 3d texture to texture unit 0.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, volumeTexture);
draw_stuff();

Issue drawing to textures with framebuffer (texture blank)

I'll preface this by saying I'm rather new to these topics, and have been giving myself a crash course the last few days for a school project. I apologize that this is quite long, but as I'm not sure where the problem is I figured I'd try to be thorough.
I'm attempting to use a framebuffer to draw to some RGBA textures, which are used later for other drawing (I'm using the values in the textures as vectors to draw particles -- textures store values such as their position and velocity).
However, as best I can tell, the textures after I draw them are blank. Sampling values in the texture gives back (0, 0, 0, 1). This also seems to be confirmed by my later drawing procedure, as it appears that all the particles are being drawn overlapped at origin (this observation is based on my blend function and some hard-coded test colour values).
I'm using OpenGL 4 with SDL2 and glew.
I'll attempt to post everything relevant in an orderly fashion:
The framebuffer class creates the framebuffer and attaches (3) textures.
It has:
GLuint buffer_id_ that is the framebuffer's id, initialized to 0, unsigned int width, height, which are the width and height of the textures in pixels, numTextures, which is how many textures to create for the framebuffer (again, in this case 3), and std::vector<GLuint> textures_ for all the textures associated with the framebuffer.
bool Framebuffer::init() {
assert( numTextures <= GL_MAX_COLOR_ATTACHMENTS );
// Get the buffer id.
glGenFramebuffers( 1, &buffer_id_ );
glBindFramebuffer( GL_FRAMEBUFFER, buffer_id_ );
// Generate the textures.
for (unsigned int i = 0; i < numTextures; ++i) {
GLuint tex;
glGenTextures( 1, &tex );
glBindTexture(GL_TEXTURE_2D, tex);
// Give empty image to OpenGL.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 0);
// Use GL_NEAREST, since we don't want any kind of averaging across values:
// we just want one pixel to represent a particle's data.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// This probably isn't necessary, but we don't want to have UV coords past the image edges.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
textures_.push_back(tex);
}
glBindTexture(GL_TEXTURE_2D, 0);
// Bind our textures to the framebuffer.
GLenum drawBuffers[GL_MAX_COLOR_ATTACHMENTS];
for (unsigned int i = 0; i < numTextures; ++i) {
GLenum attach = GL_COLOR_ATTACHMENT0 + i;
glFramebufferTexture(GL_FRAMEBUFFER, attach, textures_[i], 0);
drawBuffers[i] = attach;
}
glDrawBuffers( numTextures, drawBuffers );
if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) {
std::cerr << "Error: Failed to create framebuffer." << std::endl;
return false;
}
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
return true;
}
Two matching framebuffers are made by the particle system, so that one can be used as input to the shader programs and the other as output.
bool AmbientParticleSystem::initFramebuffers() {
framebuffers_[0] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
framebuffers_[1] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
return framebuffers_[0]->init() && framebuffers_[1]->init();
}
Then I attempt to draw an initial state to the first framebuffer:
void AmbientParticleSystem::initParticleDrawing() {
glBindFramebuffer( GL_FRAMEBUFFER, framebuffers_[0]->getBufferID() );
// Store the previous viewport.
GLint prevViewport[4];
glGetIntegerv( GL_VIEWPORT, prevViewport );
glViewport( 0, 0, particle_texture_width_, particle_texture_height_ );
glClear( GL_COLOR_BUFFER_BIT );
glDisable( GL_DEPTH_TEST );
glBlendFunc( GL_ONE, GL_ZERO);
init_shader_->use(); // This sets glUseProgram to the init shader.
GLint attrib = init_variable_ids_[constants::init::Variables::IN_VERTEX_POS]->id;
glEnableVertexAttribArray( attrib );
glBindBuffer( GL_ARRAY_BUFFER, full_size_quad_->getBufferID() );
glVertexAttribPointer( attrib, full_size_quad_->vertexSize,
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 ); // Array buffer offset
glDrawArrays( GL_TRIANGLES, 0, full_size_quad_->numVertices );
// Here I'm just printing some sample values to confirm it is blank/black.
glBindTexture( GL_TEXTURE_2D, framebuffers_[0]->getTexture(0) );
GLfloat *pixels = new GLfloat[particle_texture_width_ * particle_texture_height_ * 4];
glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, pixels);
std::cout << "some pixel entries:\n";
std::cout << "pixel0: " << pixels[0] << " " << pixels[1] << " " << pixels[2] << " " << pixels[3] << std::endl;
std::cout << "pixel10: " << pixels[40] << " " << pixels[41] << " " << pixels[42] << " " << pixels[43] << std::endl;
std::cout << "pixel100: " << pixels[400] << " " << pixels[401] << " " << pixels[402] << " " << pixels[403] << std::endl;
std::cout << "pixel10000: " << pixels[40000] << " " << pixels[40001] << " " << pixels[40002] << " " << pixels[40003] << std::endl;
// They all print 0, 0, 0, 1
delete pixels;
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray( attrib );
init_shader_->stopUsing();
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
// Return the viewport to its previous state.
glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3] );
}
You can see here is also where I try getting some pixel values, which all return (0, 0, 0, 1).
The full_size_quad_ used here is defined by:
// Create quad for textures to draw onto.
static const GLfloat quad_array[] = {
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
std::vector<GLfloat> quad(quad_array, quad_array + 18);
full_size_quad_ = new VertexBuffer(3, 6, quad);
VertexBuffer is my own class, which I don't think I'll need to show here. It just glGens and glBinds the vertex array and buffers.
The shader used here has this for the vertex shader:
#version 400 core
in vec3 inVertexPos;
void main() {
gl_Position = vec4(inVertexPos, 1.0);
}
and this for the fragment shader:
#version 400 core
layout(location = 0) out vec4 position;
layout(location = 1) out vec4 velocity;
layout(location = 2) out vec4 other;
uniform vec2 uResolution;
// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
float rand(vec2 seed) {
return fract(sin(dot(seed.xy,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 uv = gl_FragCoord.xy/uResolution.xy;
vec3 pos = vec3(uv.x, uv.y, rand(uv));
vec3 vel = vec3(-2.0, 0.0, 0.0);
vec4 col = vec4(1.0, 0.3, 0.1, 0.5);
position = vec4(pos, 1.0);
velocity = vec4(vel, 1.0);
other = col;
}
uResolution is the width and height of the textures in pixels, set by:
init_shader_->use();
glUniform2f( init_uniform_ids_[constants::init::Uniforms::U_RESOLUTION]->id, particle_texture_width_, particle_texture_height_ );
Out of curiosity I tried changing position = vec4(pos, 1.0); to different values, but my pixel printing still gave 0 0 0 1.
I've been debugging this for about 20-30 hours now, and looked up a dozen or so different tutorials and other questions here, but the last several hours I don't feel I've gained any ground.
Anything here standing out for why the textures appear to be blank/black, or any thing else that needs addressing? I was using this project to learn about shaders, so I'm quite new to all of this. Any help would be immensely appreciated.
This was resolved with a complete re-write of the code.
I'm still not 100% sure where the problem was, but I think I likely had some mismatched enable/disable calls.

OpenGL cube map texture doesn't work

I have problem getting cube maps to work in OpenGL. I followed some tutorials and created my own class wrapping the OpenGL cube map texture.
For start I'm trying to load six images for the cube map sides and render them onto a pot model. I want to create a mirror-like effect ultimately but for now I'm using just the normal as the cube map texture coordinate. The problem is the sampler in fragment shader seems to be returning only zeroes so the whole model is just black.
my findings so far:
Texture data should be uploaded to GPU - I checked this by uploading (glTexImage2D) and retrieving the data back (glGetTexImage2D).
I checked glGetError() and it returns no error, even though when I set glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); or
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); for the cubemap textures, I get invalid operation error inside the rendering loop when I try to render the mirror (the pot) - strange.
Normals of the pot should be OK, I have checked them by rendering them (see images below).
The ppm image I'm loading is 512 x 512, works for 2D textures, so there shouldn't be a problem.
Here is how I initialize the cube map:
texture_cube = new TextureCubeMap(512,TEXEL_TYPE_COLOR);
cout << "cubemap loading: " << texture_cube->load_ppms("rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm") << endl;
texture_cube->update_gpu();
...
glUniform1i(sampler_location,0); // 'tex' unit
glUniform1i(sampler_cube_location,0); // 'tex_cube' unit
Here's my rendering loop:
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(view_matrix_location,1,GL_TRUE,glm::value_ptr(CameraHandler::camera_transformation.get_matrix()));
texture_cup->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cup.get_matrix()));
geometry_cup->draw_as_triangles();
texture_rock->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_rock.get_matrix()));
geometry_rock->draw_as_triangles();
texture_cow->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cow.get_matrix()));
geometry_cow->draw_as_triangles();
texture_room->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(glm::mat4(1.0)));
geometry_room->draw_as_triangles();
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE, glm::value_ptr(transformation_mirror.get_matrix()));
// draw the mirror:
texture_cube->bind(0);
glUniform1ui(mirror_location,1);
geometry_mirror->draw_as_triangles();
glUniform1ui(mirror_location,0);
ErrorWriter::checkGlErrors("rendering loop");
glutSwapBuffers();
}
Here's my fragment shader:
#version 330
in vec3 transformed_normal;
in vec2 uv_coords;
uniform vec3 light_direction;
uniform sampler2D tex;
uniform samplerCube tex_cube;
uniform bool mirror;
out vec4 FragColor;
float diffuse_intensity;
float lighting_intensity;
void main()
{
diffuse_intensity = clamp(dot(normalize(transformed_normal),-1 * light_direction),0.0,1.0);
lighting_intensity = clamp(0.4 + diffuse_intensity,0.0,1.0);
if (mirror)
{
FragColor = texture(tex_cube, transformed_normal);
}
else
{
FragColor = 0.3 * vec4(lighting_intensity, lighting_intensity, lighting_intensity, 1.0);
FragColor += texture(tex, uv_coords);
}
}
And here is my whole cube map class (Image2D is my own class that should be working OK, I have it tested with 2D textures):
class TextureCubeMap: public Texture
{
protected:
unsigned int size;
public:
Image2D *image_front;
Image2D *image_back;
Image2D *image_left;
Image2D *image_right;
Image2D *image_top;
Image2D *image_bottom;
/**
* Initialises a new cube map object.
*
* #size width and height resolution in pixels (cube map must
* have square size)
*/
TextureCubeMap(unsigned int size, unsigned int texel_type = TEXEL_TYPE_COLOR)
{
this->size = size;
glGenTextures(1,&(this->to));
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP,0);
this->image_front = new Image2D(size,size,texel_type);
this->image_back = new Image2D(size,size,texel_type);
this->image_left = new Image2D(size,size,texel_type);
this->image_right = new Image2D(size,size,texel_type);
this->image_top = new Image2D(size,size,texel_type);
this->image_bottom = new Image2D(size,size,texel_type);
}
virtual ~TextureCubeMap()
{
delete this->image_front;
delete this->image_back;
delete this->image_left;
delete this->image_right;
delete this->image_top;
delete this->image_bottom;
}
virtual void update_gpu()
{
int i;
Image2D *images[] =
{this->image_front,
this->image_back,
this->image_left,
this->image_right,
this->image_bottom,
this->image_top};
GLuint targets[] =
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y};
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
for (i = 0; i < 6; i++)
{
glTexImage2D(targets[i],0,GL_RGBA,this->size,this->size,0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
glBindTexture(GL_TEXTURE_CUBE_MAP,0);
}
virtual void load_from_gpu()
{
int i;
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
Image2D *images[] =
{this->image_front,
this->image_back,
this->image_left,
this->image_right,
this->image_bottom,
this->image_top};
GLuint targets[] =
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y};
for (i = 0; i < 6; i++)
{
images[i]->set_size(this->size,this->size);
glGetTexImage(targets[i],0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());
}
}
bool load_ppms(string front, string back, string left, string right, string bottom, string top)
{
bool result = true;
result = result && this->image_front->load_ppm(front);
result = result && this->image_back->load_ppm(back);
result = result && this->image_left->load_ppm(left);
result = result && this->image_right->load_ppm(right);
result = result && this->image_bottom->load_ppm(bottom);
result = result && this->image_top->load_ppm(top);
auto lambda_size_ok = [](Image2D *image, int size)
{
return (image->get_width() == size) && (image->get_height() == size);
};
if (!lambda_size_ok(this->image_front,this->size) ||
!lambda_size_ok(this->image_back,this->size) ||
!lambda_size_ok(this->image_left,this->size) ||
!lambda_size_ok(this->image_right,this->size) ||
!lambda_size_ok(this->image_top,this->size) ||
!lambda_size_ok(this->image_bottom,this->size))
ErrorWriter::write_error("Loaded cubemap images don't have the same size.");
return result;
}
virtual void print()
{
cout << "front:" << endl;
this->image_front->print();
cout << "back:" << endl;
this->image_back->print();
cout << "left:" << endl;
this->image_left->print();
cout << "right:" << endl;
this->image_right->print();
cout << "bottom:" << endl;
this->image_bottom->print();
cout << "top:" << endl;
this->image_top->print();
}
virtual void bind(unsigned int unit)
{
switch (unit)
{
case 0: glActiveTexture(GL_TEXTURE0); break;
case 1: glActiveTexture(GL_TEXTURE1); break;
case 2: glActiveTexture(GL_TEXTURE2); break;
case 3: glActiveTexture(GL_TEXTURE3); break;
case 4: glActiveTexture(GL_TEXTURE4); break;
case 5: glActiveTexture(GL_TEXTURE5); break;
case 6: glActiveTexture(GL_TEXTURE6); break;
case 7: glActiveTexture(GL_TEXTURE7); break;
case 8: glActiveTexture(GL_TEXTURE8); break;
case 9: glActiveTexture(GL_TEXTURE9); break;
default:
break;
}
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
}
};
And the images of the error (first one what I get, the second one is with normals rendered):
You figured out the solution yourself in the comments, but requested some more background on why it behaves the way it does.
Yes, if you want to use two different textures in the same fragment shader, you will have to bind the two textures to different texture units, and set the values of the sampler uniform variables to the matching texture unit indices.
For the case where the two textures use the same target, it's fairly obvious that this must be true. Otherwise your texture binding code would look something like this:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_2D, tex2);
and the second glBindTexture() call would of course override the first one. You will therefore use different texture units:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
Things are much less obvious in the case you are looking at, where you use two different texture targets. After this call sequence:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);
You indeed have both textures bound to texture unit 0. In the OpenGL state, each texture unit contains a binding for each target.
Now, if you want to use a 2D and a cube map texture in the same shader, it seems like a perfectly reasonable expectation that you could bind the two textures as shown above, and set both sampler uniforms to value 0. But in fact, this is not supported, because... it's defined that way.
This is on page 74 of the OpenGL 3.3 spec, in section "2.11.5 Samplers":
It is not allowed to have variables of different sampler types pointing to the same texture image unit within a program object. This situation can only be detected at the next rendering command issued, and an INVALID_OPERATION error will then be generated.
While this makes sense based on how GPUs normally access samplers from shader code, it's unfortunate that you can set up state for texture bindings in ways that are not usable from shader code. Part of this is probably based on the long history of OpenGL.
To make this work, you will have to use two different texture units, just like you did for the case with two textures using the same target:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);

FBO rendering issue - Using generated FBO color/depth in same App for Drawing using GLSL

Image with two cubes is when its using BLIT Call - Test Code
Image with one Cube is when its not Using BLIT CALL - Test Code
Folks,
Please follow the attached pictures & Code as below..
Struggling with this from last 2 days..
Facing an issue while using FBO in below code, I am able to attach & render to FBo successfully, BUT some how not able to use that as a texture (color & depth buffers) in the same program, MY glReadpixel call after rendering to FBO Shows me all Zero's data...
When generated FBO(color,depth) is used in GLSL- shader code , which is called from Draw2() function displays a blend white cube without texture..
please suggest..., below is the complete code... , I am creating an FBO & using 2 shaders one for Normal rendering & other one uses the color & depth from FBO processed earlier..
Here is MY SHADER CODE
init_gl()
{
This Fragment Shader Code is for normal rendering used in Program under draw()
static const char *fragment_shader_source =
"precision mediump float; \n"
" \n"
"varying vec4 vVaryingColor; \n"
" \n"
"void main() \n"
"{ \n"
" gl_FragColor = vVaryingColor; \n"
"} \n";
This Fragment Shader Code is used in Program2 under draw2()
static const char *fragment_shader_source2 =
"precision mediump float; \n"
" \n"
"varying vec4 vVaryingColor; \n"
"uniform sampler2D color; \n"
"uniform sampler2D depth; \n"
" \n"
"void main() \n"
"{ \n"
"float r = pow(texture2D(color, gl_FragCoord.xy / vec2(256, 256)).a, 128.0); \n"
"float g = texture2D(depth, gl_FragCoord.xy / vec2(256, 256)).a;\n"
"float b = pow(texture2D(depth, gl_FragCoord.xy / vec2(256, 256)).a, 128.0); \n"
"gl_FragColor = vec4(r, g, b, 1.); \n"
"} \n";
MY FIRST DRAWING ON FBO
static void draw(uint32_t i)
{
EGL_CHECK(glEnable(GL_DEPTH_TEST));
glEnable(GL_TEXTURE_2D);
EGL_CHECK(glDepthFunc(GL_ALWAYS));
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fboInfo.id));
EGL_CHECK(glUseProgram(program));
//CUBE DRAWING
Doing Read Pixel here, Its returning all zero's
GLubyte *pixels = malloc(4 * 256 * 256);
EGL_CHECK(glReadBuffer(GL_COLOR_ATTACHMENT0));
EGL_CHECK(glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
//**TO TEST my FBO , IF ITS HAVING SOME RENDER DATA**
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboInfo.id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
//TEST CODE ONLY
glBindTexture(GL_TEXTURE_2D, fboInfo.id);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
draw2(i);
}
2nd DRAWING WITHOUT FBO (BUT this uses FRAG SHADER WHICH CAN ACCESS FBO data color,depth)
static void draw2(uint32_t i)
{
glUseProgram(program2);
//Draw Same Kind of Cube , Which we draw in draw() function
}
These are the functions to create FBO (color,depth)
GLuint createTexture2D(const int w, const int h, GLint internalFormat, GLenum format, GLenum type)
{
GLuint textureIdX;
EGL_CHECK(glGenTextures(1, &textureIdX));
EGL_CHECK(glBindTexture(GL_TEXTURE_2D, textureIdX));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
if (GL_DEPTH_COMPONENT == format) {
EGL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE));
EGL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY));
}
EGL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, 0));
EGL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
return textureIdX;
}
int createFBO(void)
{
int result = 0;
unsigned int fb = 0;
fboInfo.color = createTexture2D(WINDOW_WIDTH, WINDOW_HEIGHT, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
fboInfo.depth = createTexture2D(WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT);
EGL_CHECK(glGenFramebuffers(1, &fboInfo.id));
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fboInfo.id));
EGL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboInfo.color, 0));
EGL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboInfo.depth, 0));
int data = EGL_CHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
printf("FBO %d set up successfully\n", fboInfo.id);
result = 1;
}
else {
printf("FBO %d NOT set up properly!\n", fboInfo.id);
}
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
return result;
}
This is how my FBO Struct Looks Like
typedef struct {
GLuint id;
GLuint color;
GLuint depth;
}FBOInfo;
My MAIN function looks a below where I create FBO & then call my draw function & swap the buffers..
unsigned long i = 0;
int ret = init_gl();
ret = createFBO();
draw(i);
EGL_CHECK(eglSwapBuffers(eglDisplay, eglSurface));
Since this is quite a lot of code, and you suggested that the problem is with the FBO setup, I focused on that section. I did spot a critical problem in that part of the code where you set up the attachments:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboInfo.depth, 0);
If you call glGetError() after this, you should see an error returned, because GL_RENDERBUFFER is not a valid value for the 3rd argument. The only valid arguments are GL_TEXTURE_2D and the GL_TEXTURE_CUBE_MAP_* values. You have to use GL_TEXTURE_2D in this case.
It might actually be better to use a renderbuffer for the depth buffer if you never use it as a texture. But to do that, you'll have to use all the corresponding calls:
Create the id with glGenRenderbuffers.
Bind it with glBindRenderbuffer.
Allocated it with glRenderbufferStorage.
Attach it to the FBO with glFramebufferRenderbuffer.