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
|
// 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;
%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);
}
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);
gl_FragColor = vec4(final_color, 1.0);
}
|