Search code examples
openglrustgraphics3dglium

How to draw rotating cube glium?


Why is my rotating cube doesn't looks like rotating cube? Do I need to move camera? I don't have any idea what is wrong. I am using glium in rust.

24 Vertices

const P: f32 = 0.5;
let vertex_buffer = glium::VertexBuffer::new(&display, &vec![
        // front 0-3
        Vertex { position: (-P, -P, 0.0) }, 
        Vertex { position: (P, P, 0.0) },
        Vertex { position: (P, -P, 0.0) },
        Vertex { position: (-P, P, 0.0) },
        // back 4-7
        Vertex { position: (-P, -P, 1.0) },
        Vertex { position: (P, P, 1.0) },
        Vertex { position: (P, -P, 1.0) },
        Vertex { position: (-P, P, 1.0) },
        // up 8-11
        Vertex { position: (-P, P, 0.0) }, 
        Vertex { position: (P, P, 0.0) },
        Vertex { position: (P, P, 1.0) },
        Vertex { position: (-P, P, 1.0) },
        // down 12-15
        Vertex { position: (-P, -P, 0.0) },
        Vertex { position: (P, -P, 0.0) },
        Vertex { position: (P, -P, 1.0) },
        Vertex { position: (-P, -P, 1.0) },
        // right 16-19
        Vertex { position: (P, -P, 0.0) }, 
        Vertex { position: (P, -P, 1.0) },
        Vertex { position: (P, P, 1.0) },
        Vertex { position: (P, P, 0.0) },
        // left 20-23
        Vertex { position: (-P, -P, 0.0) },
        Vertex { position: (-P, -P, 1.0) },
        Vertex { position: (-P, P, 1.0) },
        Vertex { position: (-P, P, 0.0) },
    ]
).unwrap();

Indices for generating 6 faces of cube(4 Vertices per face, 2 triangles per face):

let indices = glium::IndexBuffer::new(&display, glium::index::PrimitiveType::TrianglesList,
&[
        // front
        0, 2, 1, 
        0, 3, 1,
        // back
        4, 6, 5,
        4, 7, 5,
        // up
        8, 9, 10,
        8, 11, 10,
        // down
        12, 13, 14,
        12, 15, 14,
        // right
        16, 17, 18,
        16, 19, 18,
        // left
        20, 21, 22,
        20, 23, 22u16,
    ]
).unwrap();

I am using Gouraud shading(from glium tutorial) for lighting.
I find normals for cube on the stackoverflow.
Normals:

let normals = glium::VertexBuffer::new(&display, &vec![
        // front
        Normal { normal: (1.0, 0.0, 0.0) }, 
        // back
        Normal { normal: (0.0, -1.0, 0.0) },
        // up
        Normal { normal: (1.0, 0.0, -1.0) },
        // down
        Normal { normal: (0.0, 0.0, 1.0) },
        // right
        Normal { normal: (1.0, 0.0, 0.0) },
        // left
        Normal { normal: (-1.0, 0.0, 0.0) },
    ]
).unwrap();

Vertex shader(glsl):

#version 150

in vec3 position;
in vec3 normal;
out vec3 v_normal;
uniform mat4 m;

void main() {
    v_normal = transpose(inverse(mat3(m))) * normal;
    gl_Position = m * vec4(position, 1.0);
}

Fragment shader(glsl):

#version 150

in vec3 v_normal;
out vec4 color;
uniform vec3 u_light;

void main() {
    float brightness = dot(normalize(v_normal), normalize(u_light));
    vec3 dark_color = vec3(0.6, 0.0, 0.0);
    vec3 regular_color = vec3(1.0, 0.0, 0.0);
    color = vec4(mix(dark_color, regular_color, brightness), 1.0);
}

Rotating is done by 4x4 matrix:

let mut t: f32 = 0.0;
let mut s: f32 = 0.002;

// ... loop.run

t += s;
if t > 180.0 || t < -180.0 {
    s = -s;
}

let m = [
    [1.0, 0.0, 0.0, 0.0],
    [0.0, t.cos(), -t.sin(), 0.0],
    [0.0, t.sin(), t.cos(), 0.0],
    [0.0, 0.0, 0.0, 1.0f32]
];

let light = [-1.0, -0.4, 0.9f32];

// params

target.draw((&vertex_buffer, &normals), &indices, &program, &uniform! { m: m, u_light: light }, &params).unwrap();

target.finish().unwrap();

Any ideas whats wrong? I am sorry for so long Q. which has so much of code, but I don't know what's else than this I can provide.

There's some images of the my "rotating cube": startmiddle phase 1middle phase 2end


Solution

  • Your rotation matrix is a rotation about the X axis, and you are applying no perspective projection matrix, so you get the implied default orthographic projection. Your pictures are more or less typical examples of what you get in that case, though with what might be further errors, perhaps in the vertex data (I'm noticing the visible diagonal line).

    The vertices are being moved in circles about the X axis, in the YZ plane, and you can't see Z directly, so you just see them moving up and down in Y.

    In order to get a picture that looks like a rotating cube, you will want to set up a perspective projection, and probably a camera position/rotation (view matrix) that is looking at the cube.

    Or you could try changing your matrix to a matrix for rotation about the Z axis:

    let m = [
        [t.cos(), -t.sin(), 0.0, 0.0],
        [t.sin(), t.cos(), 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 0.0, 0.0, 1.0f32]
    ];
    

    That will be visibly a rotation.

    But in general, I highly recommend getting perspective projection in your program. This will reveal motion in the Z axis (currently invisible) and make the images make more intuitive sense to human vision. It is also useful to add more objects and more complex ones, making a more complete “scene” — then if you have code bugs, you can see how they're affecting all the geometry being displayed, rather than seeing only a very abstract bunch of rectangles as you have now.