I'm trying to show two objects that have (different) transparent textures, but the second one always loses its transparency, no matter the order (i.e. if I generate texture A then B, B loses transparency; if I load B then A, A loses transparency).
This seems to come from the glGenTextures
function, since if I comment everything else related to the textures I still get this problem (I don't even need to load the image). I can even instantiate an empty material and the other texture will work, so long as I don't instantiate the first one.
This is how it looks if I comment the lines with the grass texture.
(For some reason it gets assigned the window texture but I'm assuming that's an unrelated problem.)
These are what I think the most relevant parts of my code:
fn draw_scene(
scene: &Vec<&SceneObject>,
object_shader: &ShaderProgram,
outline_shader: &ShaderProgram,
camera: &Camera,
lighting: &Lighting,
) {
let projection = &perspective(1.0, camera.get_fov(), 0.1, 100.0);
let reinitialize_object_shader = || {
object_shader.use_program();
object_shader.set_view(&camera);
object_shader.set_directional_light("dirLight", &lighting.dir);
object_shader.set_matrix_4fv("projectionMatrix", projection);
for (i, point) in lighting.point.iter().enumerate() {
object_shader.set_point_light(format!("pointLights[{}]", i).as_str(), &point);
}
object_shader.set_spotlight("spotlight", &lighting.spot);
};
let reinitialize_outline_shader = || {
outline_shader.use_program();
outline_shader.set_view(&camera);
outline_shader.set_matrix_4fv("projectionMatrix", projection);
};
let mut ordered_scene = scene.clone();
let distance_compare = |a: &&SceneObject, b: &&SceneObject| {
let distance_a = length(&(camera.get_pos() - a.get_pos()));
let distance_b = length(&(camera.get_pos() - b.get_pos()));
distance_b.partial_cmp(&distance_a).unwrap()
};
ordered_scene.sort_by(distance_compare);
reinitialize_object_shader();
for object in ordered_scene {
object.draw(&object_shader);
if object.has_outline() {
reinitialize_outline_shader();
object.draw_outline(&outline_shader);
reinitialize_object_shader();
}
}
}
fn main() {
[...]
unsafe {
let fun =
|x: *const u8| win.get_proc_address(x as *const i8) as *const std::os::raw::c_void;
load_global_gl(&fun);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
let mut scene: Vec<&SceneObject> = vec![];
let mut grass_mesh = Mesh::square(1.0);
let mut grass_tex = Texture::new(TextureType::Diffuse);
grass_tex.load(Path::new(GRASS_TEXTURE));
grass_tex.set_wrapping(GL_CLAMP_TO_EDGE);
grass_mesh.material = Material::new(vec![grass_tex], vec![], 1.0);
let mut grass: SceneObject = SceneObject::from(grass_mesh);
grass.set_outline(vec3(0.04, 0.28, 0.26));
grass.enable_outline(true);
scene.push(&grass);
let mut window_mesh = Mesh::square(1.0);
let mut window_tex = Texture::new(TextureType::Diffuse);
window_tex.load(Path::new(WINDOW_TEXTURE));
window_tex.set_wrapping(GL_CLAMP_TO_EDGE);
window_mesh.material = Material::new(vec![window_tex], vec![], 1.0);
let mut window1 = SceneObject::from(window_mesh.clone());
window1.translate(&vec3(0.0, 0.0, -1.0));
scene.push(&window1);
rendering::clear_color(0.2, 0.3, 0.3, 1.0);
[...]
'main_loop: loop {
[...]
unsafe {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
draw_scene(
&scene,
&shader_program_model,
&shader_program_outline,
&main_camera,
&lighting,
);
win.swap_window();
}
}
pub struct Texture {
id: u32,
ttype: TextureType,
path: String,
}
impl Texture {
pub fn new(ttype: TextureType) -> Self {
let mut texture: u32 = 0;
unsafe {
glGenTextures(1, &mut texture);
}
Self {
id: texture,
ttype,
path: String::new(),
}
}
pub fn load(&mut self, path: &Path) {
let (mut width, mut height, mut nr_channels): (i32, i32, i32) = (0, 0, 0);
let path_string = CString::new(path.as_os_str().as_bytes()).unwrap();
unsafe {
glBindTexture(GL_TEXTURE_2D, self.id);
stbi_set_flip_vertically_on_load(1);
let data = stbi_load(
path_string.as_ptr(),
&mut width,
&mut height,
&mut nr_channels,
0,
);
let format = match nr_channels {
4 => GL_RGBA,
_ => GL_RGB,
};
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA.0 as i32,
width,
height,
0,
format,
GL_UNSIGNED_BYTE,
data as *const c_void,
);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data as *mut c_void);
glBindTexture(GL_TEXTURE_2D, 0);
}
self.path = path.display().to_string();
}
pub fn bind(&self) {
unsafe {
glBindTexture(GL_TEXTURE_2D, self.id);
}
}
[...]
}
impl ShaderProgram {
[...]
pub fn set_material(&self, material_name: &str, value: &Material) {
let diffuse_vector = value.get_diffuse_maps();
let specular_vector = value.get_specular_maps();
let mut tex_count = 0;
for (i, diffuse) in diffuse_vector.iter().enumerate() {
unsafe {
glActiveTexture(GLenum(GL_TEXTURE0.0 + tex_count as u32));
}
tex_count += 1;
diffuse.bind();
let name = format!("{}.Diffuse[{}]", material_name, i);
self.set_1i(&name, (diffuse.get_id() - 1) as i32);
}
for (i, specular) in specular_vector.iter().enumerate() {
unsafe {
glActiveTexture(GLenum(GL_TEXTURE0.0 + tex_count as u32));
}
tex_count += 1;
specular.bind();
let name = format!("{}.Specular[{}]", material_name, i);
self.set_1i(&name, (specular.get_id() - 1) as i32);
}
self.set_1f(
&format!("{}.shininess", material_name),
value.get_shininess(),
);
self.set_1i(
&format!("{}.loadedDiffuse", material_name),
diffuse_vector.len().max(1) as i32,
);
self.set_1i(
&format!("{}.loadedSpecular", material_name),
specular_vector.len().max(1) as i32,
);
}
}
#version 330 core
in vec3 fragPos;
in vec3 normal;
in vec2 texCoords;
#define NR_DIFFUSE_TEXTURES 3
#define NR_SPECULAR_TEXTURES 3
struct Material {
sampler2D Diffuse[NR_DIFFUSE_TEXTURES];
sampler2D Specular[NR_SPECULAR_TEXTURES];
float shininess;
int loadedDiffuse;
int loadedSpecular;
};
uniform Material material;
out vec4 fragColor;
vec4 diff_tex_values[NR_DIFFUSE_TEXTURES];
vec4 spec_tex_values[NR_SPECULAR_TEXTURES];
void main() {
for (int i = 0; i < material.loadedDiffuse; i++)
diff_tex_values[i] = texture(material . Diffuse[i], texCoords);
for (int i = 0; i < material.loadedSpecular; i++)
spec_tex_values[i] = texture(material . Specular[i], texCoords);
vec3 norm = normalize(normal);
vec3 viewDir = normalize(viewPos - fragPos);
vec4 result = calculateDirectionalLight(dirLight, norm, viewDir);
for (int i = 0; i < NR_POINT_LIGHTS; i++)
result += calculatePointLight(pointLights[i], norm, fragPos, viewDir);
result += calculateSpotlight(spotlight, norm, fragPos, viewDir);
if (result . a < 0.1) {
discard;
} else {
fragColor = result;
}
}
Found the problem. When passing the material to the shader, I was using the id of the texture, when I should have used the index of the texture its respective array (diffuse or specular) in the Material struct. Changing the loops inside the set_material
method to something like:
for (i, diffuse) in diffuse_vector.iter().enumerate() {
unsafe {
glActiveTexture(GLenum(GL_TEXTURE0.0 + tex_count as u32));
}
tex_count += 1;
diffuse.bind();
let name = format!("{}.Diffuse[{}]", material_name, i);
self.set_1i(&name, i as i32);
}
for (i, specular) in specular_vector.iter().enumerate() {
unsafe {
glActiveTexture(GLenum(GL_TEXTURE0.0 + tex_count as u32));
}
tex_count += 1;
specular.bind();
let name = format!("{}.Specular[{}]", material_name, i);
self.set_1i(&name, i as i32);
}
fixed the problem.