summaryrefslogtreecommitdiff
path: root/src/fshader_main.glsl
blob: 0f13a019b553de394c608c4cfe8d518c216e96c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// this draws the scene

IN vec2 pos;
uniform mat3 u_rotation;
uniform vec3 u_translation;
uniform float u_time;
uniform float u_fov;
uniform float u_focal_length;
uniform float u_level_set;
uniform float u_distance_threshold;
uniform int u_hsv;
uniform ivec2 u_antialiasing;
uniform int u_iterations;
uniform float u_aspect_ratio;
uniform vec2 u_screen_size;
uniform vec4 u_flash;
uniform int u_flash_icon;

#define ICON_COPY 1
#define ICON_PLAY 2
#define ICON_PAUSE 3
#define ICON_REWIND 4

%COMMON%
%SDF%
%COLOR%

// see https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative
float hsvf(float n, vec3 hsv) {
	float k = mod(n + hsv.x * 6.0, 6.0);
	return hsv.z - hsv.z * hsv.y * clamp(min(k, 4.0 - k), 0.0, 1.0);
}

vec3 hsv_to_rgb(vec3 hsv) {
	hsv.yz = clamp(hsv.yz, 0.0, 1.0);
	return vec3(hsvf(5.0, hsv), hsvf(3.0, hsv), hsvf(1.0, hsv));
}

vec3 get_color(vec3 p) {
	if (u_hsv != 0) {
		vec3 hsv = clamp(get_color_(p), 0.0, 1.0);
		// make sure object isn't too dark so we can actually see it
		hsv.z = mix(hsv.z, 1.0, 0.5);
		return hsv_to_rgb(hsv);
	} else {
		vec3 color = clamp(get_color_(p), 0.0, 1.0);
		return mix(color, vec3(1.0), 0.2);
	}
}

#define ITERATIONS u_iterations
#define AA_X u_antialiasing.x
#define AA_Y u_antialiasing.y


float sdf_adjusted(vec3 p) {
	return sdf(p) - u_level_set;
}
#define sdf sdf_adjusted

vec3 normal(vec3 p)
{
	// thanks to https://iquilezles.org/articles/normalsSDF/
	float h = 0.0001;
	vec2 k = vec2(1.,-1.);
	vec3 sdf_normal = k.xyy*sdf(p + k.xyy*h) + 
	              k.yyx*sdf(p + k.yyx*h) + 
	              k.yxy*sdf(p + k.yxy*h) + 
	              k.xxx*sdf(p + k.xxx*h);
	return normalize(sdf_normal);
}

bool play_icon(vec2 pos) {
	vec2 a = abs(pos);
	if (a.x >= 0.5 || a.y >= 0.5)
		return false;
	return a.y < 0.25 + 0.5 * pos.x;
}

void main() {
	float min_dist = 10.;
	vec2 inv_screen_size = 1.0 / u_screen_size;
	vec2 aa_delta = inv_screen_size / vec2(AA_X, AA_Y);
	vec3 final_color = vec3(0);
	for (int m = 0; m < AA_X; m++) {
	for (int n = 0; n < AA_Y; n++) {
	vec2 aa_offset = vec2(float(m), float(n)) * aa_delta;
	vec3 pos3d = vec3((pos + aa_offset) * sin(u_fov * 0.5), -1.0) * u_focal_length;
	vec3 p = u_rotation * pos3d;
	vec3 delta = normalize(p);
	p += u_translation;
	if (sdf(p) < 0.0) {
		// looking inside object
		final_color += get_color(p);
		continue;
	}
	int i;
	for (i = 0; i < ITERATIONS; i++) {
		float dist = sdf(p);
		min_dist = min(min_dist, dist);
		if (dist > 100.0) break;
		p += dist * delta;
	}

	float threshold = u_distance_threshold;
	if (min_dist < threshold) {
		vec3 N = normal(p);
		// light direction = towards user
		// this makes it seem like the user is pointing a flashlight at the object.
		vec3 light_direction = u_rotation * vec3(0.0, 0.0, 1.0);
		float L_diffuse = max(0., dot(N, light_direction));
		// Phong lighting
		vec3 R = reflect(light_direction, N);
		vec3 view_direction = u_rotation * vec3(0.0, 0.0, -1.0);
		// wikipedia calls this exponent the "shininess" (α)
		float shininess = 16.0;
		float L_specular = pow(max(0.0, dot(R, view_direction)), shininess);
		float brightness = (1.0/threshold) * (threshold-min_dist);
		brightness = pow(brightness, 16.0);
		float L_ambient = 0.3;
		vec3 color = get_color(p);
		float specularity = 0.15; // strength of specular lighting
		final_color += brightness * mix(mix(L_diffuse, 1.0, L_ambient) * color, vec3(L_specular), specularity);
		break;
	}
	
	}
	}
	final_color *= 1.0 / (AA_X * AA_Y);
	bool icon = false;
	switch (u_flash_icon) {
	case 0: break;
	case ICON_COPY:
		icon = abs(pos.x) > u_aspect_ratio - 0.1 || abs(pos.y) > 0.9;
		break;
	case ICON_PLAY:
		icon = play_icon(pos);
		break;
	case ICON_REWIND:
		icon = play_icon(vec2(-pos.x, pos.y));
		break;
	case ICON_PAUSE:
		vec2 p = abs(pos);
		icon = p.x >= 0.1 && p.x <= 0.4 && p.y <= 0.5;
		break;
	}
	if (icon)
		final_color = mix(final_color, u_flash.xyz, u_flash.w);
	
	gl_FragColor = vec4(final_color, 1.0);
}