<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6819369948550238004</id><updated>2012-01-03T08:58:33.351-08:00</updated><category term='siggraph graphics'/><category term='gpu fluid splash painting cuda'/><category term='webgl google android robot distance field'/><category term='scan ipod slides 35mm'/><category term='gdc09 fluid simulation particles'/><category term='pixar gpu realtime'/><title type='text'>Industrial Arithmetic</title><subtitle type='html'>truth at 60 frames per second</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-7563523078923373326</id><published>2011-08-26T04:50:00.000-07:00</published><updated>2011-09-01T06:47:11.817-07:00</updated><title type='text'>Meeting Steve Jobs</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://1.gvt0.com/vi/wRtCfJcEYz0/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/wRtCfJcEYz0&amp;fs=1&amp;source=uds" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266"  src="http://www.youtube.com/v/wRtCfJcEYz0&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
With the recent news about Steve Jobs leaving Apple, &lt;a href="https://plus.google.com/107117483540235115863/posts/gcSStkKxXTw"&gt;everybody &lt;/a&gt;seems to be posting their &lt;a href="http://www.tbray.org/ongoing/When/201x/2011/08/25/Steve-at-NeXT"&gt;stories&lt;/a&gt; about him. Mine isn't that funny, but here goes.&lt;br /&gt;
&lt;br /&gt;
One of my first projects at NVIDIA was working on a real-time version of Pixar's famous &lt;a href="http://www.youtube.com/watch?v=PvCWPZfK8pI"&gt;"Luxo Jr&lt;/a&gt;" short film. Shadows maps were a new feature on the GeForce 3 graphics card, and our marketing guys thought this would be a great way of showing them off (without any concept of how difficult this would be, obviously). The GeForce 3 was planned to be launched with the new iMacs at MacWorld Tokyo 2001.&lt;br /&gt;
&lt;br /&gt;
The crazy thing was that we developed the whole demo without any permission (or help) from Pixar whatsoever. As my boss liked to say, it's easier to ask for forgiveness than permission.&lt;br /&gt;
&lt;br /&gt;
Our intern Eugene D'Eon (now at Weta), modeled the lamps and rotoscoped the entire animation frame-by-frame from a rip of the original DVD. I worked on the shadows and shading. We finished the demo with a few days to spare (those bendy lamp cords were a real pain to get right, let me tell you), and our execs showed it to Steve at Apple. He said it was pretty close to the original, talked to Pixar, and gave us permission to show it at the launch.&lt;br /&gt;
&lt;br /&gt;
So me and our chief scientist David Kirk flew out to Tokyo for the launch. My job was to drive the demo on stage, which was somewhat nerve-wracking, as you might be able to tell from the video above. During the rehearsal one of Steve's assistants told us we might have to leave the room because he was getting upset that his slide remote control wasn't working, and we wouldn't want to see that. I got the impression this happened quite often!&lt;br /&gt;
&lt;br /&gt;
Later, David and I had a brief chance to talk to Steve. From what I remember of the conversation, he mainly talked about how great the new OS X user interface was. In my youthful ignorance I jumped in and said it was pretty, but had they thought about using the graphics hardware to speed up the rendering? (It was pretty slow at the time). He said he had talked to his best engineers about this, and they told him it was impossible. There was no way they could get the quality they needed using the GPU. Anyway, a year or so later OS X did introduce a GPU accelerated UI with smoothly scaling icons etc. Coincidence? I don't think so! &lt;br /&gt;
&lt;br /&gt;
We only got to show that demo once, but it was worth it, I think.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-7563523078923373326?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/7563523078923373326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=7563523078923373326' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7563523078923373326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7563523078923373326'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/08/meeting-steve-jobs.html' title='Meeting Steve Jobs'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-5994949267878274110</id><published>2011-08-16T04:47:00.000-07:00</published><updated>2011-08-16T07:26:36.684-07:00</updated><title type='text'>Tribute to MC Escher</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-hrrXFxHbWKM/Tkpap7A9fPI/AAAAAAAAAHM/E5RqyvNHtaM/s1600/escher.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-hrrXFxHbWKM/Tkpap7A9fPI/AAAAAAAAAHM/E5RqyvNHtaM/s320/escher.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;script id="shader-vs" type="x-shader/x-vertex"&gt;
  attribute vec3 aVertexPosition;
  void main(void) {
    gl_Position = vec4(aVertexPosition, 1.0);
  }
&lt;/script&gt;&lt;br /&gt;
&lt;script id="shader-fs" type="x-shader/x-fragment"&gt;
// recursive tetrahedron
// sgreen 8/2011
// based on:
// http://blog.hvidtfeldts.net/index.php/2011/08/distance-estimated-3d-fractals-iii-folding-space/

#ifdef GL_ES
precision highp float;
#endif

uniform vec2 resolution;
uniform float time;

// CSG operations
float _union(float a, float b)
{
    return min(a, b);
}

float intersect(float a, float b)
{
    return max(a, b);
}

float difference(float a, float b)
{
    return max(a, -b);
}

// objects
float box(vec3 p, vec3 abc )
{
    vec3 di=max(abs(p)-abc, vec3(0.0));
    //return dot(di,di);
    return length(di);
}

float sphere(vec3 p, float r)
{
    return length(p) - r;
}

// transforms
vec3 rotateX(vec3 p, float a)
{
    float sa = sin(a);
    float ca = cos(a);
    vec3 r;
    r.x = p.x;
    r.y = ca*p.y - sa*p.z;
    r.z = sa*p.y + ca*p.z;
    return r;
}

vec3 rotateY(vec3 p, float a)
{
    float sa = sin(a);
    float ca = cos(a);
    vec3 r;
    r.x = ca*p.x + sa*p.z;
    r.y = p.y;
    r.z = -sa*p.x + ca*p.z;
    return r;
}

float tet(vec3 z)
{
        const int iterations = 10;
        const float scale = 2.0;

     vec3 a1 = vec3(1,1,1);
     vec3 a2 = vec3(-1,-1,1);
     vec3 a3 = vec3(1,-1,-1);
     vec3 a4 = vec3(-1,1,-1);
     vec3 c;
     float dist, d;
     int i = 0;
     //while(n &lt; iterations) {
     for(int n=0; n &lt; iterations; n++) {
          c = a1; dist = length(z-a1);
          d = length(z-a2); if (d &lt; dist) { c = a2; dist=d; }
          d = length(z-a3); if (d &lt; dist) { c = a3; dist=d; }
          d = length(z-a4); if (d &lt; dist) { c = a4; dist=d; }
          z = scale*z-c*(scale-1.0);
          i++;
     }

     return (length(z)-2.0) * pow(scale, float(-i));
}

// optimized version using folds
float tet2(vec3 z)
{
    const int iterations = 10;
    const float scale = 2.0;
    //float scale = 2.0+sin(time);

    float r;
    //float s = 1.0;
    int c = 0;
    for(int n = 0; n &lt; iterations; n++) {
    //while(n &lt; iterations) {
       if(z.x+z.y&lt;0.0) z.xy = -z.yx; // fold 1
       if(z.x+z.z&lt;0.0) z.xz = -z.zx; // fold 2
       if(z.y+z.z&lt;0.0) z.yz = -z.zy; // fold 3
       z = z*scale - (scale-1.0);
       //r = dot(z, z);
       //s = s * scale;
       c++;
    }
    return (length(z)-2.0 ) * pow(scale, -float(c));
    //return length(z) / s;
}

// distance to scene
float scene(vec3 p)
{
    float d;

    // floor plane
    //d = plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0));
    d = p.y + 1.0;

    // repeat
    p = mod(p + vec3(1.0), 2.0) - vec3(1.0);

    //d = min(d, box(p, 0.5));
    //d = min(d, difference(box(p, 0.5), sphere(p, 0.7)));

    d = min(d, tet2(p));

    return d;
}

// calculate scene normal
vec3 sceneNormal( in vec3 pos )
{
    float eps = 0.0001;
    vec3 n;
    n.x = scene( vec3(pos.x+eps, pos.y, pos.z) ) - scene( vec3(pos.x-eps, pos.y, pos.z) );
    n.y = scene( vec3(pos.x, pos.y+eps, pos.z) ) - scene( vec3(pos.x, pos.y-eps, pos.z) );
    n.z = scene( vec3(pos.x, pos.y, pos.z+eps) ) - scene( vec3(pos.x, pos.y, pos.z-eps) );
    return normalize(n);
}

// ambient occlusion approximation
float ambientOcclusion(vec3 p, vec3 n)
{
    const int steps = 3;
    const float delta = 0.5;

    float a = 0.0;
    float weight = 1.0;
    for(int i=1; i&lt;=steps; i++) {
        float d = (float(i) / float(steps)) * delta; 
        a += weight*(d - scene(p + n*d));
        weight *= 0.5;
    }
    return clamp(1.0 - a, 0.0, 1.0);
}

// lighting
vec3 shade(vec3 pos, vec3 n, vec3 eyePos)
{
    //const vec3 lightPos = vec3(5.0, 10.0, 5.0);
    //const vec3 color = vec3(0.643, 0.776, 0.223);
    //const float shininess = 100.0;

    //vec3 l = normalize(lightPos - pos);
    vec3 l = vec3(0.0, 0.0, 1.0);
    //vec3 v = normalize(eyePos - pos);
    //vec3 h = normalize(v + l);
    float diff = dot(n, l);
    //float spec = max(0.0, pow(dot(n, h), shininess)) * float(diff &gt; 0.0);
    //diff = max(0.0, diff);
    diff = 0.5+0.5*diff;

    //float fresnel = pow(1.0 - dot(n, v), 5.0);
    float ao = ambientOcclusion(pos, n);

//    return vec3(diff*ao) * color + vec3(spec + fresnel*0.5);
//    return vec3(diff*ao) * color + vec3(spec);
//    return diff;
//    return vec3(ao);
   return vec3(diff*ao);
//     return lerp(vec3(1.0), vec3(diff*ao), fog);
//    return vec3(fresnel);
}

