-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathChristmasTree.frag
412 lines (335 loc) · 11.7 KB
/
ChristmasTree.frag
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
// Yet Another Christmas Tree by Ruslan Shestopalyuk, 2014/15
// Many thanks to iq, eiffie and paolofalcao for the insight and the code
// Copy paste to https://www.shadertoy.com/new/ to run
// Or use Fragmentarium (http://syntopia.github.io/Fragmentarium), uncommenting the following line:
//#include "ShaderToy.frag"
#define PI 3.14159265
#define NORMAL_EPS 0.001
#define NEAR_CLIP_PLANE 1.0
#define FAR_CLIP_PLANE 100.0
#define MAX_RAYCAST_STEPS 200
#define STEP_DAMPING 0.7
#define DIST_EPSILON 0.001
#define MAX_RAY_BOUNCES 3.0
#define MAX_SHADOW_DIST 10.0
#define AMBIENT_COLOR vec3(0.03, 0.03, 0.03)
#define LIGHT_COLOR vec3(0.8, 1.0, 0.9)
#define SPEC_COLOR vec3(0.8, 0.90, 0.60)
#define SPEC_POWER 16.0
#define FOG_DENSITY 0.001
#define CAM_DIST 18.0
#define CAM_H 1.0
#define CAM_FOV_FACTOR 4.0
#define LOOK_AT_H 4.0
#define LOOK_AT vec3(0.0, LOOK_AT_H, 0.0)
#define MTL_BACKGROUND -1.0
#define MTL_GROUND 1.0
#define MTL_NEEDLE 2.0
#define MTL_STEM 3.0
#define MTL_TOPPER 4.0
#define MTL_CAP 5.0
#define MTL_BAUBLE 6.0
#define CLR_BACKGROUND vec3(0.3, 0.342, 0.5)
#define CLR_GROUND vec3(3.3, 3.3, 4.5)
#define CLR_NEEDLE vec3(0.152,0.36,0.18)
#define CLR_STEM vec3(0.79,0.51,0.066)
#define CLR_TOPPER vec3(1.6,1.0,0.6)
#define CLR_CAP vec3(1.2,1.0,0.8)
#define BAUBLE_REFLECTIVITY 0.7
#define TREE_H 4.0
#define TREE_R 3.0
#define TREE_CURVATURE 1.0
#define TRUNK_WIDTH 0.025
#define TREE2_ANGLE 0.4
#define TREE2_OFFSET 0.4
#define TREE2_SCALE 0.9
#define NEEDLE_LENGTH 0.5
#define NEEDLE_SPACING 0.15
#define NEEDLE_THICKNESS 0.05
#define NEEDLES_RADIAL_NUM 17.0
#define NEEDLE_BEND 0.99
#define NEEDLE_TWIST 1.0
#define NEEDLE_GAIN 0.7
#define STEM_THICKNESS 0.02
#define BRANCH_ANGLE 0.38
#define BRANCH_SPACING 1.2
#define BRANCH_NUM_MAX 9.0
#define BRANCH_NUM_FADE 2.0
#define BAUBLE_SIZE 0.5
#define BAUBLE_SPACING 1.9
#define BAUBLE_COUNT_FADE1 1.2
#define BAUBLE_COUNT_FADE2 0.3
#define BAUBLE_JITTER 0.05
#define BAUBLE_SPREAD 0.6
#define BAUBLE_MTL_SEED 131.0
#define BAUBLE_YIQ_MUL vec3(0.8, 1.1, 0.6)
#define BAUBLE_CLR_Y 0.7
#define BAUBLE_CLR_I 1.3
#define BAUBLE_CLR_Q 0.9
#define TOPPER_SCALE 2.0
// Primitives
float plane(vec3 p, vec3 n, float offs) {
return dot(p, n) + offs;
}
float sphere(vec3 p, float r) {
return length(p) - r;
}
float cone(in vec3 p, float r, float h) {
return max(abs(p.y) - h, length(p.xz)) - r*clamp(h - abs(p.y), 0.0, h);
}
float cylinder(vec3 p, vec2 h) {
vec2 d = abs(vec2(length(p.xz), p.y)) - h;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
float torus(vec3 p, float ri, float ro) {
vec2 q = vec2(length(p.xz) - ri, p.y);
return length(q) - ro;
}
// Boolean operations
float diff(float d1, float d2) {
return max(-d2, d1);
}
float add(float d1, float d2) {
return min(d2, d1);
}
float intersect(float d1, float d2) {
return max(d2, d1);
}
// Boolean operations (with material ID in second component)
void diff(inout vec2 d1, in vec2 d2) {
if (-d2.x > d1.x) {
d1.x = -d2.x;
d1.y = d2.y;
}
}
void add(inout vec2 d1, in vec2 d2) {
if (d2.x < d1.x) d1 = d2;
}
void intersect(inout vec2 d1, in vec2 d2) {
if (d1.x < d2.x) d1 = d2;
}
// Affine transformations
vec3 translate(vec3 p, vec3 d) {
return p - d;
}
vec2 rotate(vec2 p, float ang) {
float c = cos(ang), s = sin(ang);
return vec2(p.x*c-p.y*s, p.x*s+p.y*c);
}
// Repetition
float repeat(float coord, float spacing) {
return mod(coord, spacing) - spacing*0.5;
}
vec2 repeatAng(vec2 p, float n) {
float ang = 2.0*PI/n;
float sector = floor(atan(p.x, p.y)/ang + 0.5);
p = rotate(p, sector*ang);
return p;
}
vec3 repeatAngS(vec2 p, float n) {
float ang = 2.0*PI/n;
float sector = floor(atan(p.x, p.y)/ang + 0.5);
p = rotate(p, sector*ang);
return vec3(p.x, p.y, mod(sector, n));
}
// Complex primitives
float star(vec3 p) {
p.xy = repeatAng(p.xy, 5.0);
p.xz = abs(p.xz);
return plane(p, vec3(0.5, 0.25, 0.8), -0.09);
}
// Scene elements
vec2 ground(in vec3 p) {
p.y += (sin(sin(p.z*0.1253) - p.x*0.371)*0.31 + cos(p.z*0.553 + sin(p.x*0.127))*0.12)*1.7 + 0.2;
return vec2(p.y, MTL_GROUND);
}
vec2 bauble(in vec3 pos, float matID) {
float type = mod(matID, 5.0);
float d = sphere(pos, BAUBLE_SIZE);
if (type <= 1.0) {
// bumped sphere
d += cos(atan(pos.x, pos.z)*30.0)*0.01*(0.5 - pos.y) + sin(pos.y*60.0)*0.01;
} else if (type <= 2.0) {
// dented sphere
d = diff(d, sphere(pos + vec3(0.0, 0.0, -0.9), 0.7));
} else if (type <= 3.0) {
// horisontally distorted sphere
d += cos(pos.y*28.0)*0.01;
} else if (type <= 4.0) {
// vertically distorted sphere
d += cos(atan(pos.x, pos.z)*20.0)*0.01*(0.5 - pos.y);
}
vec2 res = vec2(d, matID);
// the cap
pos = translate(pos, vec3(0.0, BAUBLE_SIZE, 0.0));
float cap = cylinder(pos, vec2(BAUBLE_SIZE*0.2, 0.1));
// the hook
cap = add(cap, torus(pos.xzy - vec3(0.0, 0.0, 0.12), BAUBLE_SIZE*0.1, 0.015));
vec2 b = vec2(cap, MTL_CAP);
add(res, b);
return res;
}
vec2 baubles(in vec3 p) {
vec3 pos = p;
float h = abs(-floor(pos.y/BAUBLE_SPACING)/TREE_H + 1.0)*TREE_R;
float nb = max(1.0, floor(BAUBLE_COUNT_FADE1*(h + BAUBLE_COUNT_FADE2)*h));
vec3 rp = repeatAngS(pos.xz, nb);
float matID = (h + rp.z + BAUBLE_MTL_SEED)*117.0;
pos.xz = rp.xy;
pos.y = repeat(pos.y, BAUBLE_SPACING);
pos.y += mod(matID, 11.0)*BAUBLE_JITTER;
pos.z += -h + BAUBLE_SPREAD;
vec2 res = bauble(pos, matID);
res.x = intersect(res.x, sphere(p - vec3(0.0, TREE_H*0.5 + 0.5, 0.0), TREE_H + 0.5));
return res;
}
vec2 topper(vec3 pos) {
pos.y -= TREE_H*2.0;
pos /= TOPPER_SCALE;
float d = add(star(pos), cylinder(pos - vec3(0.0, -0.2, 0.0), vec2(0.04, 0.1)))*TOPPER_SCALE;
return vec2(d, MTL_TOPPER);
}
float needles(in vec3 p) {
p.xy = rotate(p.xy, -length(p.xz)*NEEDLE_TWIST);
p.xy = repeatAng(p.xy, NEEDLES_RADIAL_NUM);
p.yz = rotate(p.yz, -NEEDLE_BEND);
p.y -= p.z*NEEDLE_GAIN;
p.z = min(p.z, 0.0);
p.z = repeat(p.z, NEEDLE_SPACING);
return cone(p, NEEDLE_THICKNESS, NEEDLE_LENGTH);
}
vec2 branch(in vec3 p) {
vec2 res = vec2(needles(p), MTL_NEEDLE);
float s = cylinder(p.xzy + vec3(0.0, 100.0, 0.0), vec2(STEM_THICKNESS, 100.0));
vec2 stem = vec2(s, MTL_STEM);
add(res, stem);
return res;
}
vec2 halfTree(vec3 p) {
float section = floor(p.y/BRANCH_SPACING);
float numBranches = max(2.0, BRANCH_NUM_MAX - section*BRANCH_NUM_FADE);
p.xz = repeatAng(p.xz, numBranches);
p.z -= TREE_R*TREE_CURVATURE;
p.yz = rotate(p.yz, BRANCH_ANGLE);
p.y = repeat(p.y, BRANCH_SPACING);
return branch(p);
}
vec2 tree(vec3 p) {
// the first bunch of branches
vec2 res = halfTree(p);
// the second bunch of branches (to hide the regularity)
p.xz = rotate(p.xz, TREE2_ANGLE);
p.y -= BRANCH_SPACING*TREE2_OFFSET;
p /= TREE2_SCALE;
vec2 t2 = halfTree(p);
t2.x *= TREE2_SCALE;
add(res, t2);
// trunk
vec2 tr = vec2(cone(p.xyz, TRUNK_WIDTH, TREE_H*2.0), MTL_STEM);
add(res, tr);
res.x = intersect(res.x, sphere(p - vec3(0.0, TREE_H*0.5 + 1.0, 0.0), TREE_H + 1.0));
return res;
}
vec2 distf(in vec3 pos) {
vec2 res = ground(pos);
vec2 tr = tree(pos);
add(res, tr);
vec2 top = topper(pos);
add(res, top);
vec2 b = baubles(pos);
add(res, b);
return res;
}
vec3 normal(in vec3 p)
{
vec2 d = vec2(NORMAL_EPS, 0.0);
return normalize(vec3(
distf(p + d.xyy).x - distf(p - d.xyy).x,
distf(p + d.yxy).x - distf(p - d.yxy).x,
distf(p + d.yyx).x - distf(p - d.yyx).x));
}
vec2 rayMarch(in vec3 rayOrig, in vec3 rayDir) {
float t = NEAR_CLIP_PLANE;
float mtlID = MTL_BACKGROUND;
for (int i = 0; i < MAX_RAYCAST_STEPS; i++) {
vec2 d = distf(rayOrig + rayDir*t);
if (d.x < DIST_EPSILON || t > FAR_CLIP_PLANE) break;
t += d.x*STEP_DAMPING;
mtlID = d.y;
}
if (t > FAR_CLIP_PLANE) mtlID = MTL_BACKGROUND;
return vec2(t, mtlID);
}
vec3 applyFog(vec3 col, float dist) {
return mix(col, CLR_BACKGROUND, 1.0 - exp(-FOG_DENSITY*dist*dist));
}
const mat3 YIQ_TO_RGB = mat3(
1.0, 0.956, 0.620,
1.0, -0.272, -0.647,
1.0, -1.108, 1.705);
vec3 jollyColor(float matID) {
vec3 clr = cos(matID*BAUBLE_YIQ_MUL);
clr= clr*vec3(0.1, BAUBLE_CLR_I, BAUBLE_CLR_Q) + vec3(BAUBLE_CLR_Y, 0.0, 0.0);
return clamp(clr*YIQ_TO_RGB, 0.0, 1.0);
}
vec3 getMaterialColor(float matID) {
vec3 col = CLR_BACKGROUND;
if (matID <= MTL_GROUND) col = vec3(3.3, 3.3, 4.5);
else if (matID <= MTL_NEEDLE) col = vec3(0.152,0.36,0.18);
else if (matID <= MTL_STEM) col = vec3(0.79,0.51,0.066);
else if (matID <= MTL_TOPPER) col = vec3(1.6,1.0,0.6);
else if (matID <= MTL_CAP) col = vec3(1.2,1.0,0.8);
else col = jollyColor(matID);
return col;
}
float shadow(in vec3 rayOrig, in vec3 rayDir, in float tmin, in float tmax) {
float shadowAmt = 1.0;
float t = tmin;
for (int i = 0; i < MAX_RAYCAST_STEPS; i++) {
float d = distf(rayOrig + rayDir*t).x*STEP_DAMPING;
shadowAmt = min(shadowAmt, 16.0*d/t);
t += clamp(d, 0.01, 0.25);
if (d < DIST_EPSILON || t > tmax) break;
}
return clamp(shadowAmt, 0.0, 1.0);
}
vec3 render(in vec3 rayOrig, in vec3 rayDir) {
vec3 lightDir = -rayDir; // miner's lamp
vec3 resCol = vec3(0.0);
float alpha = 1.0;
for (float i = 0.0; i < MAX_RAY_BOUNCES; i++) {
vec2 d = rayMarch(rayOrig, rayDir);
float t = d.x;
float mtlID = d.y;
vec3 pos = rayOrig + t*rayDir;
vec3 nrm = normal(pos);
vec3 ref = reflect(rayDir, nrm);
vec3 mtlDiffuse = getMaterialColor(mtlID);
float diffuse = clamp(dot(nrm, lightDir), 0.0, 1.0);
float specular = pow(clamp(dot(ref, lightDir), 0.0, 1.0), SPEC_POWER);
diffuse *= shadow(pos, lightDir, DIST_EPSILON, MAX_SHADOW_DIST);
vec3 col = mtlDiffuse*(AMBIENT_COLOR + LIGHT_COLOR*(diffuse + specular*SPEC_COLOR));
col = applyFog(col, t);
resCol += col*alpha; // blend in (a possibly reflected) new color
if (mtlID <= MTL_BAUBLE || abs(dot(nrm, rayDir)) < 0.1) break;
rayOrig = pos + ref*DIST_EPSILON;
alpha *= BAUBLE_REFLECTIVITY;
rayDir = ref;
}
return vec3(clamp(resCol, 0.0, 1.0));
}
vec3 getRayDir(vec3 camPos, vec3 viewDir, vec2 pixelPos) {
vec3 camRight = normalize(cross(viewDir, vec3(0.0, 1.0, 0.0)));
vec3 camUp = normalize(cross(camRight, viewDir));
return normalize(pixelPos.x*camRight + pixelPos.y*camUp + CAM_FOV_FACTOR*viewDir);
}
void main(void) {
vec2 q = gl_FragCoord.xy/iResolution.xy;
vec2 p = -1.0+2.0*q;
p.x *= iResolution.x/iResolution.y;
float ang = 0.1*(40.0 + iGlobalTime);
vec3 camPos = vec3(CAM_DIST*cos(ang), CAM_H, CAM_DIST*sin(ang));
vec3 rayDir = getRayDir(camPos,normalize(LOOK_AT - camPos), p);
vec3 color = render(camPos, rayDir);
gl_FragColor=vec4(color, 1.0);
}