Struggling on ploting the cube with OpenGL with the zm library

I am not sure if this is a zig or OpenGL issue, so please let me know so that I will post this question in somewhere else.

After having some luck on plotting a triangle using OpenGL with zig and port the main features of the sb7.h framework from the book, I decided to take a step further to port the “indexed Cube” example from the OpenGL superbible, trying to learn about the buffers and doing transformation using the matrix library, which I have picked the zm library by griush which some other opengl projects did use it.

The original C++ implementation consists two buffers objects, vertex_indices, and vertex_positions. This is the zig version of the arrays:

const vertex_positions = [_]app.gl.float{
    -0.25, -0.25, -0.25,
    -0.25, 0.25,  -0.25,
    0.25,  -0.25, -0.25,
    0.25,  0.25,  -0.25,
    0.25,  -0.25, 0.25,
    0.25,  0.25,  0.25,
    -0.25, -0.25, 0.25,
    -0.25, 0.25,  0.25,
};
const vertex_indices = [_]app.gl.ushort{
    0, 1, 2,
    2, 1, 3,
    2, 3, 4,
    4, 3, 5,
    4, 5, 6,
    6, 5, 7,
    6, 7, 0,
    0, 7, 1,
    6, 0, 2,
    2, 4, 6,
    7, 5, 3,
    7, 3, 1,
};

With this two arrays, I needed to declare two buffer objects as shown:

// creating a new buffer usually following the following pattern:
app.gl.GenBuffers(1, (&position_buffer)[0..1]);
app.gl.BindBuffer(app.gl.ARRAY_BUFFER, position_buffer);
app.gl.BufferData(
    app.gl.ARRAY_BUFFER,
    @sizeOf(f32) * vertex_positions.len,
    &vertex_positions,
    app.gl.STATIC_DRAW,
);

// set up a vertex attribute array
app.gl.EnableVertexAttribArray(0);
app.gl.VertexAttribPointer(0, 3, app.gl.FLOAT, app.gl.FALSE, 0, 0);

// the vertex index array buffer
app.gl.GenBuffers(1, (&index_buffer)[0..1]);
app.gl.BindBuffer(app.gl.ELEMENT_ARRAY_BUFFER, index_buffer);
app.gl.BufferData(
    app.gl.ELEMENT_ARRAY_BUFFER,
    @sizeOf(app.gl.ushort) * vertex_indices.len,
    &vertex_indices,
    app.gl.STATIC_DRAW,
);

Meanwhile, I have two uniforms in my code to calculate the position, orientation and perspective of my cube, which are the following:

mv_location = app.gl.GetUniformLocation(program, "mv_matrix");
proj_location = app.gl.GetUniformLocation(program, "proj_matrix");

which corresponding to the following equation in my vertex shader code for calculating the movement and the perspective of the cube:

gl_Position = proj_matrix * mv_matrix * position; 

Where the data of these matrices calculated as the following:
For proj_matrix:

// zig perspective is radian based
const proj_matrix = zm.Mat4f.perspective(
    std.math.degreesToRadians(50),
    8.0 / 6.0,
    0.1,
    1000,
);
app.gl.UniformMatrix4fv(proj_location, 1, app.gl.FALSE, @ptrCast(&proj_matrix));

Corresponding to the original C++ code from the book:

// C++ is degree based, thus no radian convertion
vmath::mat4 proj_matrix = vmath::perspective(50.0f,
    (float)info.windowWidth / (float)info.windowHeight,
    0.1f,
    1000.0f);
glUniformMatrix4fv(proj_location, 1, GL_FALSE, proj_matrix);

app.gl.UniformMatrix4fv(mv_location, 1, app.gl.FALSE, @ptrCast(&mv_matrix));
app.gl.DrawElements(app.gl.TRIANGLES, 36, app.gl.UNSIGNED_SHORT, 0);

For the transforms, I did the following:

// sin() and cos() in C++ are in radian, but vmath::rotate is in degree afaik
// thus the lack of radian conversion in the trigonometric functions.

var mv_matrix = zm.Mat4f.translation(
    0.0,
    0.0,
    -4.0,
);
const mv_b = zm.Mat4f.translation(
    @sin(2.1 * f) * 0.5,
    @cos(1.7 * f) * 0.5,
    @sin(1.3 * f) * @cos(1.5 * f) * 2.0,
);
const mv_c = zm.Mat4f.rotation(
    zm.Vec3f{ 0.0, 1.0, 0.0 },
    std.math.degreesToRadians(current_time_f32 * 45.0),
);
const mv_d = zm.Mat4f.rotation(
    zm.Vec3f{ 1.0, 0.0, 0.0 },
    std.math.degreesToRadians(current_time_f32 * 81.0),
);

