Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Opengl Texture rendering without color

I am writing an openGl wrapper in rust for educational purposes. However, I have a problem displaying textures from an image file (jpg). The image is displayed as a grayscale image with some buggy red and green lines.

I am loading the image using the "image" crate and converting it to a ImageBuffer. The loading part is working, what i have checked by saving the image again.

I think that the conversion of the buffer to rgb is also working because the second time i save the image it is also working.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

code to load the image:

extern  crate image;

use gl;

use std::{path, ffi::c_void};

#[derive(Debug)]
pub struct Texture {
    id: u32,
    location: gl::types::GLenum,
    path: path::PathBuf,
}

enum TextureError {
    FileNotFound(path::PathBuf),
    InvalidFileType(String)
}

impl Texture {
    pub fn new(file_path: &str, location: u32) -> Texture {
        let mut id = 0;

        let img = image::open(file_path).unwrap();
        img.save("res/test.jpg").unwrap();
        let img = match img {
            image::DynamicImage::ImageRgb8(img) => img,
            x => x.to_rgb8()
        };
        let width = img.width();
        let height = img.height();
        img.save("res/test2.jpg").unwrap();

        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + location);
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);

            gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB8 as i32, width as i32, height as i32, 0, gl::RGB, gl::UNSIGNED_BYTE, (&img as &[u8]).as_ptr() as *const c_void);
            gl::GenerateMipmap(gl::TEXTURE_2D);
        }
        Texture {
            id,
            location,
            path: path::PathBuf::from(file_path)
        }
    }
}

main code:

The modules fundamental, shader, window, etc. are modules i wrote and tested. (they work as inteded)

extern crate gl;
extern crate glfw;

#[macro_use]
extern crate lazy_static;

mod fundamental;
mod shader;
mod window;

use window::Window;

use crate::fundamental::{Buffer, VertexArray, VertexAttribPtr, Texture};

fn main() {
    //setup glfw
    let mut window = Window::new();

    //load OpenGL
    gl::load_with(|s| window.glfw_window.get_proc_address(s) as *const _);
    unsafe {
        gl::Viewport(0, 0, 600, 600);
    }

    let vertices: Vec<f32> = vec![
        -0.5, -0.5, 0.0, 0.0, 0.0,
         0.5, -0.5, 0.0, 1.0, 0.0,
        -0.5, 0.5, 0.0, 0.0, 1.0,
         0.5, 0.5, 0.0, 1.0, 1.0
    ];
    
    let indices: Vec<u32> = vec![0, 1, 2, 1, 2, 3];

    let vao = VertexArray::new();
    vao.bind();
    let vbo = Buffer::new(gl::STATIC_DRAW, gl::ARRAY_BUFFER);
    let ebo = Buffer::new(gl::STATIC_DRAW, gl::ELEMENT_ARRAY_BUFFER);
    vbo.set_data(&vertices);
    ebo.set_data(&indices);
    let _v_a_ptr0 = VertexAttribPtr::new(0, 3, gl::FLOAT, 5 * 4, 0);
    let _v_a_ptr1 = VertexAttribPtr::new(1, 2, gl::FLOAT, 5 * 4, 3 * 4);
    vao.unbind();

    let shader = shader::Shader::new("shaders/shader.vs", "shaders/shader.fs", None);

    let _texture_bonfire = Texture::new("res/bonfire.jpg", 0);

    //rendering loop
    println!("---Starting rendering loop---");
    while window.not_closed() {
        window.poll_events();
        unsafe {
            gl::ClearColor(0.2, 0.3, 0.3, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
            let mut error = gl::GetError();
            while error != 0 {
                println!("{}", error);
                error = gl::GetError();
            }
            shader.bind();
            vao.bind();
            gl::DrawElements(
                gl::TRIANGLES,
                indices.len() as i32,
                gl::UNSIGNED_INT,
                std::ptr::null(),
            );
        }
        window.swap_buffers();
    }
}

the code of the vertex_attrib_ptr moule:

(just in case it is needed)

use std::ffi::c_void;

pub struct VertexAttribPtr {
    location: u32,
}

impl VertexAttribPtr {
    pub fn new(
        location: u32,
        size_of_attribute: i32,
        vertex_type: gl::types::GLenum,
        vertex_size: i32,
        offset: u32,
    ) -> VertexAttribPtr {
        unsafe {
            gl::VertexAttribPointer(
                location,
                size_of_attribute,
                vertex_type,
                gl::FALSE,
                vertex_size,
                offset as *const c_void,
            );
            gl::EnableVertexAttribArray(location);
        }
        VertexAttribPtr { location }
    }

    #[allow(unused)]
    pub fn enable(&self) {
        unsafe {
            gl::EnableVertexAttribArray(self.location);
        }
    }

    #[allow(unused)]
    pub fn disable(&self) {
        unsafe {
            gl::DisableVertexAttribArray(self.location);
        }
    }
}

vertex shader

#version 460 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tex_coord;

out vec2 texture_cord;

void main()
{
    gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);
    texture_cord = tex_coord;
}

fragment shader

#version 460 core
out vec4 FragColor;

in vec2 texture_cord;

uniform sampler2D texture0;

void main()
{
    FragColor = texture(texture0, texture_cord);
    //FragColor = vec4(1.0);
}

rendered texture:

The texture image is also upside down, but this is a problem for later.

rendered texture

original image

original image

>Solution :

By default OpenGL assumes that the start of each row of an image is aligned to 4 bytes.

This is because the gl::UNPACK_ALIGNMENT parameter by default is 4. Since the image has 3 color channels (gl::RGB), and is tightly packed the size of a row of the image may not be aligned to 4 bytes.
When a RGB image with 3 color channels is loaded to a texture object and 3*width is not divisible by 4, gl::UNPACK_ALIGNMENT has to be set to 1, before specifying the texture image with gl::TexImage2D:

gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB8 as i32, width as i32, height as i32, 0, gl::RGB, gl::UNSIGNED_BYTE, (&img as &[u8]).as_ptr() as *const c_void);

The texture is also flipped. You have to "swap" the 2nd component of the texture coordinates:

let vertices: Vec<f32> = vec![
   -0.5, -0.5, 0.0, 0.0, 1.0,
    0.5, -0.5, 0.0, 1.0, 1.0,
   -0.5,  0.5, 0.0, 0.0, 0.0,     
    0.5,  0.5, 0.0, 1.0, 0.0
];
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading