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