OpenGL cube map texture doesn't work -
i have problem getting cube maps work in opengl. followed tutorials , created own class wrapping opengl cube map texture. start i'm trying load 6 images cube map sides , render them onto pot model. want create mirror-like effect i'm using normal cube map texture coordinate. problem sampler in fragment shader seems returning zeroes whole model black.
my findings far:
- texture data should uploaded gpu - checked uploading (
glteximage2d
) , retrieving data (glgetteximage2d
). - i checked glgeterror() , returns no error, though when set
gltexparameteri(gl_texture_2d, gl_texture_mag_filter, gl_linear_mipmap_linear);
orgltexparameteri(gl_texture_2d, gl_texture_min_filter, gl_linear_mipmap_linear);
cubemap textures, invalid operation error inside rendering loop when try render mirror (the pot) - strange. - normals of pot should ok, have checked them rendering them (see images below).
- the ppm image i'm loading 512 x 512, works 2d textures, there shouldn't problem.
here how initialize 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 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 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 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 whole cube map class (image2d own class should working ok, have tested 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 new cube map object. * * @size width , 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); (i = 0; < 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}; (i = 0; < 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 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 images of error (first 1 get, second 1 normals rendered):
you figured out solution in comments, requested more background on why behaves way does.
yes, if want use 2 different textures in same fragment shader, have bind 2 textures different texture units, , set values of sampler uniform variables matching texture unit indices.
for case 2 textures use same target, it's obvious must true. otherwise texture binding code this:
glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, tex1); glbindtexture(gl_texture_2d, tex2);
and second glbindtexture()
call of course override first one. therefore use different texture units:
glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, tex1); glactivetexture(gl_texture1); glbindtexture(gl_texture_2d, tex2);
things less obvious in case looking at, use 2 different texture targets. after call sequence:
glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, tex1); glbindtexture(gl_texture_cube_map, tex2);
you indeed have both textures bound texture unit 0. in opengl state, each texture unit contains binding each target.
now, if want use 2d , cube map texture in same shader, seems reasonable expectation bind 2 textures shown above, , set both sampler uniforms value 0. in fact, not supported, because... it's defined way.
this on page 74 of opengl 3.3 spec, in section "2.11.5 samplers":
it not allowed have variables of different sampler types pointing same texture image unit within program object. situation can detected @ next rendering command issued, , invalid_operation error generated.
while makes sense based on how gpus access samplers shader code, it's unfortunate can set state texture bindings in ways not usable shader code. part of based on long history of opengl.
to make work, have use 2 different texture units, did case 2 textures using same target:
glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, tex1); glactivetexture(gl_texture1); glbindtexture(gl_texture_cube_map, tex2);
Comments
Post a Comment