zeroGL

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



Demo Download

PRO TIP Download and add this to you C file:


#define ZERGL_IMPLEMENTATION
#include "zerogl.h"
      


Features


Getting Started


#define ZERGL_IMPLEMENTATION
#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_clear_depth_buffer(canvas);
    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
          

Linear Algebra

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

Matrices

ZeroGL provides a 4x4 matrix type called zgl_mat4x4_t, with float elements


zgl_mat4x4_t m = {{
  {1.0, 0.0, 0.0, 0.0},
  {0.0, 1.0, 0.0, 0.0},
  {0.0, 0.0, 1.0, 0.0},
  {0.0, 0.0, 0.0, 1.0}
}};
          

You can manipulate vectors and matrices using the following functions:


zgl_vec3_t   zgl_cross(zgl_vec3_t a, zgl_vec3_t b);
float        zgl_dot(zgl_vec3_t a, zgl_vec3_t b);
float        zgl_dot_v4(zgl_vec4_t a, zgl_vec4_t b);
float        zgl_magnitude(zgl_vec3_t v);
zgl_vec3_t   zgl_sub(zgl_vec3_t a, zgl_vec3_t b);
zgl_vec4_t   zgl_sub_v4(zgl_vec4_t a, zgl_vec4_t b);
zgl_vec3_t   zgl_add(zgl_vec3_t a, zgl_vec3_t b);
zgl_vec3_t   zgl_add_three_vec3(zgl_vec3_t a, zgl_vec3_t b, zgl_vec3_t c);
zgl_vec4_t   zgl_add_v4(zgl_vec4_t a, zgl_vec4_t b);
zgl_vec3_t   zgl_normalize(zgl_vec3_t v);
zgl_vec3_t   zgl_mul_scalar(float k, zgl_vec3_t v);
zgl_vec4_t   zgl_mul_mat_v4(zgl_mat4x4_t mat4x4, zgl_vec4_t vec4);
zgl_vec3_t   zgl_mul_mat_v3(zgl_mat4x4_t mat4x4, zgl_vec3_t v);
zgl_mat4x4_t zgl_mul_mat(zgl_mat4x4_t m1, zgl_mat4x4_t m2);
zgl_mat4x4_t zgl_transpose(zgl_mat4x4_t m);
zgl_mat4x4_t zgl_inverse(zgl_mat4x4_t matrix);
zgl_mat4x4_t zgl_translation_mat(zgl_vec3_t vector);
zgl_mat4x4_t zgl_scale_mat(float scale);
zgl_mat4x4_t zgl_rotx_mat(float degrees);
zgl_mat4x4_t zgl_roty_mat(float degrees);
zgl_mat4x4_t zgl_rotz_mat(float degrees);
          

Quaternions

ZeroGL provides a quaternion type called zgl_quaternion_t, with float elements


zgl_quaternion_t q = {1.0f, 0.0f, 0.0f, 0.0f};
          

You can manipulate quaternions using the following functions:


zgl_quaternion_t zgl_quaternion(float degrees, zgl_vec3_t axis);
zgl_quaternion_t zgl_mul_quat(zgl_quaternion_t q1, zgl_quaternion_t q2);
zgl_vec3_t       zgl_rotate(zgl_vec3_t v, zgl_quaternion_t q);
          

Colors

Colors are represented as a uint32_t in the pixel format of choice. The default format is ZEROGL_PIXELFORMAT_RGBA8888, so colors are represented using the form 0xRRGGBBAA where AA is the alpha channel, RR is the red channel, GG is the green channel and BB is the blue channel. This are all the supported formats:


ZEROGL_PIXELFORMAT_RGBA8888
ZEROGL_PIXELFORMAT_ARGB8888
ZEROGL_PIXELFORMAT_BGRA8888
ZEROGL_PIXELFORMAT_ABGR8888
          

The formats are compatible with the respective formats in SDL. The pixel format can be set by defining the following variable before importing zeroGL:


