Last active
December 6, 2019 04:24
-
-
Save hyperlogic/5c11f3c86970a5d2a1ac0e8367708ef6 to your computer and use it in GitHub Desktop.
ctoy 2d sdf renderer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static struct m_image buffer = M_IMAGE_IDENTITY(); | |
#include <math.h> | |
#include <m_math.h> | |
#include <m_raster.h> | |
typedef struct Prim_t { | |
int type; // 0 = sphere, 1 = box | |
float m[6]; | |
float inv_m[6]; | |
float r[2]; | |
} Prim; | |
#define NUM_PRIMS 20 | |
Prim prims[NUM_PRIMS]; | |
static float sign(float v) { | |
return v > 0.0f ? 1.0f : 0.0f; | |
} | |
// transform point p by the 2x3 homogenous matrix m | |
// | m[0] m[2] m[4] | | p[0] | | r[0] | | |
// | m[1] m[3] m[5] | * | p[1] | = | r[1] | | |
// | 0 0 1 | | 1 | | | | |
static float* xform_2x3(float *r, float *m, float *p) { | |
float temp[2]; | |
temp[0] = m[0] * p[0] + m[2] * p[1] + m[4]; | |
temp[1] = m[1] * p[0] + m[3] * p[1] + m[5]; | |
r[0] = temp[0]; | |
r[1] = temp[1]; | |
} | |
// transform p by a 2x2 matrix. | |
// | m[0] m[2] | * | p[0] | = | r[0] | | |
// | m[1] m[3] | | p[1] | | r[1] | | |
static void xform_2x2(float *r, float *m, float *p) { | |
float temp[2]; | |
temp[0] = m[0] * p[0] + m[2] * p[1]; | |
temp[1] = m[1] * p[0] + m[3] * p[1]; | |
r[0] = temp[0]; | |
r[1] = temp[1]; | |
} | |
// transpose a 2x2 matrix | |
static void transpose_2x2(float *r, float *m) { | |
r[0] = m[0]; | |
float temp = m[1]; | |
r[1] = m[2]; | |
r[2] = temp; | |
r[3] = m[3]; | |
} | |
// invert a orthonormal 2x3 matrix. | |
static void orthonormal_invert_2x3(float *r, float *m) { | |
transpose_2x2(r, m); | |
float trans[2] = {-m[4], -m[5]}; | |
xform_2x2(trans, r, trans); | |
r[4] = trans[0]; | |
r[5] = trans[1]; | |
} | |
static float min(float a, float b) { | |
return (a < b) ? a : b; | |
} | |
static float max(float a, float b) { | |
return (a <= b) ? b : a; | |
} | |
static float sdf_box(float *p, Prim* prim) { | |
// vec2 d = abs(p) - r; | |
// return length(max(d, vec2(0))) + min(max(d.x, d.y), 0.0); | |
float d[2] = {fabs(p[0]) - prim->r[0], fabs(p[1]) - prim->r[1]}; | |
float m[2] = {max(d[0], 0.0f), max(d[1], 0.0f)}; | |
float a = sqrt(m[0] * m[0] + m[1] * m[1]); | |
float b = min(max(d[0], d[1]), 0.0f); | |
return a + b; | |
} | |
static float sdf_sphere(float *p, Prim* prim) { | |
// return length(p) - r; | |
return sqrtf(p[0] * p[0] + p[1] * p[1]) - prim->r[0]; | |
} | |
static float sdf_prim(float* p, Prim* prim) { | |
float local_p[2]; | |
switch (prim->type) { | |
default: | |
case 0: | |
// Shortcut: don't need to rotate by inv_m, just translate. | |
local_p[0] = p[0] + prim->inv_m[4]; | |
local_p[1] = p[1] + prim->inv_m[5]; | |
return sdf_sphere(local_p, prim); | |
case 1: | |
// transform from global into local space | |
xform_2x3(local_p, prim->inv_m, p); | |
return sdf_box(local_p, prim); | |
} | |
} | |
// evaluate sdf at point p | |
static float map(float* p) { | |
float dist = FLT_MAX; | |
int i; | |
for (i = 0; i < NUM_PRIMS; i++) { | |
dist = min(dist, sdf_prim(p, prims + i)); | |
} | |
return dist; | |
} | |
static void draw(void) { | |
int x, y; | |
for (y = 0; y < buffer.height; y++) { | |
for (x = 0; x < buffer.width; x++) { | |
float *pixel = (float *)buffer.data + (y * buffer.width + x) * 3; | |
// convert from "viewport" coordinates into "world" space | |
float p[2] = {(float)x / (float)buffer.width, (float)y / (float)buffer.height}; | |
float dist = map(p); | |
// convert dist into a color (with fancy stripes) | |
float s1 = 1.0f - expf(-3.0f * fabsf(dist)); | |
float s2 = 0.9f + 0.3f * cosf(180.0f * dist); | |
float shade = s1 * s2; | |
float color[3] = {(1.0f - sign(dist) * 0.1f) * shade, | |
(1.0f - sign(dist) * 0.4f) * shade, | |
(1.0f - sign(dist) * 0.7f) * shade}; | |
pixel[0] = color[0]; | |
pixel[1] = color[1]; | |
pixel[2] = color[2]; | |
} | |
} | |
} | |
static void random_matrix(float* m) { | |
float theta = m_randf() * 2.0f * M_PI; | |
m[0] = cosf(theta); | |
m[1] = sinf(theta); | |
m[2] = m[1]; | |
m[3] = -m[0]; | |
m[4] = m_randf(); | |
m[5] = m_randf(); | |
} | |
void ctoy_begin(void) { | |
ctoy_window_title("SDF"); | |
ctoy_window_size(512, 512); | |
m_image_create(&buffer, M_FLOAT, 256, 256, 3); | |
// initialize prims. | |
prims[0].type = 0; | |
prims[0].m[0] = 1.0f; prims[0].m[2] = 0.0f; prims[0].m[4] = 0.0f; | |
prims[0].m[1] = 0.0f; prims[0].m[3] = 1.0f; prims[0].m[5] = 0.0f; | |
orthonormal_invert_2x3(prims[0].inv_m, prims[0].m); | |
prims[0].r[0] = 0.5f; | |
prims[0].r[1] = 0.5f; | |
prims[1].type = 0; | |
prims[1].m[0] = 1.0f; prims[1].m[2] = 0.0f; prims[1].m[4] = 0.0f; | |
prims[1].m[1] = 0.0f; prims[1].m[3] = 1.0f; prims[1].m[5] = 0.0f; | |
orthonormal_invert_2x3(prims[1].inv_m, prims[1].m); | |
prims[1].r[0] = 0.2f; | |
prims[1].r[1] = 0.1f; | |
prims[2].type = 1; | |
prims[2].m[0] = 1.0f; prims[2].m[2] = 0.0f; prims[2].m[4] = 0.2f; | |
prims[2].m[1] = 0.0f; prims[2].m[3] = 1.0f; prims[2].m[5] = 0.5f; | |
orthonormal_invert_2x3(prims[2].inv_m, prims[2].m); | |
prims[2].r[0] = 0.5f; | |
prims[2].r[1] = 0.05f; | |
int i; | |
for (i = 3; i < NUM_PRIMS; i++) { | |
prims[i].type = m_randf() > 0.5f ? 0 : 1; | |
random_matrix(prims[i].m); | |
orthonormal_invert_2x3(prims[i].inv_m, prims[i].m); | |
prims[i].r[0] = m_randf() * 0.1f; | |
prims[i].r[1] = m_randf() * 0.1f; | |
} | |
} | |
void ctoy_end(void) { | |
m_image_destroy(&buffer); | |
} | |
void ctoy_main_loop(void) { | |
float t = ctoy_t() * 0.01f; | |
// animate prim1 in a circle | |
prims[1].m[4] = sinf(t) * 0.5f + 0.5; | |
prims[1].m[5] = cosf(t) * 0.5f + 0.5; | |
orthonormal_invert_2x3(prims[1].inv_m, prims[1].m); | |
// rotate prim2 | |
float u[2] = {cosf(t), sinf(t)}; | |
float v[2] = {u[1], -u[0]}; | |
prims[2].m[0] = u[0]; | |
prims[2].m[1] = u[1]; | |
prims[2].m[2] = v[0]; | |
prims[2].m[3] = v[1]; | |
orthonormal_invert_2x3(prims[2].inv_m, prims[2].m); | |
draw(); | |
ctoy_swap_buffer(&buffer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment