Last active
February 3, 2024 15:25
-
-
Save tseijp/46cfdcac07deece0a6a41d2bf0f3aaa5 to your computer and use it in GitHub Desktop.
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
// #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