// trace ray using sphere tracing
vec3 trace(vec3 ro, vec3 rd, out bool hit)
{
    const int maxSteps = 128;
    const float hitThreshold = 0.01;
    hit = false;
    vec3 pos = ro;

    for(int i=0; i&lt;maxSteps; i++)
    {
        float d = scene(pos);
        if (d &lt; hitThreshold) {
            hit = true;
            return pos;
        }
        pos += d*rd;
    }
    return pos;
}

vec3 background(vec3 rd)
{
     //return mix(vec3(1.0), vec3(0.0), rd.y);
     //return mix(vec3(1.0), vec3(0.0, 0.25, 1.0), rd.y);
     return vec3(0.0);
}

void main(void)
{
    vec2 pixel = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;

    // compute ray origin and direction
    float asp = resolution.x / resolution.y;
    vec3 rd = vec3(asp*pixel.x, pixel.y, -2.0);

    // fish-eye
    rd.z = -sqrt(2.0 - dot(rd.xy, rd.xy));
    rd = normalize(rd);

    // move camera
    //vec3 ro = vec3(0.0, 0.0, 4.0);
    //vec3 ro = vec3(0.0, 0.3, 2.0+2.0*sin(time*0.25));
    vec3 ro = vec3(time*0.25, 0.3, time*0.25);

    float a;
    //a = sin(time*0.3)*1.5;
    a = time*0.3;
    rd = rotateY(rd, a);
    //ro = rotateY(ro, a);

    float ax = sin(time*0.1)*0.25;
    rd = rotateX(rd, ax);
    //ro = rotateX(ro, ax);    

    // trace ray
    bool hit;
    vec3 pos = trace(ro, rd, hit);

    vec3 rgb;
    if(hit)
    {
        // calc normal
        vec3 n = sceneNormal(pos);
        // shade
        rgb = shade(pos, n, ro);

        // fog
        float dist = length(pos - ro)*0.25;
        float fog = exp(-dist*dist);
        //rgb = fog;
        //rgb = lerp(vec3(1.0), rgb, fog);
        rgb *= fog;


#if 0
        // reflection
        vec3 v = normalize(ro - pos);
        float fresnel = 0.1 + 0.4*pow(1.0 - dot(n, v), 5.0);

        ro = pos + n*0.01; // offset to avoid self-intersection
        rd = reflect(-v, n);
        pos = trace(ro, rd, hit);

        if (hit) {
            vec3 n = sceneNormal(pos);
            rgb += shade(pos, n, ro) * vec3(fresnel);
        } else {
            rgb += background(rd) * vec3(fresnel);
        }
#endif 

     } else {
        rgb = background(rd);
     }

    // vignetting
    //rgb *= 0.5+0.5*smoothstep(2.0, 0.5, dot(pixel, pixel));

    gl_FragColor=vec4(rgb, 1.0);
}
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
  var canvas;
  var startTime; 
  var gl;
  var squareVertexPositionBuffer;
  var shaderProgram;

  function initGL(canvas) {
    try {
      gl = canvas.getContext("experimental-webgl");
      gl.viewportWidth = canvas.width;
      gl.viewportHeight = canvas.height;
    } catch(e) {
    }
    if (!gl) {
      alert("Could not initialise WebGL!");
    }
    startTime = (new Date()).getTime();
  }


  function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
      return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
      if (k.nodeType == 3) {
        str += k.textContent;
      }
      k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
      shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
      shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
      return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert(gl.getShaderInfoLog(shader));
      return null;
    }

    return shader;
  }


  function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    //var fragmentShader = getShader(gl, "simple-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pResolutionUniform = gl.getUniformLocation(shaderProgram, "resolution");
    shaderProgram.pTimeUniform = gl.getUniformLocation(shaderProgram, "time");
  }


  function setUniforms() {
    gl.uniform2f(shaderProgram.pResolutionUniform, canvas.width, canvas.height);
    var time = (new Date()).getTime();
    gl.uniform1f(shaderProgram.pTimeUniform, (time - startTime) / 1000.0);
  }


  function initBuffers() {
    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    vertices = [
         1.0,  1.0,  0.0,
        -1.0,  1.0,  0.0,
         1.0, -1.0,  0.0,
        -1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = 4;
  }

  function onWindowResize( event ) {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    gl.viewport( 0, 0, canvas.width, canvas.height );
  }


  function drawScene() {
    //gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
  }


  function webGLStart() {
    canvas = document.getElementById("my-canvas");
    initGL(canvas);
    initShaders()
    initBuffers();

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.disable(gl.DEPTH_TEST);

    setInterval(drawScene, 16);
  }
&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
Another distance field ray marching experiment in WebGL. Inspired by &lt;a href="http://blog.hvidtfeldts.net/index.php/2011/08/distance-estimated-3d-fractals-iii-folding-space/"&gt;this &lt;/a&gt;interesting blog post on distance estimators. &lt;br /&gt;
&lt;br /&gt;
Click the button below to see the interactive version. It's a bit glitchy, but I kind of like that.&lt;br /&gt;
&lt;br /&gt;
Warning - not stable on all browsers! I recommend Firefox with the "webgl.prefer_gl" and "webgl.prefer-native-gl" preferences both set to "true" (type "about:config" into the address bar and filter for "webgl"), or Chrome launched with "--use-gl=desktop" on the command line.&lt;br /&gt;
&lt;br /&gt;
Update - fix for stupid WebGL shader validator.&lt;br /&gt;
&lt;br /&gt;
&lt;form&gt;&lt;input onclick="webGLStart()" type="button" value="Click me!" /&gt; &lt;/form&gt;&lt;br /&gt;
&lt;canvas height="300" id="my-canvas" width="400"&gt; Your browser doesn't appear to support the HTML5 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. &lt;/canvas&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-5994949267878274110?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/5994949267878274110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=5994949267878274110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5994949267878274110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5994949267878274110'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/08/tribute-to-mc-escher.html' title='Tribute to MC Escher'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-hrrXFxHbWKM/Tkpap7A9fPI/AAAAAAAAAHM/E5RqyvNHtaM/s72-c/escher.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-6037707490058271442</id><published>2011-04-26T11:11:00.000-07:00</published><updated>2011-04-26T11:11:58.187-07:00</updated><title type='text'>SGI's Finest Product</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-tDZQfxivVOU/TbcIn4UOHxI/AAAAAAAAAC8/MPISHdw-h5Q/s1600/sgi_screwdriver.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-tDZQfxivVOU/TbcIn4UOHxI/AAAAAAAAAC8/MPISHdw-h5Q/s400/sgi_screwdriver.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
I worked for &lt;a href="http://www.sgi.com/"&gt;Silicon Graphics&lt;/a&gt; for a while in the late 1990s. It was a fun time, but the one thing that has stayed with me is this brilliant reversible screwdriver (part no. 9980915) which they used to include with graphics board upgrades. If SGI had any sense they would re-release this as a collectors item!&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-6037707490058271442?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/6037707490058271442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=6037707490058271442' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/6037707490058271442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/6037707490058271442'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/04/sgis-finest-product.html' title='SGI&apos;s Finest Product'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-tDZQfxivVOU/TbcIn4UOHxI/AAAAAAAAAC8/MPISHdw-h5Q/s72-c/sgi_screwdriver.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-5333807390369283356</id><published>2011-04-21T02:48:00.000-07:00</published><updated>2011-04-21T02:48:18.987-07:00</updated><title type='text'>Volume Scattering Is Cool</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Qgr_fj2XG38/Ta_2omB_1pI/AAAAAAAAAC0/XrwQDOe9WK8/s1600/ignition_noscatter.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="308" src="http://3.bp.blogspot.com/-Qgr_fj2XG38/Ta_2omB_1pI/AAAAAAAAAC0/XrwQDOe9WK8/s400/ignition_noscatter.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Without scattering&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-LFrP_LqBI9c/Ta_2ufE7YNI/AAAAAAAAAC4/5eJOXMiczDE/s1600/ignition.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="308" src="http://2.bp.blogspot.com/-LFrP_LqBI9c/Ta_2ufE7YNI/AAAAAAAAAC4/5eJOXMiczDE/s400/ignition.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;With scattering&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;A big part of the look of explosions is the very hot bright interior  light being scattered (i.e. blurred) as it travels towards the eye and is  attenuated by the surrounding smoke. These images are from a work-in-progress real-time explosion simulation and show the benefit of adding approximate volume scattering.&lt;br /&gt;
&lt;br /&gt;
I'm using the slice-based scattering technique from the &lt;a href="http://http.developer.nvidia.com/GPUGems/gpugems_ch39.html"&gt;volume rendering chapter&lt;/a&gt; in GPU Gems 1 by Joe Kniss et. al. This is one of my favourite tricks, and is the same technique I used in the old smoke particles sample in the CUDA SDK. The main difference here is I'm always drawing the slices from back-to-front, instead of using the half-angle direction. It turns out for emissive volumes like explosions, where most of the light is from the inside,this works pretty well. It's quite a lot slower than doing ray marching in a pixel shader (lots of passes, and you're blending in the frame buffer), but I think the results are worth it.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-5333807390369283356?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/5333807390369283356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=5333807390369283356' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5333807390369283356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5333807390369283356'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/04/volume-scattering-is-cool.html' title='Volume Scattering Is Cool'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-Qgr_fj2XG38/Ta_2omB_1pI/AAAAAAAAAC0/XrwQDOe9WK8/s72-c/ignition_noscatter.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-7086490350779621092</id><published>2011-01-28T02:35:00.000-08:00</published><updated>2011-01-28T02:35:19.034-08:00</updated><title type='text'>Random Screenshot of the Week</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_XkozmCCjhTQ/TUKajV8UE-I/AAAAAAAAACg/PXjF-G8aBqI/s1600/vortex.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_XkozmCCjhTQ/TUKajV8UE-I/AAAAAAAAACg/PXjF-G8aBqI/s320/vortex.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
I generated this image the other day when messing around with my CUDA 2D fluid simulation code. For the technically minded, it's the magnitude of the velocity mapped to a color gradient, the velocity field has some turbulence added using curl noise. For the non-technically minded, it looks like THE BURNING FIRES OF HELL!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-7086490350779621092?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/7086490350779621092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=7086490350779621092' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7086490350779621092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7086490350779621092'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/01/random-screenshot-of-week.html' title='Random Screenshot of the Week'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XkozmCCjhTQ/TUKajV8UE-I/AAAAAAAAACg/PXjF-G8aBqI/s72-c/vortex.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-5157520535167347599</id><published>2011-01-18T13:13:00.000-08:00</published><updated>2011-01-18T13:13:56.196-08:00</updated><title type='text'>ShaderToy for Android</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_XkozmCCjhTQ/TTYAY2ZeSVI/AAAAAAAAACc/VmIacKFCqj8/s1600/device4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="179" src="http://4.bp.blogspot.com/_XkozmCCjhTQ/TTYAY2ZeSVI/AAAAAAAAACc/VmIacKFCqj8/s320/device4.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&amp;nbsp;At &lt;a href="http://www.gdconf.com/"&gt;GDC&lt;/a&gt; last year, a little-known search engine company called Google were kind enough give me a free "Android" phone (a Motorola Droid). It turned out this was CDMA and so wasn't of much use to me as a phone in the UK, but fortunately it had a keyboard and could run OpenGL ES 2.0, so my next thought was that it would be cool to write a little app that let you edit and compile GLSL shaders directly on the phone. And this is the result.&lt;br /&gt;
&lt;br /&gt;
It's not that easy editing code on a tiny keyboard, and the shader performance of the current generation of Android phones isn't great, but even so it's fun little app for messing around with shaders on the train or whatever.&lt;br /&gt;
&lt;br /&gt;
It requires Android 2.2 and OpenGL ES 2.0. I've only really tested it on the Droid and my Tegra 2 devkit, so I'd be curious to hear how well it works on other devices. It's free, so you can't complain too much.&lt;br /&gt;
&lt;br /&gt;
This is largely inspired by Inigo Quilez's &lt;a href="http://www.iquilezles.org/apps/shadertoy/"&gt;ShaderToy&lt;/a&gt;, and I included some of his shaders, so all due credit to him.&lt;br /&gt;
&lt;br /&gt;
To install it, search "ShaderToy" in the Android Market, or if you're reading this on your phone, just click the link above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-5157520535167347599?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://market.android.com/details?id=com.simongreen.shadertoy' title='ShaderToy for Android'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/5157520535167347599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=5157520535167347599' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5157520535167347599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5157520535167347599'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/01/shadertoy-for-android.html' title='ShaderToy for Android'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_XkozmCCjhTQ/TTYAY2ZeSVI/AAAAAAAAACc/VmIacKFCqj8/s72-c/device4.png' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-1684033812684856852</id><published>2011-01-07T09:51:00.000-08:00</published><updated>2011-01-07T10:02:45.956-08:00</updated><title type='text'>WebGL in a blog post</title><content type='html'>&lt;script id="shader-vs" type="x-shader/x-vertex"&gt;
  attribute vec3 aVertexPosition;
  void main(void) {
    gl_Position = vec4(aVertexPosition, 1.0);
  }
&lt;/script&gt;&lt;br /&gt;
&lt;script id="shader-fs" type="x-shader/x-fragment"&gt;
// distance field ray caster
// simon green 06/01/2011
// 
// based on Inigo Quilezles's:
// http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf
// http://www.iquilezles.org/apps/shadertoy/
// 
// Google Android robot:
// http://www.android.com/branding.html

#ifdef GL_ES
precision highp float;
#endif

uniform vec2 resolution;
uniform float time;

// CSG operations
float _union(float a, float b)
{
    return min(a, b);
}

float intersect(float a, float b)
{
    return max(a, b);
}

float difference(float a, float b)
{
    return max(a, -b);
}

// primitive functions
// these all return the distance to the surface from a given point

float plane(vec3 p, vec3 planeN, vec3 planePos)
{
    return dot(p - planePos, planeN);
}

float box(vec3 p, vec3 abc )
{
    vec3 di=max(abs(p)-abc, 0.0);
    //return dot(di,di);
    return length(di);
}

float sphere(vec3 p, float r)
{
    return length(p) - r;
}

// capsule in Y axis
float capsuleY(vec3 p, float r, float h)
{
    p.y -= clamp(p.y, 0.0, h);
    return length(p) - r;
}

// given segment ab and point c, computes closest point d on ab
// also returns t for the position of d, d(t) = a + t(b-a)
vec3 closestPtPointSegment(vec3 c, vec3 a, vec3 b, out float t)
{
    vec3 ab = b - a;
    // project c onto ab, computing parameterized position d(t) = a + t(b-a)
    t = dot(c - a, ab) / dot(ab, ab);
    // clamp to closest endpoint
    t = clamp(t, 0.0, 1.0);
    // compute projected position
    return a + t * ab;
}

// generic capsule
float capsule(vec3 p, vec3 a, vec3 b, float r)
{
    float t;
    vec3 c = closestPtPointSegment(p, a, b, t);
    return length(c - p) - r;
}

float cylinderY(vec3 p, float r, float h)
{
     float d = length(vec2(p.x, p.z)) - r;
     d = difference(d, plane(p, vec3(0.0, -1.0, 0.0), vec3(0.0, h, 0.0)));
     d = difference(d, plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0)));
     return d;
}


// transforms
vec3 rotateX(vec3 p, float a)
{
    float sa = sin(a);
    float ca = cos(a);
    vec3 r;
    r.x = p.x;
    r.y = ca*p.y - sa*p.z;
    r.z = sa*p.y + ca*p.z;
    return r;
}

vec3 rotateY(vec3 p, float a)
{
    float sa = sin(a);
    float ca = cos(a);
    vec3 r;
    r.x = ca*p.x + sa*p.z;
    r.y = p.y;
    r.z = -sa*p.x + ca*p.z;
    return r;
}


float halfSphere(vec3 p, float r)
{
    return difference( 
               sphere(p, r),
               plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0)) );
}

// distance to scene
float scene(vec3 p)
{
    float d;


#if 1
    // army of droids
    p += vec3(-3.0, 0.0, -3.0);
    p.x = mod(p.x, 6.0);
    p.z = mod(p.z, 6.0);
    p -= vec3(3.0, 0.0, 3.0);
#endif

    p.x = abs(p.x);  // mirror in X to reduce no. of primitives

    p -= vec3(0.0, 1.0, 0.0);
    //vec3 hp = rotateY(p, sin(time*0.5)*0.5);
    vec3 hp = p;

    // head
    //d = sphere(p, 1.0);
    d = halfSphere(hp, 1.0);

    // eyes
    d = _union(d, sphere(hp - vec3(0.3, 0.3, 0.9), 0.1));
    //d = _union(d, sphere(hp - vec3(-0.3, 0.3, 0.9), 0.1));

    // antenna
    d = _union(d, capsule(hp, vec3(0.4, 0.7, 0.0), vec3(0.75, 1.2, 0.0), 0.05));
    //d = _union(d, capsule(hp, vec3(-0.4, 0.7, 0.0), vec3(-0.75, 1.2, 0.0), 0.05));

    // body
    //d = _union(d, cylinderY(p - vec3(0.0, -1.1, 0.0), 1.0, 1.0));
    d = _union(d, capsuleY((p*vec3(1.0, 4.0, 1.0) - vec3(0.0, -4.6, 0.0)), 1.0, 4.0));

    // arms
    //d = _union(d, capsuleY(p - vec3(-1.2, -0.9, 0.0), 0.2, 0.7));
    //d = _union(d, capsuleY(p - vec3(1.2, -0.9, 0.0), 0.2, 0.7));
    d = _union(d, capsuleY(rotateX(p, sin(time)) - vec3(1.2, -0.9, 0.0), 0.2, 0.7));
    //d = _union(d, capsuleY(rotateX(p, cos(time)) - vec3(-1.2, -0.9, 0.0), 0.2, 0.7));

    // legs
    d = _union(d, capsuleY(p - vec3(0.4, -1.8, 0.0), 0.2, 0.5));
    //d = _union(d, capsuleY(p - vec3(-0.4, -1.8, 0.0), 0.2, 0.5));

    // floor
    p += vec3(0.0, 1.0, 0.0);
    d = _union(d, plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0)));

    return d;
}

// calculate scene normal
vec3 sceneNormal( in vec3 pos )
{
    float eps = 0.0001;
    vec3 n;
    n.x = scene( vec3(pos.x+eps, pos.y, pos.z) ) - scene( vec3(pos.x-eps, pos.y, pos.z) );
    n.y = scene( vec3(pos.x, pos.y+eps, pos.z) ) - scene( vec3(pos.x, pos.y-eps, pos.z) );
    n.z = scene( vec3(pos.x, pos.y, pos.z+eps) ) - scene( vec3(pos.x, pos.y, pos.z-eps) );
    return normalize(n);
}

// ambient occlusion approximation
float ambientOcclusion(vec3 p, vec3 n)
{
    const int steps = 3;
    const float delta = 0.5;

    float a = 0.0;
    float weight = 1.0;
    for(int i=1; i&lt;=steps; i++) {
        float d = (float(i) / float(steps)) * delta; 
        a += weight*(d - scene(p + n*d));
        weight *= 0.5;
    }
    return clamp(1.0 - a, 0.0, 1.0);
}

// lighting
vec3 shade(vec3 pos, vec3 n, vec3 eyePos)
{
    const vec3 lightPos = vec3(5.0, 10.0, 5.0);
    const vec3 color = vec3(0.643, 0.776, 0.223);
    const float shininess = 100.0;

    vec3 l = normalize(lightPos - pos);
    vec3 v = normalize(eyePos - pos);
    vec3 h = normalize(v + l);
    float diff = dot(n, l);
    float spec = max(0.0, pow(dot(n, h), shininess)) * float(diff &gt; 0.0);
    //diff = max(0.0, diff);
    diff = 0.5+0.5*diff;

    float fresnel = pow(1.0 - dot(n, v), 5.0);
    float ao = ambientOcclusion(pos, n);

//    return vec3(diff*ao) * color + vec3(spec + fresnel*0.5);
    return vec3(diff*ao) * color + vec3(spec);
//    return vec3(ao);
//    return vec3(fresnel);
}

// trace ray using sphere tracing
vec3 trace(vec3 ro, vec3 rd, out bool hit)
{
    const int maxSteps = 128;
    const float hitThreshold = 0.01;
    hit = false;
    vec3 pos = ro;

    for(int i=0; i&lt;maxSteps; i++)
    {
        float d = scene(pos);
        if (d &lt; hitThreshold) {
            hit = true;
            return pos;
        }
        pos += d*rd;
    }
    return pos;
}

vec3 background(vec3 rd)
{
     //return mix(vec3(1.0), vec3(0.0), rd.y);
     return mix(vec3(1.0), vec3(0.0, 0.25, 1.0), rd.y);
     //return vec3(0.0);
}

void main(void)
{
    vec2 pixel = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;

    // compute ray origin and direction
    float asp = resolution.x / resolution.y;
    vec3 rd = normalize(vec3(asp*pixel.x, pixel.y, -2.0));
    vec3 ro = vec3(0.0, 0.5, 4.5);

    float a;
    //    a = sin(time*0.3)*1.5;
    a = time*0.5;
    rd = rotateY(rd, a);
    ro = rotateY(ro, a);
    a = sin(time*0.3)*0.3;
    rd = rotateX(rd, a);
    ro = rotateX(ro, a);

    // trace ray
    bool hit;
    vec3 pos = trace(ro, rd, hit);

    vec3 rgb;
    if(hit)
    {
        // calc normal
        vec3 n = sceneNormal(pos);
        // shade
        rgb = shade(pos, n, ro);

#if 1
        // reflection
        vec3 v = normalize(ro - pos);
        float fresnel = 0.1 + 0.4*pow(1.0 - dot(n, v), 5.0);

        ro = pos + n*0.01; // offset to avoid self-intersection
        rd = reflect(-v, n);
        pos = trace(ro, rd, hit);

        if (hit) {
            vec3 n = sceneNormal(pos);
            rgb += shade(pos, n, ro) * vec3(fresnel);
        } else {
            rgb += background(rd) * vec3(fresnel);
        }
#endif 

     } else {
        rgb = background(rd);
     }

    // vignetting
    rgb *= 0.5+0.5*smoothstep(2.0, 0.5, dot(pixel, pixel));

    gl_FragColor=vec4(rgb, 1.0);
}
&lt;/script&gt;
&lt;script id="simple-fs" type="x-shader/x-fragment"&gt;
precision highp float;

uniform vec2 resolution;
uniform float time;

