Search code examples
rustopenglglsl

Only the first generated texture has transparency


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.
Image of two red windows, with a grass texture in between, the rightmost window is a darker shade of red

This is how it looks if I comment the lines with the grass texture. Image of three red windows, all in the same shade of red

(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;
    }
}

Solution

  • 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.