#define ZEROGL_PIXELFORMAT ZEROGL_PIXELFORMAT_RGBA8888
          

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_mul_vec3_color(zgl_vec3_t v, 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

Lighting

ZeroGL provides a simple lighting system. You can define a light using three diffent structs: zgl_ambient_light_t, zgl_point_light_t and zgl_dir_light_t. You can bundle all lights in a scene in a zgl_light_sources_t. To compute lights in a point, you can just use:


zgl_lighting_result_t zgl_lighting(zgl_vec3_t position, zgl_vec3_t normal, float invMagnitudeNormal, float specularExponent,
                                   zgl_light_sources_t lightSources, uint8_t renderOptions);
          
This function returns a zgl_lighting_result_t struct containing the specular, diffuse and ambient light intensity at the point.

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)
          

Texture and Materials

You can define a texture using the zgl_canvas_t struct (the same type used for the framebuffer). ZeroGL also provides a simple material system. Define a material using the zgl_material_t struct:


typedef struct {
    char*        name;
    uint32_t     diffuseColor;
    uint32_t     specularColor;
    float        specularExponent;
    zgl_canvas_t diffuseTexture;
    zgl_canvas_t specularTexture;
} zgl_material_t;
          

Rendering

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_clear_depth_buffer(zgl_canvas_t canvas)
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)
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);
          

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. They must return a uint32_t type, which is the color of the pixel.

Default Shaders

ZeroGL provides default shaders for you to use

Basic Shader

Draw with a single color, no lighting or textures


typedef struct {
    zgl_mat4x4_t modelviewprojection;
} zgl_basic_uniform_t;

zgl_shader_context_t zgl_basic_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_basic_fragment_shader(const zgl_shader_context_t* input, void* uniformData);
          
Colored Shader

Draw with the color of the vertex, no lighting or textures


typedef struct {
    zgl_mat4x4_t modelviewprojection;
} zgl_colored_uniform_t;

zgl_shader_context_t zgl_colored_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_colored_fragment_shader(const zgl_shader_context_t* input, void* uniformData);
          
Unlit Shader

Draw with colors and textures, no lighting


typedef struct {
    zgl_mat4x4_t        modelMatrix;
    zgl_mat4x4_t        modelInvRotationMatrixTransposed;
    zgl_mat4x4_t        viewProjectionMatrix;
    int                 bilinearFiltering;
    zgl_material_t*     materials;
} zgl_unlit_uniform_t;

zgl_shader_context_t zgl_unlit_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_unlit_fragment_shader(const zgl_shader_context_t* input, void* uniformData);
          
Flat Shader

Compute the lighting at one vertex and use it for the whole triangle


typedef struct {
    zgl_mat4x4_t        modelMatrix;
    zgl_mat4x4_t        modelInvRotationMatrixTransposed;
    zgl_mat4x4_t        viewProjectionMatrix;
    zgl_light_sources_t lightSources;
    int                 bilinearFiltering;
    zgl_material_t*     materials;
} zgl_flat_uniform_t;

zgl_shader_context_t zgl_flat_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_flat_fragment_shader(const zgl_shader_context_t* input, void* uniformData);
          
Gourard Shader

Compute the lighting at each vertex and interpolate the values at each pixel


typedef struct {
    zgl_mat4x4_t        modelMatrix;
    zgl_mat4x4_t        modelInvRotationMatrixTransposed;
    zgl_mat4x4_t        viewProjectionMatrix;
    zgl_light_sources_t lightSources;
    int                 bilinearFiltering;
    zgl_material_t*     materials;
} zgl_gourard_uniform_t;

zgl_shader_context_t zgl_gourard_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_gourard_fragment_shader(const zgl_shader_context_t* input, void* uniformData);
          
Phong Shader

Compute the lighting per pixel


typedef struct {
    zgl_mat4x4_t        modelMatrix;
    zgl_mat4x4_t        modelInvRotationMatrixTransposed;
    zgl_mat4x4_t        viewProjectionMatrix;
    zgl_light_sources_t lightSources;
    int                 bilinearFiltering;
    zgl_material_t*     materials;
} zgl_phong_uniform_t;

zgl_shader_context_t zgl_phong_vertex_shader(void* inputVertex, void* uniformData);
uint32_t zgl_phong_fragment_shader(const zgl_shader_context_t* input, void* uniformData);