void main(void)
{
    gl_FragColor = vec4(gl_FragCoord.x / resolution.x, gl_FragCoord.y / resolution.y, time - floor(time), 1.0);
}
&lt;/script&gt;&lt;br /&gt;
&lt;script type="text/javascript"&gt;

  var canvas;
  var startTime; 
  var gl;
  var squareVertexPositionBuffer;
  var shaderProgram;

  function initGL(canvas) {
    try {
      gl = canvas.getContext("experimental-webgl");
      gl.viewportWidth = canvas.width;
      gl.viewportHeight = canvas.height;
    } catch(e) {
    }
    if (!gl) {
      alert("Could not initialise WebGL!");
    }
    startTime = (new Date()).getTime();
  }


  function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
      return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
      if (k.nodeType == 3) {
        str += k.textContent;
      }
      k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
      shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
      shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
      return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert(gl.getShaderInfoLog(shader));
      return null;
    }

    return shader;
  }


  function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    //var fragmentShader = getShader(gl, "simple-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pResolutionUniform = gl.getUniformLocation(shaderProgram, "resolution");
    shaderProgram.pTimeUniform = gl.getUniformLocation(shaderProgram, "time");
  }


  function setUniforms() {
    gl.uniform2f(shaderProgram.pResolutionUniform, canvas.width, canvas.height);
    var time = (new Date()).getTime();
    gl.uniform1f(shaderProgram.pTimeUniform, (time - startTime) / 1000.0);
  }


  function initBuffers() {
    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    vertices = [
         1.0,  1.0,  0.0,
        -1.0,  1.0,  0.0,
         1.0, -1.0,  0.0,
        -1.0, -1.0,  0.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = 4;
  }

  function onWindowResize( event ) {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    gl.viewport( 0, 0, canvas.width, canvas.height );
  }


  function drawScene() {
    //gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
  }


  function webGLStart() {
    canvas = document.getElementById("my-canvas");
    initGL(canvas);
    initShaders()
    initBuffers();

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.disable(gl.DEPTH_TEST);

    setInterval(drawScene, 16);
  }

&lt;/script&gt;&lt;br /&gt;
I was looking for somewhere to host my WebGL shaders when I came across &lt;a href="http://www.jaanga.com/2010/08/webgl-in-blogger-posts.html"&gt;this&lt;/a&gt; post. And it actually works!&lt;br /&gt;
&lt;br /&gt;
You'll need Firefox 4.0b8 with the "webgl.prefer_gl" config set to "true" for this to work.&lt;br /&gt;
&lt;br /&gt;
&lt;form&gt;&lt;input onclick="webGLStart()" type="button" value="Click me!" /&gt; &lt;/form&gt;&lt;canvas width="400" height="300" id="my-canvas" &gt; Your browser doesn't appear to support the HTML5 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. &lt;/canvas&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-1684033812684856852?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/1684033812684856852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=1684033812684856852' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1684033812684856852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1684033812684856852'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/01/test.html' title='WebGL in a blog post'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-8680532527872904275</id><published>2011-01-06T12:21:00.000-08:00</published><updated>2011-01-07T02:15:05.777-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webgl google android robot distance field'/><title type='text'>Ray traced Google Robot in WebGL</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_XkozmCCjhTQ/TSYhHZcdBxI/AAAAAAAAACU/A7Af_LuxbuM/s1600/droid1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_XkozmCCjhTQ/TSYhHZcdBxI/AAAAAAAAACU/A7Af_LuxbuM/s400/droid1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_XkozmCCjhTQ/TSYkGGAgtuI/AAAAAAAAACY/IhrAES4ZJfI/s1600/droid_ao.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="319" src="http://1.bp.blogspot.com/_XkozmCCjhTQ/TSYkGGAgtuI/AAAAAAAAACY/IhrAES4ZJfI/s400/droid_ao.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
It seems like everybody is rendering with &lt;a href="http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf"&gt;distance fields&lt;/a&gt; these days. I'm somewhat late to the party, but distance fields do have a lot of interesting advantages:&lt;br /&gt;
- you can ray-cast them very quickly using &lt;a href="http://graphics.cs.uiuc.edu/%7Ejch/papers/zeno.pdf"&gt;sphere tracing&lt;/a&gt;&lt;br /&gt;
- it's very easy to do &lt;a href="http://en.wikipedia.org/wiki/Constructive_solid_geometry"&gt;CSG&lt;/a&gt; operations on them (union, difference etc.)&lt;br /&gt;
- there's a clever fast approximation to ambient occlusion (originally due to Alex Evans, I think). &lt;br /&gt;
&lt;br /&gt;
My original plan was do to a real-time version of the original light cycle from Tron (which is apparently comprised of only 57 geometric primitives), but that turned out to be a bit complicated. When I saw the Google Android robot, it seemed like a more suitable target (it's very easy to calculate the distance to spheres and capsules, and you can see the result above. &lt;br /&gt;
&lt;br /&gt;
Anyway, the code is below, you can paste this into Iniqo Quielz's excellent&amp;nbsp;&lt;a href="http://www.iquilezles.org/apps/shadertoy/"&gt;ShaderToy&lt;/a&gt; and play with it yourself using WebGL. (It's somewhat better commented than most of the example shaders there, too.) You'll need the latest Chrome or Firefox beta to run this, there's a good FAQ &lt;a href="http://learningwebgl.com/blog/"&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// distance field ray caster&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// simon green 06/01/2011&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;//&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// based on Iniqo Quilez's:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// http://www.iquilezles.org/apps/shadertoy/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;//&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// Google Android robot:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// http://www.android.com/branding.html&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;#ifdef GL_ES&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;precision highp float;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;uniform vec2 resolution;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;uniform float time;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// CSG operations&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float _union(float a, float b)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return min(a, b);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float intersect(float a, float b)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return max(a, b);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float difference(float a, float b)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return max(a, -b);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// primitive functions&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// these all return the distance to the surface from a given point&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float plane(vec3 p, vec3 planeN, vec3 planePos)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return dot(p - planePos, planeN);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float box(vec3 p, vec3 abc )&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 di=max(abs(p)-abc, 0.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//return dot(di,di);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return length(di);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float sphere(vec3 p, float r)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return length(p) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// capsule in Y axis&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float capsuleY(vec3 p, float r, float h)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (p.y &amp;lt; 0.0) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return length(p) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;} else if (p.y &amp;gt; h) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return length(p - vec3(0, h, 0)) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;} else {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return length(vec2(p.x, p.z)) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// given segment ab and point c, computes closest point d on ab&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// also returns t for the position of d, d(t) = a + t(b-a)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;vec3 closestPtPointSegment(vec3 c, vec3 a, vec3 b, out float t)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 ab = b - a;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// project c onto ab, computing parameterized position d(t) = a + t(b-a)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;t = dot(c - a, ab) / dot(ab, ab);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// clamp to closest endpoint&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;t = clamp(t, 0.0, 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// compute projected position&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return a + t * ab;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// generic capsule&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float capsule(vec3 p, vec3 a, vec3 b, float r)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float t;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 c = closestPtPointSegment(p, a, b, t);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return length(c - p) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float cylinderY(vec3 p, float r, float h)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; float d = length(vec2(p.x, p.z)) - r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; d = difference(d, plane(p, vec3(0.0, -1.0, 0.0), vec3(0.0, h, 0.0)));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; d = difference(d, plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0)));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; return d;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// transforms&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;vec3 rotateX(vec3 p, float a)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float sa = sin(a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float ca = cos(a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.x = p.x;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.y = ca*p.y - sa*p.z;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.z = sa*p.y + ca*p.z;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;vec3 rotateY(vec3 p, float a)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float sa = sin(a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float ca = cos(a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.x = ca*p.x + sa*p.z;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.y = p.y;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;r.z = -sa*p.x + ca*p.z;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return r;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float halfSphere(vec3 p, float r)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return difference(&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sphere(p, r),&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0)) );&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// distance to scene&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float scene(vec3 p)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//return box(p, vec3(1.0, 1.0, 1.0));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//return sphere(p, vec3(0.0), 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float d = 1e10;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;p -= vec3(0.0, 1.0, 0.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 hp = rotateY(p, sin(time*0.5)*0.5);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// head&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//d = sphere(p, 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = halfSphere(hp, 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// eyes&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, sphere(hp - vec3(-0.3, 0.3, 0.9), 0.1));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, sphere(hp - vec3(0.3, 0.3, 0.9), 0.1));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// antenna&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsule(hp, vec3(-0.4, 0.7, 0.0), vec3(-0.75, 1.2, 0.0), 0.05));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsule(hp, vec3(0.4, 0.7, 0.0), vec3(0.75, 1.2, 0.0), 0.05));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// body&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsuleY((p*vec3(1.0, 4.0, 1.0) - vec3(0.0, -4.6, 0.0)), 1.0, 4.0));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//d = _union(d, cylinderY(p - vec3(0.0, -1.1, 0.0), 1.0, 1.0));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// arms&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//d = _union(d, capsuleY(p - vec3(-1.2, -0.9, 0.0), 0.2, 0.7));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//d = _union(d, capsuleY(p - vec3(1.2, -0.9, 0.0), 0.2, 0.7));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsuleY(rotateX(p, cos(time)) - vec3(-1.2, -0.9, 0.0), 0.2, 0.7));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsuleY(rotateX(p, sin(time)) - vec3(1.2, -0.9, 0.0), 0.2, 0.7));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// legs&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsuleY(p - vec3(-0.4, -1.8, 0.0), 0.2, 0.5));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, capsuleY(p - vec3(0.4, -1.8, 0.0), 0.2, 0.5));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// floor&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;p += vec3(0.0, 1.0, 0.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;d = _union(d, plane(p, vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0)));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return d;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// calculate scene normal&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;vec3 sceneNormal( in vec3 pos )&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float eps = 0.0001;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 n;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;n.x = scene( vec3(pos.x+eps, pos.y, pos.z) ) - scene( vec3(pos.x-eps, pos.y, pos.z) );&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;n.y = scene( vec3(pos.x, pos.y+eps, pos.z) ) - scene( vec3(pos.x, pos.y-eps, pos.z) );&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;n.z = scene( vec3(pos.x, pos.y, pos.z+eps) ) - scene( vec3(pos.x, pos.y, pos.z-eps) );&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return normalize(n);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;// ambient occlusion approximation&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;float ambientOcclusion(vec3 p, vec3 n)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;const int steps = 3;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;const float delta = 0.5;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float a = 0.0;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float weight = 1.0;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;for(int i=1; i&amp;lt;=steps; i++) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float d = (float(i) / float(steps)) * delta;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;a += weight*(d - scene(p + n*d));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;weight *= 0.5;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return clamp(1.0 - a, 0.0, 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;void main(void)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec2 pixel = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// compute ray origin and direction&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float asp = resolution.x / resolution.y;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 rd = normalize(vec3(asp*pixel.x, pixel.y, -2.0));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 ro = vec3(0.0, 0.5, 4.5);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float a = time*0.5;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//rd = rotateY(rd, a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//ro = rotateY(ro, a);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;float t = 1.0;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;bool hit = false;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 pos;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// cast ray (sphere tracing)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;for(int i=0; i&amp;lt;64; i++)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;pos = ro + t*rd;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float d = scene(pos);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (d &amp;lt; 0.01) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;hit = true;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;break;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;t += d;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// shade&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;vec3 rgb;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if(hit)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// calc normal&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vec3 n = sceneNormal(pos);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// lighting&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;const vec3 lightPos = vec3(5.0, 10.0, 5.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;const vec3 color = vec3(0.643, 0.776, 0.223);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;const float shininess = 100.0;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vec3 l = normalize(lightPos - pos);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vec3 v = normalize(ro - pos);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vec3 h = normalize(v + l);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float diff = dot(n, l);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float spec = max(0.0, pow(dot(n, h), shininess)) * float(diff &amp;gt; 0.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//diff = max(0.0, diff);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;diff = 0.5+0.5*diff;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float fresnel = pow(1.0 - dot(n, v), 5.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;float ao = ambientOcclusion(pos, n);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;rgb = vec3(diff*ao) * color + vec3(spec + fresnel*0.5);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//rgb = vec3(ao);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//rgb = vec3(fresnel);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; } else {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;rgb = mix(vec3(1.0), vec3(0.0), rd.y);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// vignetting&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;rgb *= 0.5+0.5*smoothstep(2.0, 0.5, dot(pixel, pixel));&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;gl_FragColor=vec4(rgb, 1.0);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt; &lt;/span&gt;&lt;span style="font-family: 'Courier New',Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-8680532527872904275?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/8680532527872904275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=8680532527872904275' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/8680532527872904275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/8680532527872904275'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2011/01/ray-traced-google-robot-in-webgl.html' title='Ray traced Google Robot in WebGL'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XkozmCCjhTQ/TSYhHZcdBxI/AAAAAAAAACU/A7Af_LuxbuM/s72-c/droid1.jpg' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-412636394088295242</id><published>2010-11-17T05:21:00.000-08:00</published><updated>2010-11-17T05:56:10.813-08:00</updated><title type='text'>Random screen shot of the day</title><content type='html'>&lt;div style="text-align: left;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XkozmCCjhTQ/TOPW57nQRFI/AAAAAAAAACI/oTwUtul63yM/s1600/nice_flame.png"&gt;&lt;img style="cursor: pointer; width: 308px; height: 320px;" src="http://1.bp.blogspot.com/_XkozmCCjhTQ/TOPW57nQRFI/AAAAAAAAACI/oTwUtul63yM/s320/nice_flame.png" alt="" id="BLOGGER_PHOTO_ID_5540508257339065426" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XkozmCCjhTQ/TOPW5pID4mI/AAAAAAAAACA/EAFcweUqXIA/s1600/fire_shaded.png"&gt;&lt;img style="cursor: pointer; width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_XkozmCCjhTQ/TOPW5pID4mI/AAAAAAAAACA/EAFcweUqXIA/s320/fire_shaded.png" alt="" id="BLOGGER_PHOTO_ID_5540508252376392290" border="0" /&gt;&lt;/a&gt;
&lt;/div&gt;
I realised that I haven't been posting much here recently, so I decided to post some of the random images that are sitting around on my hard drive gathering dust.

These are from a 2D CUDA fire simulation which I submitted as a talk to Siggraph this year, but  was cruely rejected. I'm still sulking about this, but I guess I shouldn't feel too bad since we were up against &lt;a href="http://www.siggraph.org/s2010/for_attendees/talk/206"&gt;ILM, Dreamworks and DNeg&lt;/a&gt;, none of whom are exactly amateurs in the simulation department.

The technique is based on Chris Horvath and Willi Geiger's excellent &lt;a href="http://portal.acm.org/citation.cfm?doid=1531326.1531347"&gt;paper &lt;/a&gt;from Siggraph 2009. In my opinion one of the most interesting  contributions of this paper is its use of a noise texture which is advected (i.e. moved) by the velocity field, and used to add detail to the rendering and simulation. The simulation code is based on Mark Harris's &lt;a href="http://http.developer.nvidia.com/GPUGems/gpugems_ch38.html"&gt;chapter &lt;/a&gt;from GPU Gems 1, and he helped optimizing the CUDA solver.

The idea was to use this to replace those obviously repeating animated fire sprites you see in many games these days - you would run a bunch of small independent simulations on the GPU and display them on billboards. Anyway, hopefully someday soon I'll finish this up and release the code!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-412636394088295242?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/412636394088295242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=412636394088295242' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/412636394088295242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/412636394088295242'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2010/11/random-screen-shot-of-day.html' title='Random screen shot of the day'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XkozmCCjhTQ/TOPW57nQRFI/AAAAAAAAACI/oTwUtul63yM/s72-c/nice_flame.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-609850351776164785</id><published>2010-03-19T03:47:00.000-07:00</published><updated>2010-03-19T04:21:40.260-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gpu fluid splash painting cuda'/><title type='text'>Digital Splash Paintings</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XkozmCCjhTQ/S6NWnRnAFSI/AAAAAAAAABg/dDP6uJIursI/s1600-h/image_0_yellow.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_XkozmCCjhTQ/S6NWnRnAFSI/AAAAAAAAABg/dDP6uJIursI/s320/image_0_yellow.jpg" alt="" id="BLOGGER_PHOTO_ID_5450295206790305058" border="0" /&gt;&lt;/a&gt;

As part of my work on fluid simulation I've been experimenting with methods to make  fluid leave marks on the environment as it moves. As an offshoot of this work, I thought it would be fun to try generating some &lt;a href="http://en.wikipedia.org/wiki/Jackson_Pollock"&gt;Jackson Pollock&lt;/a&gt; style splash paintings. Here are the results, generated by my GPU fluid simulator. I'll post more pictures on &lt;a href="http://www.flickr.com/photos/40432072@N00/sets/72157623525339075/"&gt;flickr  &lt;/a&gt;as I generate them.

While not great artistically, it's satisfying to me how close to real paint these look. This whole thing may seem slightly pointless to some (why not use actual frickin' paint and paper?!), but doing this in the digital domain does offer some interesting non-realistic possibilities, such as paint that changes colour based on its velocity etc.

Next step - submit to pretentious art gallery ("it's done on a computer, see?"). Step two, profit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-609850351776164785?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.flickr.com/photos/40432072@N00/sets/72157623525339075/' title='Digital Splash Paintings'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/609850351776164785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=609850351776164785' title='36 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/609850351776164785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/609850351776164785'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2010/03/as-part-of-my-work-on-fluid-simulation.html' title='Digital Splash Paintings'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_XkozmCCjhTQ/S6NWnRnAFSI/AAAAAAAAABg/dDP6uJIursI/s72-c/image_0_yellow.jpg' height='72' width='72'/><thr:total>36</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-3552348279421877633</id><published>2010-01-10T10:59:00.001-08:00</published><updated>2010-01-10T11:36:49.333-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scan ipod slides 35mm'/><title type='text'>How to Scan 35mm Slides Using a Flat-bed Scanner And an iPod As a Backlight</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XkozmCCjhTQ/S0oj70bKSLI/AAAAAAAAABY/4Q_RTjL-OsA/s1600-h/Scan_demo.jpeg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 181px;" src="http://2.bp.blogspot.com/_XkozmCCjhTQ/S0oj70bKSLI/AAAAAAAAABY/4Q_RTjL-OsA/s320/Scan_demo.jpeg" alt="" id="BLOGGER_PHOTO_ID_5425188211713001650" border="0" /&gt;&lt;/a&gt;

I had a few old 35mm slides I wanted to scan this weekend. Not wanting to shell out for a dedicated slide scanner, it seemed like I should be able to use my flat-bed scanner instead. It turns out there are a couple of problems with this. First of all, these kind of scanners are designed to scan images that are flat on the glass, and so mounted slides (which are offset slightly from the glass) appear very out of focus. This is easily fixed by removing the slide from the mount and putting on the glass directly. The second, more serious problem is that the scanner is designed to measure light reflected from paper, where as slides are meant to illuminated from the back (transmissive), so they usually turn out much too dark in the scan. Searching for answer, I found this &lt;a href="http://www.abstractconcreteworks.com/essays/scanning/Backlighter.html"&gt;link&lt;/a&gt;, where someone has built a simple backlight out of paper that attempts to reflect light from the scanner around the back of the slide. Unfortunately this didn't work for me, probably because my Canon Lide scanner uses LEDs for illumination. So instead I had the idea to use my iPod, displaying a pure white image, as a backlight. This works amazingly well. The iPod screen fits flush on the glass, and you can change the display brightness to adjust the exposure. Sadly it only works in black and white, because the white light interferes with the R/G/B illumination of the scanner, but the results are still pretty good (see above).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-3552348279421877633?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/3552348279421877633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=3552348279421877633' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3552348279421877633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3552348279421877633'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2010/01/how-to-scan-35mm-slides-using-flat-bed.html' title='How to Scan 35mm Slides Using a Flat-bed Scanner And an iPod As a Backlight'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XkozmCCjhTQ/S0oj70bKSLI/AAAAAAAAABY/4Q_RTjL-OsA/s72-c/Scan_demo.jpeg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-7721863195527504187</id><published>2010-01-05T08:25:00.000-08:00</published><updated>2010-01-05T09:01:11.473-08:00</updated><title type='text'>Adventures in Rapid Prototyping</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XkozmCCjhTQ/S0NoNUtWCpI/AAAAAAAAABQ/LbEdcLXhoPo/s1600-h/monkey.jpg"&gt;&lt;img style="cursor: pointer; width: 240px; height: 320px;" src="http://1.bp.blogspot.com/_XkozmCCjhTQ/S0NoNUtWCpI/AAAAAAAAABQ/LbEdcLXhoPo/s320/monkey.jpg" alt="" id="BLOGGER_PHOTO_ID_5423292954390694546" border="0" /&gt;&lt;/a&gt;

Happy New Year! A slight change from our usual subject matter here.

I had a bit of free time over the holidays, so I decided to try out this &lt;a href="http://en.wikipedia.org/wiki/3D_printing"&gt;3D printing&lt;/a&gt; technology all the cool kids are &lt;a href="http://craphound.com/makers/download/"&gt;talking&lt;/a&gt; about. I had visited the &lt;a href="http://www.shapeways.com/"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Shapeways&lt;/span&gt;&lt;/a&gt; booth at &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Siggraph&lt;/span&gt;, and was impressed with their sample models, so I decided to try out their service (they're based in the Netherlands, so they're pretty convenient for here in the U.K.). Not having a huge amount of modelling skills, I chose to try and recreate Kay &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Bojesen's&lt;/span&gt; classic &lt;a href="http://www.retrotogo.com/2006/09/kay_bojesen_mon.html"&gt;monkey&lt;/a&gt; sculpture, which has the advantage of being mainly &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;constructed&lt;/span&gt; out of geometric cylinders and spheres! The trickiest part is that you have to provide them with a polygon mesh which is completely water-tight and manifold, which is easier said than done in Maya. Anyway, the results can be seen above. It turned out amazingly well, you can see a bit of stair-stepping (physical aliasing?) but every tiny detail is visible (I kind of wish I'd smoothed the mesh more). The material is surprisingly hard and has a rough finish. I would recommend &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Shapeways&lt;/span&gt;' service, although it's a little expensive - this little guy was $15. They also have an amazing new stainless steel material which would be fun to try out with a smaller model.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-7721863195527504187?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/7721863195527504187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=7721863195527504187' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7721863195527504187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7721863195527504187'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2010/01/adventures-in-rapid-prototyping.html' title='Adventures in Rapid Prototyping'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XkozmCCjhTQ/S0NoNUtWCpI/AAAAAAAAABQ/LbEdcLXhoPo/s72-c/monkey.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-9100356117486616341</id><published>2009-11-17T01:16:00.000-08:00</published><updated>2010-01-05T08:25:07.315-08:00</updated><title type='text'>2012 == Fluid</title><content type='html'>&lt;object width="400" height="242"&gt;&lt;param name="movie" value="http://www.youtube.com/v/24-Z80_I9cU&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;rel=0"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/24-Z80_I9cU&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="400" height="242"&gt;&lt;/embed&gt;&lt;/object&gt;

Went to see &lt;a href="http://whowillsurvive2012.com/"&gt;2012 &lt;/a&gt;this weekend. Stupid film, but some great fluid simulations! Pretty much the whole thing is people being chased by natural phenomena. But where else can you see Danny Glover (playing the least convincing American president ever) get hit by an aircraft carrier being carried on a giant tidal wave?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-9100356117486616341?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/9100356117486616341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=9100356117486616341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/9100356117486616341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/9100356117486616341'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/11/2012-fluid.html' title='2012 == Fluid'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-4659398946992727901</id><published>2009-10-01T10:01:00.000-07:00</published><updated>2009-10-01T15:22:18.732-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pixar gpu realtime'/><title type='text'>Real time Toy Story 3D?</title><content type='html'>As the first all-CG movie, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Pixar's&lt;/span&gt; Toy Story has always been something of an aspirational target for real-time graphics. When &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;NVIDIA&lt;/span&gt; launched the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;GeForce&lt;/span&gt; 2 back in 2000, Jen-&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Hsun&lt;/span&gt; Huang said it was a "major step" towards achieving "&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Pixar&lt;/span&gt;-level animation" in real-time. This may have been overstating things a bit, and &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_5"&gt;it elicited&lt;/span&gt; the following hilarious response from &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;Pixar's&lt;/span&gt; Tom Duff on the &lt;a href="http://groups.google.co.uk/group/comp.graphics.rendering.renderman/browse_thread/thread/3271d7feceba4e10/154a658380cbb3ce?hl=en"&gt;comp.graphics.rendering.renderman&lt;/a&gt; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;newgroup&lt;/span&gt;:
&lt;p style="font-style: italic;"&gt;"These guys just have no idea what goes into `Pixar-level animation.' (That's not quite fair, their engineers do, they come and visit all the time.  But their managers and marketing monkeys haven't a clue, or possibly just think that you don't.)
&lt;/p&gt;&lt;p style="font-style: italic;"&gt;`Pixar-level animation' runs about 8 hundred thousand times slower than real-time on our renderfarm cpus.  (I'm guessing.  There's about 1000 cpus in the renderfarm and I guess we could produce all the frames in TS2 in about 50 days of renderfarm time.  That comes to 1.2 million cpu hours for a 1.5 hour movie.  That lags real time by a factor of 800,000.)
&lt;/p&gt;&lt;p style="font-style: italic;"&gt;Do you really believe that their toy is a million times faster than one of the cpus on our Ultra Sparc servers?  What's the chance that we wouldn't put one of these babies on every desk in the building?  They cost a couple of hundred bucks, right?  Why hasn't NVIDIA tried to give us a carton of these things? -- think of the publicity milage they could get out of it!
&lt;/p&gt;&lt;p style="font-style: italic;"&gt;Don't forget that the scene descriptions of TS2 frames average between 500MB and 1GB.  The data rate required to read the data in real time is at least 96Gb/sec. Think your AGP port can do that?  Think again.  96 Gb/sec means that if they clock data in at 250 MHz, they need a bus 384 bits wide.  NBL!
&lt;/p&gt;&lt;span style="font-style: italic;"&gt;At Moore's Law-like rates (a factor of 10 in 5 years), even if the hardware they have today is 80 times more powerful than what we use now, it will take them 20 years before they can do &lt;/span&gt;
&lt;span style="font-style: italic;"&gt; the frames we do today in real time.  And 20 years from now, Pixar won't be even remotely interested in TS2-level images, and I'll be retired, sitting on the front porch and picking my banjo, laughing at the same press release, recycled by NVIDIA's heirs and assigns. "&lt;/span&gt;

Well, it's only 10 years later, and I have no idea if Tom is sitting on his porch yet, but our "toys" are certainly getting closer to achieving this. 500MB of data per frame doesn't sound unreasonable these days.

Anyway, I was reminded about all this by the recent re-release of Toy Story in 3D, and &lt;a href="http://watchingapple.com/2009/09/pixars-blistering-rendering-speed-for-toy-story-3d/"&gt;this&lt;/a&gt; news story that claims when they re-rendered it, it took less than 1/24&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_27"&gt;th&lt;/span&gt; of a second per frame:

&lt;span style="font-style: italic;"&gt;"The process of rendering the films — or translating computer data into images — was vastly accelerated by current technology. Where the original “Toy Story” required an hour per frame to create, Mr. &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_28"&gt;Lasseter&lt;/span&gt; said, rendering the new 3-D version took less than 1/24&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_29"&gt;th&lt;/span&gt; of a second per frame.— &lt;/span&gt;&lt;a style="font-style: italic;" class="attr" href="http://online.wsj.com/article/SB125201712352284765.html"&gt;Disney Seeks Buzz With ‘Toy Story’ Re-Release&lt;/a&gt;&lt;span style="font-style: italic;"&gt;, The Wall Street Journal, September 4, 2009&lt;/span&gt;"

So maybe we can already render Toy Story in realtime, given a big enough render farm. It's impressive how far we've all come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-4659398946992727901?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/4659398946992727901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=4659398946992727901' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4659398946992727901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4659398946992727901'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/10/real-time-toy-story-3d.html' title='Real time Toy Story 3D?'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-1953495504612345270</id><published>2009-10-01T09:28:00.001-07:00</published><updated>2009-10-01T09:48:44.075-07:00</updated><title type='text'>New fluid simulation video posted</title><content type='html'>On YouTube &lt;a href="http://www.youtube.com/watch?v=r17UOMZJbGs"&gt;here&lt;/a&gt;.

The main improvements here are that it's been optimized (so we can do 128K particles with SPH at 70fps now), and includes surface tension effects so you get nice droplets and splashes. It also includes some improvements to our screen-space surface rendering technique (thanks to Rouslan Dimitrov for helping with this).

Can't wait to see this running on &lt;a href="http://www.nvidia.com/object/fermi_architecture.html"&gt;Fermi&lt;/a&gt;. Stop me if you're getting bored of these...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-1953495504612345270?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.youtube.com/watch?v=r17UOMZJbGs' title='New fluid simulation video posted'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/1953495504612345270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=1953495504612345270' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1953495504612345270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1953495504612345270'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/10/new-fluid-simulation-video-posted.html' title='New fluid simulation video posted'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-5752640963179720447</id><published>2009-05-07T07:53:00.000-07:00</published><updated>2009-05-07T08:08:01.647-07:00</updated><title type='text'>Siggraph 2009 papers</title><content type='html'>The official lists haven't been posted yet, but as usual &lt;a href="http://kesen.huang.googlepages.com/"&gt;Ke-Sen Huang&lt;/a&gt; is maintaining a list of accepted papers at Siggraph 2009:
http://kesen.huang.googlepages.com/sig2009.html

Only a few PDFs are available - there are a few interesting sounding titles (I dig &lt;a href="http://www.cs.caltech.edu/%7Epatrickm/"&gt;Energy-Preserving Integrators for Fluid Animation&lt;/a&gt;, for example), but so far it seems like the reviewing panel has really excelled themselves this year in accepting mathematically interesting but so-far-from-being-practical-it's-not-even-funny papers! Or maybe I'm just getting old and miserable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-5752640963179720447?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kesen.huang.googlepages.com/sig2009.html' title='Siggraph 2009 papers'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/5752640963179720447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=5752640963179720447' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5752640963179720447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/5752640963179720447'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/05/siggraph-2009-papers.html' title='Siggraph 2009 papers'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-4036761138139609111</id><published>2009-03-24T09:00:00.000-07:00</published><updated>2009-03-24T09:12:23.266-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gdc09 fluid simulation particles'/><title type='text'>GDC 09: Cool new fluid simulation demo</title><content type='html'>NVIDIA just released &lt;a href="http://www.youtube.com/watch?v=RuZQpWo9Qhs"&gt;video&lt;/a&gt; of a very impressive new fluid simulation demo. It uses a grid-based fluid simulation running on CUDA (written by &lt;a href="http://www.jcohen.name/"&gt;Jonathan Cohen&lt;/a&gt;) that translates and interacts with the car, and is used to advect about 0.5 million particles which are rendered with volumetric shadows. It's running on two GeForce GTX 280s, one running the simulation, and one doing the rendering (but it's scalable). It's amazing to me that this kind of thing was only possible offline only a few years ago, and is now running in real-time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-4036761138139609111?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.youtube.com/watch?v=RuZQpWo9Qhs' title='GDC 09: Cool new fluid simulation demo'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/4036761138139609111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=4036761138139609111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4036761138139609111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4036761138139609111'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/03/gdc-09-cool-new-fluid-simulation-demo.html' title='GDC 09: Cool new fluid simulation demo'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-7110294253455399220</id><published>2009-03-16T10:32:00.000-07:00</published><updated>2009-03-16T10:38:09.852-07:00</updated><title type='text'>GDC Direct3D Tutorial Slides Posted</title><content type='html'>The GDC organizers have made the unusual move of publishing slides from some of the tutorial sessions ahead of the actual conference. You can find them &lt;a href="http://www.gdconf.com/conference/tutorials.html"&gt;here&lt;/a&gt;, but I think it it's still worth attending the tutorials for all the extra details.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-7110294253455399220?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.gdconf.com/conference/tutorials.html' title='GDC Direct3D Tutorial Slides Posted'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/7110294253455399220/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=7110294253455399220' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7110294253455399220'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7110294253455399220'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/03/gdc-direct3d-tutorial-slides-posted.html' title='GDC Direct3D Tutorial Slides Posted'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-3070763095277688985</id><published>2009-02-26T03:16:00.000-08:00</published><updated>2009-02-26T04:01:37.941-08:00</updated><title type='text'>GDC 2009</title><content type='html'>I have to say that I've never been a huge fan of the &lt;a href="http://www.gdconf.com/"&gt;Game Developers Conference&lt;/a&gt;. It's massively over-priced (if you're unfortunate enough to have to pay), often poorly organized, and on the whole the quality of the technical presentations (including my own) is somewhere between a high school science fair and an Asperger's syndrome help group. It ain't no &lt;a href="http://www.siggraph.org/s2009/"&gt;Siggraph&lt;/a&gt;, that's for sure. At least they moved it from the incredibly dull San Jose to San Francisco, where they have such luxuries as bars that stay open past 10pm. GDC's only saving grace is that everyone in the games industry goes there, so it's a great place to network and meet people (read: find a new job).

All that said, I'll be there this year, and there are a few talks that look interesting:

&lt;a href="https://www.cmpevents.com/GD09/a.asp?option=C&amp;amp;V=11&amp;amp;SessID=8536"&gt;&lt;b class="subhead"&gt;Advanced Visual Effects with Direct3D for PC&lt;/b&gt;&lt;/a&gt;
- learn about the latest developments in Direct3D, and see AMD and NVIDIA make snide digs at each other under their breath.

&lt;a href="https://www.cmpevents.com/GD09/a.asp?option=C&amp;amp;V=11&amp;amp;SessID=8531"&gt;&lt;b class="subhead"&gt;Math for Programmers/Physics for Programmers&lt;/b&gt;&lt;/a&gt;
- these guys know their physics. &lt;a href="http://www.iii.u-tokyo.ac.jp/%7Etakahiroharada/"&gt;Takahiro Harada&lt;/a&gt; (who shares my love of simulating colliding balls and now works at Havok) and Erwin Coumans (of &lt;a href="http://www.bulletphysics.com/Bullet/wordpress/"&gt;Bullet&lt;/a&gt; fame) will be talking about implementing rigid body physics on CUDA/OpenCL, Cell SPU and Larrabee, which should be interesting (especially given how different these 3 architectures are).

&lt;a href="https://www.cmpevents.com/GD09/a.asp?option=C&amp;amp;V=11&amp;amp;SessID=8539"&gt;&lt;b class="subhead"&gt;Mixed Resolution Rendering&lt;/b&gt;&lt;/a&gt;
- sounds like Jeremy is really into cross-bilateral filtering.

&lt;a style="font-weight: bold;" class="bodytext" href="https://www.cmpevents.com/GD09/a.asp?option=C&amp;amp;V=11&amp;amp;SessID=9138"&gt;Rasterization on Larrabee: A First Look at the Larrabee New Instructions (LRBni) in Action&lt;/a&gt;
- apparently Intel have some kind of new CPU that's meant to do graphics and this talk is about some new variant of MMX it has. Michael Abrash is something of a legend and always an entertaining speaker.

&lt;a href="https://www.cmpevents.com/GD09/a.asp?option=C&amp;amp;V=11&amp;amp;SessID=8873"&gt;&lt;b class="subhead"&gt;The Rendering Technology of KILLZONE 2&lt;/b&gt;&lt;/a&gt;
- this game looks great, and it's always interesting to hear from developers who have managed to get the PS3 to do something impressive.

Anyway, I should probably do some work now, see you there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-3070763095277688985?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.gdconf.com/conference/programming.html' title='GDC 2009'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/3070763095277688985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=3070763095277688985' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3070763095277688985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3070763095277688985'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/02/gdc-2009.html' title='GDC 2009'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-3924316541202034829</id><published>2009-01-30T07:19:00.000-08:00</published><updated>2009-01-30T07:42:10.143-08:00</updated><title type='text'>Interview with NVIDIA's Jensen Huang (and some books)</title><content type='html'>I just came across &lt;a href="http://venturebeat.com/2009/01/28/jen-hsun-huangs-quest-to-spread-graphics-beyond-pcs/"&gt;this&lt;/a&gt; entertaining interview with Jensen Huang. I particularly love this quote:

"&lt;span style="font-weight: bold;"&gt;Q&lt;/span&gt;: Jeffrey Katzenberg is giving effusive testimonials for Intel’s Larrabee graphics chip.

&lt;span style="font-weight: bold;"&gt;A&lt;/span&gt;: You and I don’t know what Larrabee can do. It doesn’t exist. If you pay for Katzenberg’s compute farm, he will tell you your lawnmower is great for making movies. "

The interview was done by Dean Takahashi, who also wrote the book "&lt;i&gt;&lt;span class="new"&gt;&lt;a href="http://www.amazon.com/Opening-Xbox-Microsofts-Entertainment-Revolution/dp/0761537082/ref=sr_1_3?ie=UTF8&amp;amp;s=books&amp;amp;qid=1233329321&amp;amp;sr=8-3"&gt;Opening the Xbox: Inside Microsoft's Plan to Unleash an Entertainment Revolution&lt;/a&gt;", &lt;/span&gt;&lt;/i&gt;&lt;span class="new"&gt;which I only recommend&lt;/span&gt; if you're really into games consoles and high-tech folklore.

While we're on the subject of nerd books, I also just read "&lt;a href="http://www.amazon.com/Race-New-Game-Machine-Playstation/dp/0806531010/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1233329385&amp;amp;sr=1-1"&gt;The Race for a New Game Machine:  &lt;/a&gt;&lt;span style="font-size:100%;"&gt;&lt;span id="btAsinTitle" style=""&gt;&lt;a href="http://www.amazon.com/Race-New-Game-Machine-Playstation/dp/0806531010/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1233329385&amp;amp;sr=1-1"&gt;Creating the Chips Inside the XBox 360 and the Playstation 3&lt;/a&gt;", which all about the IBM/Sony/Toshiba project to develop the Cell processor. It's no "&lt;a href="http://www.amazon.com/Soul-New-Machine-Tracy-Kidder/dp/0316491977/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1233329620&amp;amp;sr=1-1"&gt;The Soul of a New Machine&lt;/a&gt;" (which the authors credit in the intro), and it doesn't really cover the subsequent total lack of success of Cell outside the PS3, but it's somewhat interesting if you're into chip design. It also reads like their publishers tried to turn it into a management book half way through, which is a shame.

&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-3924316541202034829?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://venturebeat.com/2009/01/28/jen-hsun-huangs-quest-to-spread-graphics-beyond-pcs/' title='Interview with NVIDIA&apos;s Jensen Huang (and some books)'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/3924316541202034829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=3924316541202034829' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3924316541202034829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/3924316541202034829'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/01/interview-with-nvidias-jensen-huang.html' title='Interview with NVIDIA&apos;s Jensen Huang (and some books)'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-2987756915233645666</id><published>2009-01-15T04:02:00.000-08:00</published><updated>2009-01-30T07:59:20.691-08:00</updated><title type='text'>Visual Adrenaline</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XkozmCCjhTQ/SW8tfforlOI/AAAAAAAAAAk/kcuyKv28dho/s1600-h/qwrt_0012.png"&gt;&lt;img style="cursor: pointer; width: 320px; height: 180px;" src="http://2.bp.blogspot.com/_XkozmCCjhTQ/SW8tfforlOI/AAAAAAAAAAk/kcuyKv28dho/s320/qwrt_0012.png" alt="" id="BLOGGER_PHOTO_ID_5291498106274944226" border="0" /&gt;&lt;/a&gt;


I just love Intel's "&lt;a href="http://www.intelsoftwaregraphics.com/welcome.htm?siteid=30"&gt;Visual Adrenaline&lt;/a&gt;" magazine. It's such an unabashed marketing vehicle! This month there's a real treat in the form of a  article by Daniel Pohl on his new ray traced version of the game Quake Wars.

It was this paragraph at the beginning that really got my goat (my emphasis):

"Today’s games all use a rendering technique called rasterization. Rasterization requires &lt;span style="font-style: italic;"&gt;difficult programming work&lt;/span&gt; and as many special effects (such as shadows or reflections) need to be calculated as &lt;span style="font-style: italic;"&gt;approximations &lt;/span&gt;over multiple rendering passes and are often stored in resolution-limited textures in between." (sic)

Now, don't get me wrong, I love ray tracing, and I think that soon (like in the off-line rendering world) real-time graphics will probably converge to a hybrid model that uses rasterization with ray-tracing effects where they make sense.

But to claim that rasterization requires "difficult programming work" and is any more of an approximation than ray tracing is just misleading. Rendering is all an approximation, and real-time rendering is more approximate than most. Sure, it's easier to to do hard edged shadows using ray tracing, just shoot another ray, but start adding soft shadows and you might start wishing you had a shadow map to blur. And what about the "difficult programming work" and run-time cost of constructing the spatial data structures which are essential to make ray-tracing perform decently? Ray tracing advocates too often ignore this.

He then actually goes on to discuss in detail how badly ray tracing handles the alpha-tested trees in the game because of the thread divergence this causes.

He says:
"Another advantage of using a ray tracer for partially transparent objects is that they don’t need to be sorted by their depth"

Err, I believe the trees in Quake Wars are rendered using alpha-to-coverage, so no sorting is necessary. (Addendum: you could also argue that ray tracing is also essentially doing a sort by traversing a spatial data structure of the scene and returning the closest hit.)

The final icing on the cake is the image above, which is supposed to show ray-traced refraction, but as far as I can tell doesn't actually show the back faces of the dome and thus spectacularly fails to demonstrate an actual benefit of ray tracing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-2987756915233645666?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.intelsoftwaregraphics.com/welcome.htm?siteid=30' title='Visual Adrenaline'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/2987756915233645666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=2987756915233645666' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/2987756915233645666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/2987756915233645666'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/01/visual-adrenaline.html' title='Visual Adrenaline'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XkozmCCjhTQ/SW8tfforlOI/AAAAAAAAAAk/kcuyKv28dho/s72-c/qwrt_0012.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-1566951828028925131</id><published>2009-01-07T05:14:00.000-08:00</published><updated>2009-01-13T07:53:41.674-08:00</updated><title type='text'>Volumetric Particle Shadowing</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSrQRGMtuI/AAAAAAAAAAU/bH7XEvEr6lU/s1600-h/screenshot_lg.png"&gt;&lt;img style="cursor: pointer; width: 320px; height: 320px;" src="http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSrQRGMtuI/AAAAAAAAAAU/bH7XEvEr6lU/s320/screenshot_lg.png" alt="" id="BLOGGER_PHOTO_ID_5288540158395463394" border="0" /&gt;&lt;/a&gt;

This has been available for a while in the &lt;a href="http://www.nvidia.com/object/cuda_get.html"&gt;CUDA 2.1 SDK beta&lt;/a&gt; download, but since the PDF isn't on the web yet it hasn't had much exposure, so I thought I'd post something about it here. It's a very simple technique which is based on an old volume rendering trick by Joe Kniss,  but it can achieve some great looking results.

The basic idea is to that instead of sorting the particles in back-to-front or front-to-back order, you sort them along a vector half way between the view and light directions. You then render the particles in batches, accumulating the shadows to a 2D texture as you go. You can even add some blur to simulate scattering. Details are in the &lt;a href="http://sites.google.com/site/simesgreen/Home/smokeParticles.pdf"&gt;PDF&lt;/a&gt; (fixed link).

Oh, and there's a short movie &lt;a href="http://www.youtube.com/watch?v=xh2q_p6hQEo"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-1566951828028925131?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/1566951828028925131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=1566951828028925131' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1566951828028925131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/1566951828028925131'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/01/volumetric-particle-shadowing.html' title='Volumetric Particle Shadowing'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSrQRGMtuI/AAAAAAAAAAU/bH7XEvEr6lU/s72-c/screenshot_lg.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-7944764606210233943</id><published>2009-01-07T05:05:00.000-08:00</published><updated>2009-01-07T05:51:01.911-08:00</updated><title type='text'>Screen Space Fluid Rendering with Curvature Flow</title><content type='html'>Our paper "Screen Space Fluid Rendering with Curvature Flow" is now available to download &lt;a href="http://www.cs.rug.nl/%7Ewladimir/fluidcurvature.pdf"&gt;here&lt;/a&gt;.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSppDQFtUI/AAAAAAAAAAM/xOJVB8iHWiE/s1600-h/wpipe_curvmin_halfres.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSppDQFtUI/AAAAAAAAAAM/xOJVB8iHWiE/s320/wpipe_curvmin_halfres.jpg" alt="" id="BLOGGER_PHOTO_ID_5288538385152324930" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-7944764606210233943?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/7944764606210233943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=7944764606210233943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7944764606210233943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/7944764606210233943'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/01/our-paper-screen-space-fluid-rendering.html' title='Screen Space Fluid Rendering with Curvature Flow'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_XkozmCCjhTQ/SWSppDQFtUI/AAAAAAAAAAM/xOJVB8iHWiE/s72-c/wpipe_curvmin_halfres.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-4163583327465352831</id><published>2009-01-07T05:01:00.000-08:00</published><updated>2009-01-07T05:04:38.180-08:00</updated><title type='text'>I3D 2009 papers online</title><content type='html'>The always reliable Ke-Sen Huang has posted a list of the available papers from I3D 2009, although quite a few are missing at this point.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-4163583327465352831?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://kesen.huang.googlepages.com/i3d2009Papers.htm' title='I3D 2009 papers online'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/4163583327465352831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=4163583327465352831' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4163583327465352831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/4163583327465352831'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2009/01/i3d-2009-papers-online.html' title='I3D 2009 papers online'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6819369948550238004.post-2508632092515350406</id><published>2007-05-18T05:46:00.000-07:00</published><updated>2007-05-18T06:03:08.595-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='siggraph graphics'/><title type='text'>Siggraph 2007 papers available</title><content type='html'>Most of this year's Siggraph papers are online now, why actually go to the conference? I particularly like the curl noise one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6819369948550238004-2508632092515350406?l=industrialarithmetic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.cs.brown.edu/~tor/sig2007.html' title='Siggraph 2007 papers available'/><link rel='replies' type='application/atom+xml' href='http://industrialarithmetic.blogspot.com/feeds/2508632092515350406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6819369948550238004&amp;postID=2508632092515350406' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/2508632092515350406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6819369948550238004/posts/default/2508632092515350406'/><link rel='alternate' type='text/html' href='http://industrialarithmetic.blogspot.com/2007/05/siggraph-2007-papers-available.html' title='Siggraph 2007 papers available'/><author><name>Simon Green</name><uri>http://www.blogger.com/profile/14994361307494791932</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
