# Functional rendering
## vs Procedural rendering
by Gaëtan Renaudeau - [@greweb](http://twitter.com/greweb)
# Principle
*Procedural* rendering
VS
*Functional* rendering
### "Procedural" rendering
The classical way.
A set of procedures to draw primitive shapes on a viewport.
![](./img/shapes.png)
**Example:** *HTML Canvas, Java Graphics, Processing,...*
### "Functional" rendering
A function...
**`ƒ : Point => Color`**
...for drawing all pixels
> **`∀ p, image.data[p] ← ƒ(p)`**
![](./img/webgl-logo.png)
**Example:** *(OpenGL or WebGL) **GLSL***
## Atari 2600
![](./img/atari-2600.jpg)
How rendering was during that time?
## Analog TV: Scan line
![](./img/raster-scan.png)
e.g. SECAM: 625 lines, 64 µs/line
[(wikipedia)](http://en.wikipedia.org/wiki/Analog_television)
## Rendering was about
* Figuring out what color to set for the current point
* jump to a position = waiting a given time
* **Repeat** each frame rate
# Then came faster CPU...
## Allowing us to
### Make easier rendering abstraction
![](./img/shapes.png)
## But what about
that
**`Position => Color` function**
with modern techs?
Let's call that ***Functional Rendering***.
# HTML5 games
### Different web techs
* DOM-based
* Canvas 2D
* WebGL
## Blazing Race
![](./img/blazing-race.png)
[http://greweb.me/blazing-race/](http://greweb.me/blazing-race/)
didn't finish that game and made a library instead...
## Illuminated.js
![](./img/illuminatedjs.jpg)
Today using Procedural rendering,
would be easier and more realistic to use Functional rendering (+ maybe faster in GLSL).
http://github.com/gre/illuminated.js
played with WebGL shaders (GLSL) and made
## glsl.js
a subset of a WebGL library which focus on making the GLSL (OpenGL Shading Language) easy and accessible for vizualisation and game purposes (2D or 3D).
**http://github.com/gre/glsl.js**
## Some games
### using WebGL & GLSL
* http://greweb.me/dta/
* http://greweb.me/glsl.js/examples/pong/
* http://greweb.me/glsl.js/examples/snake/
* http://greweb.me/js13k/
# Functional rendering
## needs utility functions
## Example of GLSL toolkit
[GLSL spec](http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf)
## Types
GLSL has some useful types.
* **float**
* **vectors** : `vec2`, `vec3`, `vec4`
* **matrices** : `mat2`, `mat3`, `mat4`
* **texture** : `sampler2D`
## Vectors used for position or color
```glsl
vec3 red = vec3(1.0, 0.0, 0.0); // rgb
// red.r , red.g , red.b
vec4 reda = vec4(red, 1.0); // rgba
// red === reda.rgb
vec4 color = vec4(0.0, 1.0, 0.0, 1.0);
// color.rgb is equivalent to color.xyz
```
* `{x, y, z, w}` : points/normals
* `{r, g, b, a}` : colors
* `{s, t, p, q}` : texture coordinates
## Arithmetic operations on those types
```glsl
vec2 a = vec2(1.0, 2.0);
vec2 b = vec2(3.0, 4.0);
a + b
a - b
a * b
a / b
```
## Functions
GLSL has a lot of functions to work with these types.
They are simple functions but very powerful all together.
Most of them works on all dimension types (`float`, `vec2`, `vec3`, `vec4`), `genType` is used to refer to these types.
## Classical math functions
* min, max
* floor, ceil
* mod
* fract
* pow
* sqrt
* exp
* log
* ...
## distance
*compute the distance between two values (float or vectors).*
```glsl
float distance (genType p0, genType p1)
```
implementation:
```glsl
length (p0 – p1)
```
## Much more vector functions
* length
* dot
* cross
* normalize
* ...
## Classical trigonometric functions
* cos, sin, tan
* acos, asin, atan
* radians, degrees
* ...
## texture2D
*Lookup the pixel color of a position in a sampler2D.*
```glsl
vec4 texture2D (sampler2D sampler, vec2 coord)
```
*(There is obviously interpolation)*
## mix
*mixes two values `x` and `y` (linear blend) with a criteria `a`.*
```glsl
genType mix (genType x, genType y, float a)
```
will returns
```glsl
x*(1.-a)+y*a
```
## clamp
*constraints a value between a min and a max value.*
```glsl
genType clamp (genType x, genType minVal, genType maxVal)
```
will returns
```glsl
min (max (x, minVal), maxVal)
```
## smoothstep
*normalize, constraints and smooth-interpolate a value.*
```glsl
genType smoothstep (genType edge0, genType edge1, genType x)
```
implementation:
```glsl
genType t;
t = clamp ((x – edge0) / (edge1 – edge0), 0, 1);
return t * t * (3 – 2 * t);
```
---
**very important one**, also useful for animations.
![](./img/smoothstep_schema.png)
# Some examples
to help you understand both paradigms
### Canvas 2D
```javascript
var canvas = document.getElementById("domcanvasid");
var ctx = canvas.getContext("2d");
```
### WebGL GLSL
Javascript
```javascript
var ctx = canvas.getContext("webgl");
// ... much more JS to initialize things..
```
and the GLSL:
```glsl
void main (void) {
// gl_FragCoord.xy is the current position
gl_FragColor = vec4(r, g, b, a); // set the color
}
```
## Rectangle
![](./img/rect.png)
### Canvas 2D
```javascript
ctx.fillStyle = "red";
ctx.fillRect(x, y, w, h)
```
### GLSL
```glsl
bool inRect (vec2 position, float x, float y, float w, float h) {
return position.x > x && position.y > y
&& position.x < x+w && position.y < y+h;
}
```
```glsl
void main (void) {
if (inRect(gl_FragCoord.xy, 100.0, 100.0, 200.0, 150.0)) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // red
}
else {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // black background
}
}
```
### Summary
Trivial in both cases.
## Circle
![](./img/circle.png)
### Canvas 2D
```javascript
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, r, 0, 2*Math.PI, false)
ctx.fill();
```
### GLSL
```glsl
bool inCircle (vec2 position, float x, float y, float r) {
return distance(position, vec2(x, y)) < r;
}
```
```glsl
void main (void) {
if (inCircle(gl_FragCoord.xy, 100.0, 100.0, 50.0)) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // red
}
else {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // black background
}
}
```
### Summary
Trivial in both cases.
## Line
![](./img/line.png)
### Canvas 2D
```javascript
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
```
### GLSL
```glsl
bool onLine (vec2 p, float a, float b, float lineWidth) {
// y = a * x + b
return distance (p.y, a*p.x+b) < lineWidth;
}
```
Exemple:
```glsl
uniform vec2 resolution;
const vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
void main( void ) {
vec2 p = gl_FragCoord.xy / resolution.xy;
gl_FragColor = onLine(p, 1.0, 0.0, 0.01) ? RED : BLACK;
}
```
[Playground](http://glsl.heroku.com/e#11721.0)
### Summary
The procedural Path is quite trivial and easy to use.
Making a Segment in functional rendering is much more complex but still possible.
## Curve
![](./img/cos.png)
### Canvas 2D
```javascript
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
// ...
ctx.stroke();
```
![](./img/bezier.png)
### GLSL
```glsl
bool onCos (vec2 p, float mag, float freq, float y, float lineWidth) {
return distance (p.y, y+mag*cos(p.x*freq)) < lineWidth;
}
```
[Playground](http://glsl.heroku.com/e#11721.1)
### Summary
Implementing a math formula in Functional rendering is very straighforward.
A bit more work for the Procedural one: converting the formula to path and bezier curves.
## Gradient in a Disc
![](./img/gradient_disc.png)
### Canvas 2D
```javascript
var g = ctx.createLinearGradient(x-r, 0, x+r, 0); // left to right gradient
g.addColorStop(0, "red");
g.addColorStop(1, "green");
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, r, 0, 2*Math.PI, false)
ctx.fill();
```
### GLSL
```glsl
bool inCircle (vec2 position, float x, float y, float r) {
return distance(position, vec2(x, y)) < r;
}
vec3 linearGradientX (vec2 p, vec3 from, vec3 to, float x1, float x2) {
return mix(from, to, smoothstep(x1, x2, p.x));
}
```
```glsl
const vec3 BLACK = vec3(0.,0.,0.);
const vec3 RED = vec3(1.,0.,0.);
const vec3 GREEN = vec3(0.,1.,0.);
void main (void) {
vec2 p = gl_FragCoord.xy / resolution.xy;
if (inCircle(p, 0.5, 0.5, 0.3)) {
gl_FragColor = vec4(linearGradientX(p, RED, GREEN, 0.2, 0.8), 1.0);
}
else {
gl_FragColor = vec4(BLACK, 1.0);
}
}
```
[Playground](http://glsl.heroku.com/e#11738.0)
### Summary
In Canvas 2D we can also do some `createRadialGradient(x0, y0, r0, x1, y1, r1)`,
but that's all for gradient possibilities, there is some gradients you can't do with it,
for instance a "Circular Gradient".
## Image
![](./img/nyancat.jpg)
### Canvas 2D
Stretch the image in the plain canvas:
```javascript
var img = new Image(); img.src="nyancat.jpg"; img.onload = draw;
function draw () {
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
}
```
### GLSL
```glsl
uniform sampler2D img; // uniform means external variables which is setted from JS
void main (void) {
vec2 p = gl_FragCoord.xy / resolution.xy;
gl_FragColor = texture2D(img, p); // stretch it to the full size
}
```
## Exercice in style: Spritesheets
http://greweb.me/glsl.js/examples/mario_sprites/
### Canvas 2D
```javascript
ctx.font = "bold 160px Helvetica";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "black";
ctx.fillText("Hello World!", ctx.canvas.width/2, ctx.canvas.height/2);
```
### GLSL
Nope!
But,
You can draw a text with Canvas 2D and use it as a texture.
http://greweb.me/glsl.js/examples/canvas-text/
## Combine filter conditions
```glsl
if (!inRect(...) && inCircle(...) && !underCurveA(...)) {
gl_FragColor = // fill some color here
}
```
Easily make shapes from union/intersection of other shapes.
![](./img/intersection.png)
## Combine colors
You can also do arithmetic on colors! like add and multiply.
```glsl
vec3 c = vec3(0.0);
c += mainBackground(p);
c += colorForPlayer(p, player);
for (var i=0; i < NB_BALLS; ++i) { //>
c += colorForBall(p, balls[i]);
}
c *= mainMask(p); // multiply on colors
gl_FragColor = vec4(c, 1.0);
```
Also perfect for after-effect.
The code is inspired from [glsl.js' Pong example](http://greweb.me/glsl.js/examples/pong/).
## Trick the initial position
Here is a typical use-case of post-processing effects: translate a bit the position from the initial one.
**You can easily make non-linear transformations!**
E.g.:
```glsl
uniform float time;
uniform sampler2D img;
void main (void) {
vec2 p = gl_FragCoord.xy / resolution.xy;
p += 0.05 * vec2(cos(4.0*p.x+time*0.005), sin(4.0*p.y+time*0.003));
// do more stuff with p, eventually?
gl_FragColor = texture2D(img, p);
}
```
[See it here](./examples/imagetranslate/index.html)
# Summary
## of both approaches
| Procedural | Functional |
|---------------- |:------------:|
| **set of drawing primitives** | **ƒ: `position => color`** |
| **bitmap** (generally) | **vectorial** (by design) |
| *"draw here"* | *"am I here?"* |
| what? / where? | how? |
| simple, imperative | math, functional |
| restricted to primitives | powerful, composable |
| generally faster | generally slower,... |
| | but easily scalable on GPU |
# Other examples
of functional rendering in different techs
## Wrapping Java2D API in Scala
### for functional rendering
```scala
import java.awt._
import java.awt.image._
case class Vec2 (x: Double, y: Double)
object Java2dPixelWrapper {
def render (width: Int, height: Int, getColor: Vec2 => Color) = {
var image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
for {
x <- 0 until width;
y <- 0 until height
} {
var c = getColor(Vec2(x.toDouble / width.toDouble, y.toDouble / height.toDouble))
image.setRGB(x, y, c.getRGB)
}
image
}
}
```
Iterates a scala function on all pixels.
## Use case:
### Functional rendering for generative logo
![](./img/prismicon.png)
made at my work for [prismic.io](http://prismic.io)
### Animated blog header
Uses functional rendering style inside procedural rendering.
![](./img/blog-header.png)
http://greweb.me/javascripts/header.js
### Explore and Experiment Yourself!
* http://glsl.heroku.com
* http://shadertoy.com
* http://webglplayground.net
a lot of examples + playground for GLSL
# Demo time!
## Mandelbrot fractal
[Result](http://glsl.heroku.com/e#18.0)
# Questions?
* twitter: [@greweb](http://twitter.com/greweb)
* http://greweb.me/
---
Slides: http://greweb.me/prez-functional-rendering