Skip to content

Instantly share code, notes, and snippets.

@tseijp
Last active February 3, 2024 15:25
Show Gist options
  • Save tseijp/46cfdcac07deece0a6a41d2bf0f3aaa5 to your computer and use it in GitHub Desktop.
Save tseijp/46cfdcac07deece0a6a41d2bf0f3aaa5 to your computer and use it in GitHub Desktop.
// #define USE_MAIN 1
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifndef PI
#define PI 3.14159265359
#endif // PI
#ifndef TWO_PI
#define TWO_PI 6.28318530718
#endif // TWO_PI
#ifndef WIDTH
#define WIDTH 800
#endif // WIDTH
#ifndef HEIGHT
#define HEIGHT 600
#endif // HEIGHT
/**
* color: r と g と b の成分を持つ RGB 値の色を表す構造体です。
* vec2: x と y の成分を持つ 2D ベクトルを表す構造体です。
* mix: 三番目の a の値に基づいて二つの値を線形変換します。計算方法は x*(1-a) + y*a です。
* clamp: 値を指定された範囲に制限し、min(max(x, a), b) を返します。
* length: ベクトルの長さ、つまり原点からのユークリッド距離を計算します。√(x^2 + y^2) です。
* rgba: アルファに基づく線形ブレンディングを使用して色の RGBA 値を変更します。
*/
struct color { float r, g, b; };
struct vec2 { float x, y; };
float mix(float x, float y, float a) { return x * (1.0 - a) + y * a; }
float clamp(float x, float a, float b) { return x < a ? a : (b < x ? b : x); }
float length(float x, float y) { return sqrt(x * x + y * y); }
void rgba(struct color *c, float r, float g, float b, float a) {
c->r = clamp(mix(c->r, r * 255.0, a), 0.0, 255.0);
c->g = clamp(mix(c->g, g * 255.0, a), 0.0, 255.0);
c->b = clamp(mix(c->b, b * 255.0, a), 0.0, 255.0);
}
/**
* fragColor: 描画された画像のピクセルの出力色 RGB を表します。
* fragCoord: 現在のフラグメント/ピクセルの座標 [i, j] を表します。
* iPosition: 計算に使用される描画内の特定の位置を表します。今回は三角形の重心を使用します。
* iResolution: アスペクト比に影響を与える描画の解像度 [WIDTH, HEIGHT] です。
* triangleDistance: 三角形との距離を計算します。 cos(θ) * √(dx′^2 + dy^2) で計算します。
* fragmentShader: 最終色を計算する関数で、(1 - [2 * √(dx′^2 + dy^2)])^4 で計算します。
*/
struct color fragColor = { 0.0, 0.0, 0.0 };
struct vec2 fragCoord = { 0.0, 0.0 };
struct vec2 iPosition = { 0.0, 0.0 };
struct vec2 iResolution = { WIDTH, HEIGHT };
struct color iColor = { 0.0, 0.0, 0.0 };
float iScale = 1.0;
float triangleDistance(float x, float y) {
float angle = atan2(x, y) + PI;
float radius = TWO_PI / 3.0;
float distance = cos(floor(0.5 + angle / radius) * radius - angle) * length(x, y);
return distance;
}
void fragmentShader(struct color *fragColor, struct vec2 *fragCoord) {
// calculate uv
float x = fragCoord->x / iResolution.x; // 0.0 ~ 1.0
float y = fragCoord->y / iResolution.y; // 0.0 ~ 1.0
float dx = iPosition.x - x; // x distance
float dy = iPosition.y - y; // y distance
dx *= iResolution.x / iResolution.y; // aspect ratio
// draw double triangle
float d0 = triangleDistance(dx / iScale, dy / iScale);
float d1 = triangleDistance(dx / iScale, dy / iScale * -1.0); // flip
if (d0 < 1.0 || d1 < 1.0)
rgba(fragColor, iColor.r, iColor.g, iColor.b, 1.0);
// draw fake glow
float a = length(dx, dy); // alpha channel
a *= 2.0;
a = clamp(a, 0.0, 1.0); // 0.0 ~ 1.0
a *= -1.0; // 0.0 ~ -1.0
a += 1.0; // 1.0 ~ 0.0
a *= a; // 1.0 ~ 0.0 as a^2
a *= a; // 1.0 ~ 0.0 as a^4
rgba(fragColor, 1.0, 1.0, 0.5, a); // #ffff88 ~ #000000
}
/**
* img
*/
static unsigned char buf[HEIGHT][WIDTH][3];
static int filecnt = 0;
static char fname[100];
void img_getpixel(struct color *c, int x, int y) {
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
c->r = buf[HEIGHT - y - 1][x][0];
c->g = buf[HEIGHT - y - 1][x][1];
c->b = buf[HEIGHT - y - 1][x][2];
}
void img_putpixel(struct color *c, int x, int y) {
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
buf[HEIGHT - y - 1][x][0] = c->r;
buf[HEIGHT - y - 1][x][1] = c->g;
buf[HEIGHT - y - 1][x][2] = c->b;
}
void img_clear(void) {
for(int i = 0; i < WIDTH; ++i)
for(int j = 0; j < HEIGHT; ++j)
buf[j][i][0] = buf[j][i][1] = buf[j][i][2] = 0.0;
}
void img_write(void) {
sprintf(fname, "tmp/img%04d.ppm", ++filecnt);
FILE *f = fopen(fname, "wb");
if (f == NULL) { fprintf(stderr, "can't open %s\n", fname); exit(1); }
fprintf(f, "P6\n%d %d\n255\n", WIDTH, HEIGHT);
fwrite(buf, sizeof(buf), 1, f);
fclose(f);
}
void img_filltriangle(struct color c, float x0, float y0, float x1, float y1, float x2, float y2) {
iPosition.x = (x0 + x1 + x2) / 3.0 / iResolution.x;
iPosition.y = (y0 + y1 + y2) / 3.0 / iResolution.y;
iScale = (x1 - x0) / iResolution.x;
iColor.r = c.r / 255.0;
iColor.g = c.g / 255.0;
iColor.b = c.b / 255.0;
// loop of hell
for (int i = 0; i < WIDTH; fragCoord.x = ++i)
for (int j = 0; j < HEIGHT; fragCoord.y = ++j) {
img_getpixel(&fragColor, i, j);
fragmentShader(&fragColor, &fragCoord);
img_putpixel(&fragColor, i, j);
}
}
/**
* main
*/
#ifdef USE_MAIN
int main(void) {
struct color c1 = { 30, 255, 0 };
struct color c2 = { 255, 0, 0 };
struct color c3 = { 255, 0, 255 };
struct color c4 = { 255, 255, 0 };
struct color c5 = { 0, 255, 255 };
struct color c6 = { 255, 138, 255 };
int i;
for(i = 0; i < 40; ++i) {
int x = -20 + i * 50;
int y = -20 + i * 30;
img_clear();
img_filltriangle(c4, 600 - x , 545 - y , 515 - x , 400 - y , 685 - x , 400 - y );
img_filltriangle(c4, 600 - x , 350 - y , 515 - x , 500 - y , 685 - x , 500 - y );
img_filltriangle(c4, 300 - x , 245 - y , 215 - x , 100 - y , 385 - x , 100 - y );
img_filltriangle(c4, 300 - x , 50 - y , 215 - x , 200 - y , 385 - x , 200 - y );
img_filltriangle(c5, 250 - 5 * x, 195 - 5 * y, 165 - 5 * x, 50 - 5 * y, 335 - 5 * x, 50 - 5 * y);
img_filltriangle(c5, 250 - 5 * x, 0 - 5 * y, 165 - 5 * x, 150 - 5 * y, 335 - 5 * x, 150 - 5 * y);
img_filltriangle(c6, 250 - 2 * x, 145 - 2 * y, 225 - 2 * x, 100 - 2 * y, 275 - 2 * x, 100 - 2 * y);
img_filltriangle(c6, 250 - 2 * x, 85 - 2 * y, 225 - 2 * x, 130 - 2 * y, 275 - 2 * x, 130 - 2 * y);
img_write();
}
return 0;
}
#endif // USE_MAIN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment