commit 5571e58e66502b61a2a0624ee3f14f355d430363
parent 9cfeff00354c5e27f4f1c878beae84e0e337d07b
Author: Robert Russell <robert@rr3.xyz>
Date: Fri, 30 Aug 2024 20:51:35 -0700
Play around with gradients
Diffstat:
| M | main.c | | | 150 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
1 file changed, 104 insertions(+), 46 deletions(-)
diff --git a/main.c b/main.c
@@ -12,6 +12,8 @@ typedef Vec2 Edge2[2];
typedef Vec2 Quad2[2];
typedef struct cycle2 Cycle2;
typedef struct image2 Image2;
+typedef struct linear_gradient LinearGradient;
+typedef void Shader(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg);
struct cycle2 {
usize len;
@@ -24,13 +26,28 @@ struct image2 {
Vec4 *pixels; // Row major order, starting in upper left
};
+struct linear_gradient {
+ Vec2 pos0;
+ Vec4 color0;
+ Vec2 pos1;
+ Vec4 color1;
+};
+
f32 clampf(f32 x, f32 min, f32 max);
void vec2_add(Vec2 *r, Vec2 u, Vec2 v);
void vec3_add(Vec3 *r, Vec3 u, Vec3 v);
void vec4_add(Vec4 *r, Vec4 u, Vec4 v);
+void vec2_sub(Vec2 *r, Vec2 u, Vec2 v);
+void vec3_sub(Vec3 *r, Vec3 u, Vec3 v);
+void vec4_sub(Vec4 *r, Vec4 u, Vec4 v);
void vec2_mul(Vec2 *r, f32 a, Vec2 v);
void vec3_mul(Vec3 *r, f32 a, Vec3 v);
void vec4_mul(Vec4 *r, f32 a, Vec4 v);
+f32 vec2_dot(Vec2 u, Vec2 v);
+f32 vec3_dot(Vec3 u, Vec3 v);
+f32 vec4_dot(Vec4 u, Vec4 v);
+f32 vec2_hypot(Vec2 v);
+f32 vec2_dist(Vec2 u, Vec2 v);
void aff3_mul(Aff3 *r, Aff3 a, Aff3 b);
void aff3_app(Vec2 *r, Aff3 a, Vec2 b);
void aff3_scale(Aff3 *r, f32 x, f32 y);
@@ -40,7 +57,7 @@ f32 quad2_width(Quad2 q);
f32 quad2_height(Quad2 q);
void quad2_transform(Quad2 *r, Aff3 t, Quad2 q);
void image2_bbox(Quad2 *r, Image2 image);
-int fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color);
+int fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg);
f32
clampf(f32 x, f32 min, f32 max) {
@@ -53,10 +70,21 @@ void vec2_add(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] =
void vec3_add(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] + v[i]; }
void vec4_add(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] + v[i]; }
+void vec2_sub(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = u[i] - v[i]; }
+void vec3_sub(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] - v[i]; }
+void vec4_sub(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] - v[i]; }
+
void vec2_mul(Vec2 *r, f32 a, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = a * v[i]; }
void vec3_mul(Vec3 *r, f32 a, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = a * v[i]; }
void vec4_mul(Vec4 *r, f32 a, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = a * v[i]; }
+f32 vec2_dot(Vec2 u, Vec2 v) { return u[0] * v[0] + u[1] * v[1]; }
+f32 vec3_dot(Vec3 u, Vec3 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; }
+f32 vec4_dot(Vec4 u, Vec4 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3]; }
+
+f32 vec2_hypot(Vec2 v) { return hypotf(v[0], v[1]); }
+f32 vec2_dist(Vec2 u, Vec2 v) { return hypotf(v[0] - u[0], v[1] - u[1]); }
+
void
aff3_mul(Aff3 *r, Aff3 a, Aff3 b) {
SET(*r, ((Aff3){
@@ -138,7 +166,7 @@ image2_bbox(Quad2 *r, Image2 image) {
// TODO: Support filling collections of cycles (like Postscript), not only one
int
-fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) {
+fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg) {
if (cycle.len < 3) return -1;
Quad2 dst_view;
@@ -330,17 +358,14 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) {
}
}
+ f32 y = ((f32)(image.h - i) - 0.5f) / (f32)image.h;
f32 s = 0.0f;
for (usize j = 0; j < image.w; j++) {
s += deltas[j];
- f32 a = clampf(values[j] + s, 0.0f, 1.0f);
-
- // TODO: More blending options.
- Vec4 c;
- vec4_mul(&c, a, color);
- Vec4 *p = &image.pixels[i * image.h + j];
- vec4_mul(p, 1.0f - c[3], *p);
- vec4_add(p, c, *p);
+ f32 alpha = clampf(values[j] + s, 0.0f, 1.0f);
+ Vec4 *pixel = &image.pixels[i * image.h + j];
+ f32 x = ((f32)j + 0.5f) / (f32)image.w;
+ shader(pixel, (Vec2){x, y}, alpha, shader_arg);
}
}
@@ -350,6 +375,35 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) {
}
void
+shader_over(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) {
+ Vec4 *color = arg;
+
+ Vec4 c;
+ vec4_mul(&c, alpha, *color);
+ vec4_mul(pixel, 1.0f - c[3], *pixel);
+ vec4_add(pixel, c, *pixel);
+}
+
+void
+shader_linear_gradient(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) {
+ LinearGradient *lg = arg;
+
+ // Project pos onto the gradient's axis.
+ // TODO: This is wrong somehow.
+ Vec2 v; vec2_sub(&v, lg->pos1, lg->pos0);
+ Vec2 w; vec2_sub(&w, pos, lg->pos0);
+ f32 t = vec2_dot(v, w) / vec2_hypot(v);
+
+ Vec4 color0; vec4_mul(&color0, 1.0f - t, lg->color0);
+ Vec4 color1; vec4_mul(&color1, t, lg->color1);
+ Vec4 color; vec4_add(&color, color0, color1);
+
+ vec4_mul(&color, alpha, color);
+ vec4_mul(pixel, 1.0f - color[3], *pixel);
+ vec4_add(pixel, color, *pixel);
+}
+
+void
write_farbfeld(FILE *f, Image2 image) {
char header[16];
memcpy(header, "farbfeld", 8);
@@ -376,60 +430,64 @@ write_farbfeld(FILE *f, Image2 image) {
}
}
-int
-main(void) {
- //Cycle2 cycle = {
- // .len = 3,
- // .verts = (Vec2[]){
- // { 0.0f, 0.0f },
- // { 1.0f, 0.0f },
- // { 0.5f, 1.0f },
- // },
- //};
-
- //Cycle2 cycle = {
- // .len = 4,
- // .verts = (Vec2[]){
- // { 0.2f, 0.2f },
- // { 0.8f, 0.2f },
- // { 0.8f, 0.8f },
- // { 0.2f, 0.8f },
- // },
- //};
-
+void
+fill_under(Image2 image, f32 f(f32), usize n, Shader shader, void *shader_arg) {
Cycle2 cycle = {
- .len = 6,
- .verts = (Vec2[]){
- { 0.1f, 0.1f },
- { 0.5f, 0.3f },
- { 0.9f, 0.1f },
- { 0.8f, 0.9f },
- { 0.5f, 0.5f },
- { 0.2f, 0.9f },
- },
+ .len = n + 3,
+ .verts = r_ealloc((n + 3) * sizeof(Vec2)),
};
+ SET(cycle.verts[0], ((Vec2){0.0f, 0.0f}));
+ SET(cycle.verts[1], ((Vec2){1.0f, 0.0f}));
+ for (usize i = 0; i <= n; i++) {
+ f32 x = 1.0f - (f32)i / (f32)n;
+ SET(cycle.verts[i+2], ((Vec2){x, f(x)}));
+ }
+
Quad2 cycle_view = {
{ 0.0f, 0.0f },
{ 1.0f, 1.0f },
};
+ if (fill(image, cycle_view, cycle, shader, shader_arg) < 0)
+ r_fatalf("fill error");
+
+ free(cycle.verts);
+}
+
+static f32 pi = acosf(-1.0f);
+f32 waves(f32 x) { return sinf(50.0f * pi * x) * 0.01f + 0.4f; }
+f32 mountains(f32 x) { return sinf(3.0f * pi * x) * 0.2f + sinf(100.0f * pi * x) * 0.003f + 0.4f; }
+
+int
+main(void) {
Image2 image = {
.w = 1000,
.h = 1000,
.pixels = r_ealloc(1000 * 1000 * sizeof(Vec4)),
};
- Vec4 white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ Vec4 bg = { 0.6f, 0.6f, 0.65f, 1.0f };
for (usize i = 0; i < image.h; i++) {
for (usize j = 0; j < image.w; j++)
- SET(image.pixels[i * image.h + j], white);
+ SET(image.pixels[i * image.h + j], bg);
}
- f32 a = 1.0f;
- Vec4 color = { 0.1f * a, 0.6f * a, 0.3f * a, a };
- if (fill(image, cycle_view, cycle, color) < 0)
- r_fatalf("fill error");
+ fill_under(image, waves, 1000, shader_linear_gradient,
+ &(LinearGradient){
+ .pos0 = { 0.0f, 0.0f },
+ .color0 = { 0.0f, 0.0f, 0.0f, 1.0f },
+ .pos1 = { 0.0f, 0.5f },
+ .color1 = { 0.3f, 0.3f, 0.9f, 1.0f },
+ });
+
+ fill_under(image, mountains, 1000, shader_linear_gradient,
+ &(LinearGradient){
+ .pos0 = { 0.0f, 0.0f },
+ .color0 = { 0.05f, 0.15f, 0.05f, 1.0f },
+ .pos1 = { 0.0f, 1.0f },
+ .color1 = { 0.15f, 0.5f, 0.15f, 1.0f },
+ });
write_farbfeld(stdout, image);
}