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