zeroGL

A zero-dependencies, single-header 3D graphics library



Demo Download

PRO TIP Download and add this to you C file:
#include "zerogl.h"


Features


Getting Started


#include "zerogl.h"

// Use the stb_image library (not included with zeroGL) to write the result image to a file
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

#define WIDTH 1066
#define HEIGHT 600
uint32_t pixels[WIDTH*HEIGHT];

int main(void) {
    zgl_canvas_t canvas = {WIDTH, HEIGHT, pixels, 0, NULL};

    zgl_render_fill(ZGL_COLOR_BLACK, canvas)
    zgl_render_triangle(400, 400, ZGL_COLOR_RED,
                        700, 400, ZGL_COLOR_GREEN,
                        700, 100, ZGL_COLOR_BLUE,
                        canvas, 0);

    const char *file_path = "image.png";
    if (!stbi_write_png(file_path, WIDTH, HEIGHT, 4, pixels, sizeof(uint32_t)*WIDTH)) {
        fprintf(stderr, "ERROR: could not write %s\n", file_path);
        return 1;
    }
    return 0;
}
      

Settings

You can #define ZGL_DEBUG to enable debug logs on the console. Also, you can define the following variables to customize the program behavior


// Maximum number of vertex attributes passed between the vertex and fragment shaders
#define ZGLMAX_VERTEX_SHADER_ATTRIBUTES 100
          

Vectors

Vectors are represented by two types. The first is vec3_t, having an x, y and z attributes of type float


zgl_vec3_t v = {1.0f, 2.0f, 3.0f};
          

Colors

Colors are represented as a uint32_t in the form of 0xAARRGGBB, where AA is the alpha channel, RR is the red channel, GG is the green channel and BB is the blue channel.

You can manipulate colors by using the following functions:


uint32_t zgl_color(uint8_t r, uint8_t g, uint8_t b)
void     zgl_color_components(uint32_t c, uint8_t* r, uint8_t* g, uint8_t* b)
uint32_t zgl_mul_scalar_color(double x, uint32_t color)
uint32_t zgl_add_colors(uint32_t c0, uint32_t c1)
uint32_t zgl_color_from_floats(float r, float g, float b)
void     zgl_color_to_floats(uint32_t color, float* r, float* g, float* b)
        

3D Objects

You can define 3D objects using zgl_mesh_t and zgl_object3D_t type

Camera

You can define a camera using zgl_camera_t type. Create one using:


zgl_camera_t zgl_camera(zgl_vec3_t position, zgl_vec3_t direction, zgl_vec3_t up,
                        float fov, float aspectRatio, float near, float far,
                        float movementSpeed, float turningSpeed)
          

Drawing

To draw, you need to create a zgl_canvas_t type and pass it to the drawing functions


zgl_canvas_t canvas = {WIDTH, HEIGHT, pixels, 0, NULL};
          

You can draw primitives such as lines, triangles and 3D objects using the following functions


void zgl_render_pixel(int x, int y, uint32_t color, zgl_canvas_t canvas)
void zgl_render_fill(uint32_t color, zgl_canvas_t canvas)
void zgl_render_line(int x0, int x1, int y0, int y1, uint32_t color, zgl_canvas_t canvas)
void zgl_render_circle(int x, int y, int radius, uint32_t color, zgl_canvas_t canvas)
void zgl_render_triangle(int x0, int y0, uint32_t color0,
                         int x1, int y1, uint32_t color1,
                         int x2, int y2, uint32_t color2,
                         zgl_canvas_t canvas, uint16_t renderOptions)
void zgl_render_object3D(zgl_object3D_t* obj, void *uniform, zgl_camera_t cam, zgl_canvas_t c,
                         zgl_vertex_shader_t vs, zgl_fragment_shader_t fs, uint16_t renderOptions)
          

Shaders

You can customize zgl_render_object3D behavior by passing custom shaders. Shaders are written in pure C code. You can define vertex and fragment shaders following the type:


zgl_shader_context_t zgl_vertex_shader_t(void* inputVertex, void* uniformData);
uint32_t             zgl_fragment_shader_t(zgl_shader_context_t* input, void* uniformData,
                                           int textureWidth, int textureHeight, uint32_t* texture);
          

Vertex shaders receive a pointer to the vertex data and a pointer to the uniform data. They must return a shader_context_t type, which is a struct containing the transformed vertex data.

Fragment shaders receive a pointer to the shader context (containing the interpolated data from the vertexes of each triangle), a pointer to the uniform data, the texture width and height and a pointer to the texture data. They must return a uint32_t type, which is the color of the pixel.