mv_matrix = mv_matrix.multiply(mv_b.multiply(mv_c.multiply(mv_d)));

comparing to C++

float f = (float)currentTime * 0.3f;
vmath::mat4 mv_matrix = vmath::translate(0.0f, 0.0f, -4.0f) *
    vmath::translate(sinf(2.1f * f) * 0.5f,
        cosf(1.7f * f) * 0.5f,
        sinf(1.3f * f) * cosf(1.5f * f) * 2.0f) *
    vmath::rotate((float)currentTime * 45.0f, 0.0f, 1.0f, 0.0f) *
    vmath::rotate((float)currentTime * 81.0f, 1.0f, 0.0f, 0.0f);

glUniformMatrix4fv(mv_location, 1, GL_FALSE, mv_matrix);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);

The code above should be where the problem are, and the problem is the following:

This is the intended behavior from the original C++ implementation in the book, a cube moving and spinning on a green screen:

However, my version doesn’t have the cube present, only a blank green screen, so I tried to identify the problem of the code, starting by removing the CULL_FACE, DEPTH_TEST, and LEQUAL so that the cube can be appeared out of bound, and I have gotten some random stripes instead of a cube:

For that reason, I knew the projection messed up, so I have removed the proj_matrix in my shader code such that the gl_position only calculates the transformation and the given vertices of the cubes:

It seems like something has rendered, but it looks like a truncated pyramid rather than a cube, and this happens whenever the first translation of the mv_matrix have a non-zero value in the z axis.

Thus, here are some questions:

  • Any one has some luck on generating your first cube using zig with OpenGL?
  • For people who have used the OpenGL binding generator (or other gl libraries and bindings), did I messed on configuring the buffers, vertex Attributes and uniforms?
  • For any zm users, based on the matrix transforms, did I use the library wrong, passing some wrong types?

I know this question might lack details and it is more complex as usual, so here is the repo about my “cube” and feel free to ask any questions so that I could give you a more precise information.

I would start by looking for obvious convention differences between vmath and zm (e.g. row-major vs column-major matrix memory layout and left- vs right-handed coordinate system).

Next I would run both programs in RenderDoc (https://renderdoc.org/) and check how the data that arrives at the GL driver level differs between the C++ and the Zig version.

2 Likes

Thanks, I guess I will have a look to the convention of the matrix because I have found something during the time sounding like what you have mentioned, but not sure if that is the case.

During seeking the support, I decided to step into the original C++ vmath library, and to see if the c++ produce the same result as the zig implementation; thus, I have extract the whole header into a separated C++ project and did a test as the following:

#include <iostream>
#include "vmath.h"

int main()
{
    vmath::mat4 proj_matrix = vmath::perspective(50.0f,
        8.0f / 6.0f,
        0.1f,
        1000.0f);


    for (int i = 0; i < 16; i++) {
        printf("%f ", proj_matrix[i/4][i%4]);

        if (i%4 == 3) {
            std::cout << "\n";
        }
    }
}

After I have run the code, I have this following result:

1.608380 0.000000 0.000000 0.000000
0.000000 2.144507 0.000000 0.000000
0.000000 0.000000 -1.000200 -1.000000
0.000000 0.000000 -0.200020 0.000000

And I did the same of with the zm library:

const std = @import("std");
const zm = @import("zm");

pub fn main() !void {
    const proj_matrix = zm.Mat4f.perspective(
        std.math.degreesToRadians(50),
        8.0 / 6.0,
        0.1,
        1000,
    );

    std.debug.print("\n", .{});
    for (0..16) |i| {
        std.debug.print("{d} ", .{proj_matrix.data[i]});
        if (i % 4 == 3) {
            std.debug.print("\n", .{});
        }
    }
}

However, after I have run the code, I have the “transposed” version of the C++ result, which you can see that -0.20002 and -1 at the bottom right are swapped:

1.6083802 0 0 0 
0 2.144507 0 0 
0 0 -1.0001999 -0.20002 
0 0 -1 0 

Because of that observation, I decided to transpose all the matrix calculation for both mv_matrix and proj_matrix before putting into the buffer, and… it worked…

Btw, the glUniformMatrix calls have a flag for loading the transpose of the matrix:

void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);

Yeah, I also found that out during a conversation on the zm issue board. I will update that to true and remove the transpose function later.