1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4
5 #include <libcurv/frag.h>
6
7 #include <libcurv/glsl.h>
8 #include <libcurv/shape.h>
9
10 #include <libcurv/context.h>
11 #include <libcurv/die.h>
12 #include <libcurv/format.h>
13 #include <libcurv/function.h>
14
15 namespace curv {
16
17 void export_frag_2d(const Shape_Program&, const Render_Opts&, std::ostream&);
18 void export_frag_3d(const Shape_Program&, const Render_Opts&, std::ostream&);
19
export_frag(const Shape_Program & shape,const Render_Opts & opts,std::ostream & out)20 void export_frag(
21 const Shape_Program& shape, const Render_Opts& opts, std::ostream& out)
22 {
23 if (shape.is_2d_)
24 return export_frag_2d(shape, opts, out);
25 if (shape.is_3d_)
26 return export_frag_3d(shape, opts, out);
27 die("export_frag: shape is not 2d or 3d");
28 }
29
export_frag_2d(const Shape_Program & shape,const Render_Opts & opts,std::ostream & out)30 void export_frag_2d(
31 const Shape_Program& shape, const Render_Opts& opts, std::ostream& out)
32 {
33 out <<
34 "#define AA " << opts.aa_ << "\n"
35 "#define TAA " << opts.taa_ << "\n"
36 "#define FDUR " << opts.fdur_ << "\n"
37 "const vec3 background_colour = vec3("
38 << opts.bg_.x << ","
39 << opts.bg_.y << ","
40 << opts.bg_.z << ");\n"
41 "#ifdef GLSLVIEWER\n"
42 "uniform mat3 u_view2d;\n"
43 "#endif\n";
44
45 glsl_function_export(shape, out);
46
47 BBox bbox = shape.bbox_;
48 if (bbox.empty2() || bbox.infinite2()) {
49 out <<
50 "const vec4 bbox = vec4(-10.0,-10.0,+10.0,+10.0);\n";
51 } else {
52 out << "const vec4 bbox = vec4("
53 << bbox.xmin << ","
54 << bbox.ymin << ","
55 << bbox.xmax << ","
56 << bbox.ymax
57 << ");\n";
58 }
59 out <<
60 "void mainImage( out vec4 fragColour, in vec2 fragCoord )\n"
61 "{\n"
62 " vec2 size = bbox.zw - bbox.xy;\n"
63 " vec2 scale2 = size / iResolution.xy;\n"
64 " vec2 offset = bbox.xy;\n"
65 " float scale;\n"
66 " if (scale2.x > scale2.y) {\n"
67 " scale = scale2.x;\n"
68 " offset.y -= (iResolution.y*scale - size.y)/2.0;\n"
69 " } else {\n"
70 " scale = scale2.y;\n"
71 " offset.x -= (iResolution.x*scale - size.x)/2.0;\n"
72 " }\n"
73 " vec3 col = vec3(0.0);\n"
74 "#if AA>1\n"
75 " for (int m=0; m<AA; ++m)\n"
76 " for (int n=0; n<AA; ++n) {\n"
77 " vec2 jitter = vec2(float(m),float(n)) / float(AA) - 0.5;\n"
78 "#else\n"
79 " const vec2 jitter = vec2(0.0);\n"
80 "#endif\n"
81 " vec2 xy = fragCoord + jitter;\n"
82 "#ifdef GLSLVIEWER\n"
83 " xy = (u_view2d * vec3(xy,1)).xy;\n"
84 "#endif\n"
85 "#if TAA>1\n"
86 " for (int t=0; t<TAA; ++t) {\n"
87 " float time = iTime + float(t)/float(TAA)*float(FDUR);\n"
88 "#else\n"
89 " float time = iTime;\n"
90 "#endif\n"
91 " vec4 p = vec4(xy*scale+offset,0,time);\n"
92 " float d = dist(p);\n"
93 " if (d > 0.0) {\n"
94 " col += background_colour;\n"
95 " } else {\n"
96 " col += colour(p);\n"
97 " }\n"
98 " \n"
99 "#if TAA>1\n"
100 " }\n"
101 "#endif\n"
102 "#if AA>1\n"
103 " }\n"
104 "#endif\n"
105 "#if AA>1 || TAA>1\n"
106 " col /= float(AA*AA*TAA);\n"
107 "#endif\n"
108 " // convert linear RGB to sRGB\n"
109 " fragColour = vec4(pow(col, vec3(0.454545454545454545)),1.0);\n"
110 "}\n"
111 ;
112 }
113
export_sf1(const Shape_Program & shape,const Render_Opts & opts,std::ostream & out)114 void export_sf1(
115 const Shape_Program& shape, const Render_Opts& opts, std::ostream& out)
116 {
117 SC_Compiler sc(out, SC_Target::glsl, shape.sstate_);
118 sc.define_function(
119 "sf1",
120 std::vector<SC_Type>{
121 SC_Type::Num(3),SC_Type::Num(3),SC_Type::Num(3),SC_Type::Num(3)},
122 SC_Type::Num(3),
123 opts.sf1_,
124 At_Program(shape));
125 }
126
export_frag_3d(const Shape_Program & shape,const Render_Opts & opts,std::ostream & out)127 void export_frag_3d(
128 const Shape_Program& shape, const Render_Opts& opts, std::ostream& out)
129 {
130 out <<
131 "#define AA " << opts.aa_ << "\n"
132 "#define TAA " << opts.taa_ << "\n"
133 "#define FDUR " << opts.fdur_ << "\n"
134 "const vec3 background_colour = vec3("
135 << opts.bg_.x << ","
136 << opts.bg_.y << ","
137 << opts.bg_.z << ");\n"
138 "const int ray_max_iter = " << opts.ray_max_iter_ << ";\n"
139 "const float ray_max_depth = " << dfmt(opts.ray_max_depth_, dfmt::EXPR) << ";\n"
140 "#ifdef GLSLVIEWER\n"
141 "uniform vec3 u_eye3d;\n"
142 "uniform vec3 u_centre3d;\n"
143 "uniform vec3 u_up3d;\n"
144 "#endif\n";
145
146 glsl_function_export(shape, out);
147
148 BBox bbox = shape.bbox_;
149 if (bbox.empty3() || bbox.infinite3()) {
150 out <<
151 "const vec3 bbox_min = vec3(-10.0,-10.0,-10.0);\n"
152 "const vec3 bbox_max = vec3(+10.0,+10.0,+10.0);\n";
153 } else {
154 out
155 << "const vec3 bbox_min = vec3("
156 << dfmt(bbox.xmin, dfmt::EXPR) << ","
157 << dfmt(bbox.ymin, dfmt::EXPR) << ","
158 << dfmt(bbox.zmin, dfmt::EXPR)
159 << ");\n"
160 << "const vec3 bbox_max = vec3("
161 << dfmt(bbox.xmax, dfmt::EXPR) << ","
162 << dfmt(bbox.ymax, dfmt::EXPR) << ","
163 << dfmt(bbox.zmax, dfmt::EXPR)
164 << ");\n";
165 }
166
167 // Following code is based on code fragments written by Inigo Quilez,
168 // with The MIT Licence.
169 // Copyright 2013 Inigo Quilez
170 out <<
171 "// ray marching. ro is ray origin, rd is ray direction (unit vector).\n"
172 "// result is (t,r,g,b), where\n"
173 "// * t is the distance that we marched,\n"
174 "// * r,g,b is the colour of the distance field at the point we ended up at.\n"
175 "// (-1,-1,-1) means no object was hit.\n"
176 "vec4 castRay( in vec3 ro, in vec3 rd, float time )\n"
177 "{\n"
178 " float tmin = 0.0;\n" // was 1.0
179 " float tmax = ray_max_depth;\n"
180 " \n"
181 // TODO: implement bounding volume. If I remove the 'if(t>tmax)break'
182 // check, then `tetrahedron` breaks. The hard coded tmax=200 fails for
183 // some models.
184 //"#if 0\n"
185 //" // bounding volume\n"
186 //" float tp1 = (0.0-ro.y)/rd.y; if( tp1>0.0 ) tmax = min( tmax, tp1 );\n"
187 //" float tp2 = (1.6-ro.y)/rd.y; if( tp2>0.0 ) { if( ro.y>1.6 ) tmin = max( tmin, tp2 );\n"
188 //" else tmax = min( tmax, tp2 ); }\n"
189 //"#endif\n"
190 //" \n"
191 " float t = tmin;\n"
192 " vec3 c = vec3(-1.0,-1.0,-1.0);\n"
193 " for (int i=0; i<ray_max_iter; i++) {\n"
194 " float precis = 0.0005*t;\n"
195 " vec4 p = vec4(ro+rd*t,time);\n"
196 " float d = dist(p);\n"
197 " if (abs(d) < abs(precis)) {\n"
198 " c = colour(p);\n"
199 " break;\n"
200 " }\n"
201 " t += d;\n"
202 " if (t > tmax) break;\n"
203 " }\n"
204 " return vec4( t, c );\n"
205 "}\n"
206
207 "vec3 calcNormal( in vec3 pos, float time )\n"
208 "{\n"
209 " vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;\n"
210 " return normalize( e.xyy*dist( vec4(pos + e.xyy,time) ) + \n"
211 " e.yyx*dist( vec4(pos + e.yyx,time) ) + \n"
212 " e.yxy*dist( vec4(pos + e.yxy,time) ) + \n"
213 " e.xxx*dist( vec4(pos + e.xxx,time) ) );\n"
214 //" /*\n"
215 //" vec3 eps = vec3( 0.0005, 0.0, 0.0 );\n"
216 //" vec3 nor = vec3(\n"
217 //" dist(pos+eps.xyy) - dist(pos-eps.xyy),\n"
218 //" dist(pos+eps.yxy) - dist(pos-eps.yxy),\n"
219 //" dist(pos+eps.yyx) - dist(pos-eps.yyx) );\n"
220 //" return normalize(nor);\n"
221 //" */\n"
222 "}\n";
223
224 if (opts.shader_ == Render_Opts::Shader::standard) {
225 out <<
226 // Compute an ambient occlusion factor.
227 // pos: point on surface
228 // nor: normal of the surface at pos
229 // Yields a value clamped to [0,1] where 0 means no other surfaces
230 // around the point, and 1 means the point is occluded by other surfaces.
231 "float calcAO( in vec3 pos, in vec3 nor, float time )\n"
232 "{\n"
233 " float occ = 0.0;\n"
234 " float sca = 1.0;\n"
235 " for( int i=0; i<5; i++ )\n"
236 " {\n"
237 " float hr = 0.01 + 0.12*float(i)/4.0;\n"
238 " vec3 aopos = nor * hr + pos;\n"
239 " float dd = dist( vec4(aopos,time) );\n"
240 " occ += -(dd-hr)*sca;\n"
241 " sca *= 0.95;\n"
242 " }\n"
243 " return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ); \n"
244 "}\n"
245
246 "// in ro: ray origin\n"
247 "// in rd: ray direction\n"
248 "// out: rgb colour\n"
249 "vec3 render( in vec3 ro, in vec3 rd, float time )\n"
250 "{ \n"
251 " //vec3 col = vec3(0.7, 0.9, 1.0) +rd.z*0.8;\n"
252 " vec3 col = background_colour;\n"
253 " vec4 res = castRay(ro,rd, time);\n"
254 " float t = res.x;\n"
255 " vec3 c = res.yzw;\n"
256 " if( c.x>=0.0 )\n"
257 " {\n"
258 " vec3 pos = ro + t*rd;\n"
259 " vec3 nor = calcNormal( pos, time );\n"
260 " vec3 ref = reflect( rd, nor );\n"
261 " \n"
262 " // material \n"
263 " col = c;\n"
264 "\n"
265 " // lighting \n"
266 " float occ = calcAO( pos, nor, time );\n"
267 " vec3 lig = normalize( vec3(-0.4, 0.6, 0.7) );\n"
268 " float amb = clamp( 0.5+0.5*nor.z, 0.0, 1.0 );\n"
269 " float dif = clamp( dot( nor, lig ), 0.0, 1.0 );\n"
270 " float bac = clamp( dot( nor, normalize(vec3(-lig.x,lig.y,0.0))), 0.0, 1.0 )*clamp( 1.0-pos.z,0.0,1.0);\n"
271 " float dom = smoothstep( -0.1, 0.1, ref.z );\n"
272 " float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );\n"
273 " float spe = pow(clamp( dot( ref, lig ), 0.0, 1.0 ),16.0);\n"
274 " \n"
275 " vec3 lin = vec3(0.0);\n"
276 " lin += 1.30*dif*vec3(1.00,0.80,0.55);\n"
277 " lin += 2.00*spe*vec3(1.00,0.90,0.70)*dif;\n"
278 " lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ;\n"
279 " lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ;\n"
280 " lin += 0.50*bac*vec3(0.35,0.35,0.35)*occ;\n"
281 " lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ;\n"
282 " vec3 iqcol = col*lin;\n"
283 "\n"
284 " //col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) );\n"
285 " col = mix(col,iqcol, 0.5);\n" // adjust contrast
286 " }\n"
287 "\n"
288 " return vec3( clamp(col,0.0,1.0) );\n"
289 "}\n";
290 }
291
292 if (opts.shader_ == Render_Opts::Shader::sf1) {
293 if (opts.sf1_) {
294 export_sf1(shape, opts, out);
295 } else {
296 out <<
297 // Only called if the ray struck a shape, returns pixel colour.
298 "vec3 sf1(vec3 pos, vec3 nor, vec3 rd, vec3 col)\n"
299 "{\n"
300 " vec3 ref = reflect( rd, nor );\n"
301 " \n"
302 " // lighting \n"
303 " float occ = 1.0;\n"
304 " vec3 lig = normalize( vec3(-0.4, 0.6, 0.7) );\n"
305 " float amb = clamp( 0.5+0.5*nor.z, 0.0, 1.0 );\n"
306 " float dif = clamp( dot( nor, lig ), 0.0, 1.0 );\n"
307 " float bac = clamp( dot( nor, normalize(vec3(-lig.x,lig.y,0.0))), 0.0, 1.0 )*clamp( 1.0-pos.z,0.0,1.0);\n"
308 " float dom = smoothstep( -0.1, 0.1, ref.z );\n"
309 " float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );\n"
310 " float spe = pow(clamp( dot( ref, lig ), 0.0, 1.0 ),16.0);\n"
311 " \n"
312 " vec3 lin = vec3(0.0);\n"
313 " lin += 1.30*dif*vec3(1.00,0.80,0.55);\n"
314 " lin += 2.00*spe*vec3(1.00,0.90,0.70)*dif;\n"
315 " lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ;\n"
316 " lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ;\n"
317 " lin += 0.50*bac*vec3(0.35,0.35,0.35)*occ;\n"
318 " lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ;\n"
319 " vec3 iqcol = col*lin;\n"
320 "\n"
321 " return mix(col,iqcol, 0.5);\n" // adjust contrast
322 "}\n";
323 }
324 out <<
325 "// in ro: ray origin\n"
326 "// in rd: ray direction\n"
327 "// out: rgb colour\n"
328 "vec3 render( in vec3 ro, in vec3 rd, float time )\n"
329 "{ \n"
330 " //vec3 col = vec3(0.7, 0.9, 1.0) +rd.z*0.8;\n"
331 " vec3 col = background_colour;\n"
332 " vec4 res = castRay(ro,rd, time);\n"
333 " float t = res.x;\n"
334 " vec3 c = res.yzw;\n"
335 " if( c.x>=0.0 )\n"
336 " {\n"
337 " vec3 pos = ro + t*rd;\n"
338 " vec3 nor = calcNormal( pos, time );\n"
339 " col = sf1(pos, nor, rd, c);\n"
340 " }\n"
341 "\n"
342 " return vec3( clamp(col,0.0,1.0) );\n"
343 "}\n";
344 }
345
346 if (opts.shader_ == Render_Opts::Shader::pew) {
347 // written by Philipp Emanuel Weidmann (@p-e-w on github)
348 out <<
349 "struct Light {\n"
350 " vec3 position;\n"
351 " // One component per color channel\n"
352 " vec3 specular_intensity;\n"
353 " vec3 diffuse_intensity;\n"
354 " vec3 ambient_intensity;\n"
355 "};\n"
356
357 "const Light lights[3] = Light[3](\n"
358 " Light(vec3(-10.0, -100.0, 100.0), vec3(1.5), vec3(1.5), vec3(0.25)),\n"
359 " Light(vec3( 0.0, 100.0, 100.0), vec3(2.0), vec3(2.0), vec3(0.25)),\n"
360 " Light(vec3( 20.0, 100.0, -100.0), vec3(1.5), vec3(1.5), vec3(0.5))\n"
361 ");\n"
362
363 "struct Material {\n"
364 " // One component per color channel\n"
365 " vec3 specular_reflectivity;\n"
366 " vec3 diffuse_reflectivity;\n"
367 " vec3 ambient_reflectivity;\n"
368 " vec3 shininess;\n"
369 "};\n"
370
371 "Material material(vec3 point, float time) {\n"
372 " return Material(vec3(1.5), vec3(1.2), vec3(0.5), vec3(15.0));\n"
373 "}\n"
374
375 "vec3 render(in vec3 ray_origin, in vec3 ray_direction, float time) {\n"
376 " vec4 result = castRay(ray_origin, ray_direction, time);\n"
377 " if (result.y < 0.0) {\n"
378 " return background_colour;\n"
379 " }\n"
380 "\n"
381 " float distance = result.x;\n"
382 " vec3 point = ray_origin + distance*ray_direction;\n"
383 " vec3 normal = calcNormal(point, time);\n"
384 " vec3 viewer_direction = -ray_direction;\n"
385 "\n"
386 " vec3 color = result.yzw;\n"
387 " Material material = material(point, time);\n"
388 "\n"
389 " vec3 illumination = vec3(0.0);\n"
390 "\n"
391 " // Implementation follows https://en.wikipedia.org/wiki/Phong_reflection_model\n"
392 " for (int i = 0; i < lights.length(); i++) {\n"
393 " Light light = lights[i];\n"
394 "\n"
395 " illumination += material.ambient_reflectivity * light.ambient_intensity;\n"
396 "\n"
397 " vec3 light_direction = normalize(light.position - point);\n"
398 "\n"
399 " result = castRay(point, light_direction, time);\n"
400 " if (result.y < 0.0) {\n"
401 " // No part of the shape lies between the surface point\n"
402 " // and the light source, so directional light affects this point\n"
403 " vec3 reflection_direction = -reflect(light_direction, normal);\n"
404 "\n"
405 " float diffuse_term = dot(light_direction, normal);\n"
406 " if (diffuse_term > 0.0) {\n"
407 " illumination +=\n"
408 " material.diffuse_reflectivity * diffuse_term * light.diffuse_intensity;\n"
409 "\n"
410 " float specular_term = dot(reflection_direction, viewer_direction);\n"
411 " if (specular_term > 0.0) {\n"
412 " illumination +=\n"
413 " material.specular_reflectivity *\n"
414 " pow(vec3(specular_term), material.shininess) *\n"
415 " light.specular_intensity;\n"
416 " }\n"
417 " }\n"
418 " }\n"
419 " }\n"
420 "\n"
421 " return mix(color, clamp(color*illumination, 0.0, 1.0), 0.5);\n"
422 "}\n";
423 }
424
425 out <<
426 "// Create a matrix to transform coordinates to look towards a given point.\n"
427 "// * `eye` is the position of the camera.\n"
428 "// * `centre` is the position to look towards.\n"
429 "// * `up` is the 'up' direction.\n"
430 "mat3 look_at(vec3 eye, vec3 centre, vec3 up)\n"
431 "{\n"
432 " vec3 ww = normalize(centre - eye);\n"
433 " vec3 uu = normalize(cross(ww, up));\n"
434 " vec3 vv = normalize(cross(uu, ww));\n"
435 " return mat3(uu, vv, ww);\n"
436 "}\n"
437
438 "// Generate a ray direction for ray-casting.\n"
439 "// * `camera` is the camera look-at matrix.\n"
440 "// * `pos` is the screen position, normally in the range -1..1\n"
441 "// * `lens` is the lens length of the camera (encodes field-of-view).\n"
442 "// 0 is very wide, and 2 is a good default.\n"
443 "vec3 ray_direction(mat3 camera, vec2 pos, float lens)\n"
444 "{\n"
445 " return normalize(camera * vec3(pos, lens));\n"
446 "}\n"
447
448 "void mainImage( out vec4 fragColour, in vec2 fragCoord )\n"
449 "{\n"
450 " vec3 col = vec3(0.0);\n"
451 " const vec3 origin = (bbox_min + bbox_max) / 2.0;\n"
452 " const vec3 radius = (bbox_max - bbox_min) / 2.0;\n"
453 " float r = max(radius.x, max(radius.y, radius.z)) / 1.3;\n"
454 "#if AA>1\n"
455 " for (int m=0; m<AA; ++m)\n"
456 " for (int n=0; n<AA; ++n) {\n"
457 " vec2 o = vec2(float(m),float(n)) / float(AA) - 0.5;\n"
458 "#else\n"
459 " const vec2 o = vec2(0.0);\n"
460 "#endif\n"
461 " vec2 p = -1.0 + 2.0 * (fragCoord+o) / iResolution.xy;\n"
462 " p.x *= iResolution.x/iResolution.y;\n"
463 "\n"
464 // convert from the OpenGL coordinate system to the Curv coord system.
465 "#ifdef GLSLVIEWER\n"
466 " vec3 eye = vec3(u_eye3d.x, -u_eye3d.z, u_eye3d.y)*r + origin;\n"
467 " vec3 centre = vec3(u_centre3d.x, -u_centre3d.z, u_centre3d.y)*r + origin;\n"
468 " vec3 up = vec3(u_up3d.x, -u_up3d.z, u_up3d.y);\n"
469 "#else\n"
470 " vec3 eye = vec3(2.6, -4.5, 3.0);\n"
471 " vec3 centre = vec3(0.0, 0.0, 0.0);\n"
472 " vec3 up = vec3(-0.25, 0.433, 0.866);\n"
473 "#endif\n"
474 " mat3 camera = look_at(eye, centre, up);\n"
475 " vec3 dir = ray_direction(camera, p, 2.5);\n"
476 "\n"
477 "#if TAA>1\n"
478 " for (int t=0; t<TAA; ++t) {\n"
479 " float time = iTime + float(t)/float(TAA)*float(FDUR);\n"
480 "#else\n"
481 " float time = iTime;\n"
482 "#endif\n"
483 " col += render( eye, dir, time );\n"
484 "\n"
485 "#if TAA>1\n"
486 " }\n"
487 "#endif\n"
488 "#if AA>1\n"
489 " }\n"
490 "#endif\n"
491 "#if AA>1 || TAA>1\n"
492 " col /= float(AA*AA*TAA);\n"
493 "#endif\n"
494 "\n"
495 " // convert linear RGB to sRGB\n"
496 " col = pow(col, vec3(0.454545454545454545));\n"
497 " fragColour = vec4(col,1.0);\n"
498 "}\n"
499 ;
500 }
501
502 } // namespaces
503