Category Archives: 3D

Pathtracer Research

I spent awhile a few months ago learning about Pathtracers… After I good deal of research I ended up forking Evan Wallace’s Path Tracer and added a few new features and shapes - some of which I learned from Erich Loftis’s Three.js PathTracing Renderer.

view demo 1

view demo 2

Been wanting to get back to this and do optimizations and boolean shapes - but so far I haven’t gotten around to it.

Also posted in Graphics, glsl, graphics algorithms, javascript | Leave a comment

CSS Fake Lighting With Gradients and Shadows

Awhile back I thought it would be interesting to add some quick fake lighting to a personal project of mine - that for lack of a better description is a windows management system.

Here is a screenshot of the windows management system with lighting turned on:

Here is a video of me using the system:

I whipped up this prototype (don’t mind the jQuery)

There are really two keys that make this work. Getting the shadow in place and adjusting the gradient. All we really need is the angle and distance from a given `div` in relation to the “light”:

1
2
3
4
5
6
7
8
9
10
11
12
13
let calcAng = function(x, y) {
  let lightPos = light.position()
  let dx = lightPos.left - x;
  let dy = lightPos.top - y;
  return -Math.atan2(dy, dx) / Math.PI  * 180;
};
 
let calcDist = function(x, y) {
  let lightPos = light.position()
  let dx = lightPos.left - x;
  let dy = lightPos.top - y;
  return Math.sqrt(dx * dx,  dy * dy);
};

Standard `atan2` and the pythagorean theorem get us this. Once we have those - we can use them to set our gradient and shadow values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// warning (apparently this function is slightly speed coded)
let calcShade = function(x, y) {
  let angle = calcAng(x, y);
  let dist =  calcDist(x, y);
  let sx = dist * Math.cos(-angle * Math.PI / 180) * -1;
  let sy = dist * Math.sin(-angle * Math.PI / 180) * -1;
 
  sx = Math.min(20, Math.max(sx, -20));
  sy = Math.min(20, Math.max(sy, -20));
  let blur = Math.min(100, dist);
  let hBlur = Math.min(50, blur) * 0.5;
  // consider distance in the eq?
  return {
    bg: `-webkit-linear-gradient(${angle}deg, rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.4) ${blur}%)`,
    shadow: `${sx}px ${sy}px ${hBlur}px rgba(0, 0, 0, 0.15)`
  };
};

There are more videos of the windows management system on my youtube channel. Here’s another from a much earlier version of the system.

Maybe I’ll post more about that in the future…

Also posted in Graphics, Math, graphics algorithms, html5, javascript, misc, motion | Leave a comment

3d Point to 2d Point (Easy Mini 3d Engine)

Many years ago when I had just started programming I found this absolute gem by Andries Odendaal.

modern es6 version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let rotX = 0, rotY = 0,
    perspective = 500, 
    depth,
    currX, currY;
// learned something like this at Andries Odendaal's www.wireframe.co.za 
function point3d(x, y, z) {
    let cosX = Math.cos(rotX),
        cosY = Math.cos(rotY),
        sinX = Math.sin(rotX),
        sinY = Math.sin(rotY),
        posX, posY, posZ;
 
    posZ = z * cosX - x * sinX,
    posX = z * sinX + x * cosX,
    posY = y * cosY - posZ * sinY,
    posZ = y * sinY + posZ * cosY;
 
    depth = 1 / (posZ / perspective + 1);
    currX = posX * depth;
    currY = posY * depth;
 
    return [ currX, currY, depth ];
}

Here’s is an example of it in action:

I’ve used this code many many times, it’s just easy to throw into any language and instantly get 3d points rendered in 2d. Here is a short video of a Java applet from 2003 called “Catch Env” that made use of it:

Here is the source code for that ^^
http://www.zevanrosser.com/shape2/j/Catchenv.java

You’ll notice in that source, that I nested the equation to allow for local and global transformations. It was around that time that I learned the ins and outs of real 2D and 3D matrix transformation math… Ken Perlin’s classfiles from NYU were a real help when I was learning that stuff. I don’t think this was the exact file I was working with, but it was definitely on his site somewhere.

Before all that, during my junior year in college I created a 3d engine based off Odendaal’s code in Director (Lingo). Here is a video of some of the demos for it:

…and here is a strange screenshot from my personal website at the time:

Just an example of a powerful snippet and a gateway to learning matrix transformation math. When I first really dug in to html5 canvas - before WebGL was supported so widely - having this trick up my sleeve was great. As you can see in the below link, I used it more than a few times back then:

Daily canvas experiments:

http://zreference.com/projects/all-graphics.php

:D

Also posted in Graphics, graphics algorithms, html5, javascript | Tagged , , , | Leave a comment

Stereoscopic Papervision

Cross your eyes to see 3D third image:

move your mouse up and down on the interactive demo… click ->

Posted in 3D | Tagged | 1 Comment

3D Ring

Actionscript:
  1. [SWF(width = 500, height=500)]
  2. var ring:MovieClip = createRing();
  3. ring.x = stage.stageWidth / 2;
  4. ring.y = stage.stageHeight / 2;
  5. addChild(ring);
  6.  
  7. function createRing(sectionNum:int = 30):MovieClip{
  8.     var container:MovieClip = new MovieClip();
  9.     container.circles = [];
  10.     container.theta = 0;
  11.     container.thetaDest = 0;
  12.     var step:Number = (Math.PI * 2) / sectionNum;
  13.     for (var i:int = 0; i<sectionNum; i++){
  14.         var c:MovieClip = new MovieClip();
  15.         with (c.graphics){
  16.             lineStyle(0,0x000000);
  17.             beginFill(0xCCCCCC);
  18.             drawCircle(0,0,20);
  19.         }
  20.         c.thetaOffset = step * i;
  21.         container.addChild(c);
  22.         container.circles.push(c);
  23.     }
  24.     container.addEventListener(Event.ENTER_FRAME, onRun);
  25.     return container;
  26. }
  27. function onRun(evt:Event):void {
  28.     var container:MovieClip = MovieClip(evt.currentTarget);
  29.     var num:int = container.circles.length;
  30.     for (var i:int = 0; i<num; i++){
  31.         var c:MovieClip = container.circles[i];
  32.         var angle:Number = container.theta + c.thetaOffset;
  33.         c.x = 200 * Math.cos(angle);
  34.         c.y = 100 * Math.sin(angle);
  35.         c.scaleX = (100 + c.y) / 120 + 0.2;
  36.         c.scaleY = c.scaleX;
  37.     }
  38.     container.circles.sortOn("y", Array.NUMERIC);
  39.     for (i = 0; i<num; i++){
  40.         container.addChild(container.circles[i]);
  41.     }
  42.     if (container.mouseX <-100){
  43.         container.thetaDest -= 0.05;
  44.     }
  45.     if (container.mouseX> 100){
  46.         container.thetaDest += 0.05;
  47.     }
  48.     container.theta += (container.thetaDest  - container.theta) / 12;
  49.    
  50. }

This snippet shows how to create a 3D ring navigation using sine and cosine. Have a look:

Also posted in Graphics, MovieClip, UI, arrays, motion, sortOn | Tagged , , | 3 Comments

Nonsense Clocks

Actionscript:
  1. [SWF(width = 500, height=500, backgroundColor=0x000000)]
  2.  
  3. var clockNum:int = 100;
  4. var clocks:Vector.<Function> = new Vector.<Function>(clockNum, true);
  5.  
  6. var clockContainer:Sprite = Sprite(addChild(new Sprite()));
  7. clockContainer.x = stage.stageWidth / 2;
  8. clockContainer.y = stage.stageHeight / 2;
  9. buildClocks();
  10. runClocks();
  11.  
  12. function buildClocks():void{
  13.     for (var i:int = 0; i<clockNum; i++){
  14.         var theta:Number = Math.random() * Math.PI * 2;
  15.         var radius:Number = Math.random() * 200;
  16.         var xp:Number = radius * Math.cos(theta);
  17.         var yp:Number = radius * Math.sin(theta);
  18.         clocks[i] = makeClock(xp,yp,Math.random() * Math.PI * 2);
  19.     }
  20. }
  21. function runClocks():void{
  22.     addEventListener(Event.ENTER_FRAME, onRunClocks);
  23. }
  24. function onRunClocks(evt:Event):void{
  25.     for (var i:int = 0; i<clockNum; i++){
  26.         clocks[i]();
  27.     }
  28.     clockContainer.rotationX = clockContainer.mouseY / 30;
  29.     clockContainer.rotationY = -clockContainer.mouseX / 30;
  30. }
  31. function makeClock(x:Number, y:Number, time:Number=0):Function{
  32.     var radius:Number = Math.random() * 20 + 5;
  33.     var border:Number = radius * 0.2;
  34.     var smallRadius:Number = radius - radius * 0.3;
  35.    
  36.     var clock:Sprite = Sprite(clockContainer.addChild(new Sprite()));
  37.     clock.x = x;
  38.     clock.y = y;
  39.     clock.z = 100 - Math.random() * 200;
  40.     clock.rotationX = Math.random() * 40 - 20;
  41.     clock.rotationY = Math.random() * 40 - 20;
  42.     clock.rotationZ = Math.random() * 360;
  43.     return function():void{
  44.         with (clock.graphics){
  45.             clear();
  46.             lineStyle(1,0xFFFFFF);
  47.             drawCircle(0,0,radius + border);
  48.             var xp:Number = smallRadius * Math.cos(time/2);
  49.             var yp:Number = smallRadius * Math.sin(time/2);
  50.             moveTo(0,0);
  51.             lineTo(xp, yp);
  52.             xp = radius * Math.cos(time);
  53.             yp = radius * Math.sin(time);
  54.             moveTo(0,0);
  55.             lineTo(xp, yp);
  56.         }
  57.         time+=0.1;
  58.     }
  59. }


You can go check the swf out at wonderfl.net...

Also posted in Graphics, misc, motion | Tagged , , | 3 Comments

Fp10 3d Logo

Actionscript:
  1. var container:Sprite = new Sprite();
  2. container.x = stage.stageWidth / 2;
  3. container.y = stage.stageHeight / 2;
  4. addChild(container);
  5.  
  6. var redBox:Sprite = new Sprite();
  7. redBox.graphics.beginFill(0xFF0000);
  8. redBox.graphics.drawRect(-50,-250,100,500);
  9. redBox.rotationZ = 10;
  10. container.addChild(redBox);
  11.  
  12. var logos:Array = []
  13. var elements:Array = [];
  14. elements.push({element:redBox, z:0});
  15.  
  16. // add the logos
  17. for (var i:int = 0; i<6; i++){
  18.     var logoContainer:MovieClip = new MovieClip();
  19.     var logoText:TextField = new TextField();
  20.     logoText.defaultTextFormat = new TextFormat("_sans", 50);
  21.     logoText.text = "LOGO";
  22.     logoText.autoSize = "left";
  23.     logoText.selectable= false;
  24.    
  25.     logoText.x = -logoText.width / 2;
  26.     logoText.y = -logoText.height / 2;
  27.     logoContainer.addChild(logoText);
  28.     logoText.backgroundColor = 0xFFFFFF;
  29.    
  30.     container.addChild(logoContainer);
  31.     logos.push(logoContainer);
  32.     elements.push({element:logoContainer, z:0});
  33. }
  34.  
  35. var ang:Number = -Math.PI / 2;
  36. var rotationSpeed:Number = 0.05;
  37. addEventListener(Event.ENTER_FRAME, onLoop);
  38. function onLoop(evt:Event):void {
  39.    
  40.      var dx:Number = (mouseY - stage.stageHeight / 2) / 10;
  41.      var dy:Number = (mouseX - stage.stageWidth / 2) / 10;
  42.      container.rotationX += (dx - container.rotationX) / 4;
  43.      container.rotationY += (dy - container.rotationY) / 4;
  44.      
  45.      ang += rotationSpeed;
  46.      for (var i:int = 0; i<logos.length; i++){
  47.          var logo:Sprite = logos[i];
  48.          logo.x = 150 * Math.cos(ang + i);
  49.          logo.z = 150 * Math.sin(ang + i);
  50.          logo.alpha = 1 - logo.z / 200;
  51.          logo.rotationY = -Math.atan2(logo.z, logo.x)  / Math.PI * 180  - 90;
  52.      }
  53.      
  54.      // z-sort
  55.      for (i = 0; i<elements.length; i++){
  56.           elements[i].z = elements[i].element.transform.getRelativeMatrix3D(this).position.z;
  57.      }
  58.      
  59.     elements.sortOn("z", Array.NUMERIC | Array.DESCENDING);
  60.     for (i = 0; i<elements.length; i++) {
  61.         container.addChild(elements[i].element);
  62.     }
  63. }

A student of mine was having trouble creating a 3D logo for a client. I created this snippet to help explain how some of the fp10 3D stuff works.... z-sorting etc... The code could be optimized a bit... but it works nicely...


Have a look at the swf...

Also posted in misc, motion | Tagged , , , | 2 Comments

QuickBox2D 3D

Actionscript:
  1. import com.actionsnippet.qbox.*;
  2. [SWF(width = 800, height=600, backgroundColor=0x000000, frameRate = 60)]
  3.  
  4. var main:MovieClip = MovieClip(addChild(new MovieClip()));
  5. main.z = 500;
  6. main.rotationX = -40;
  7.  
  8. var sim:QuickBox2D = new QuickBox2D(main);
  9.  
  10. sim.createStageWalls({fillColor:0x1133CC});
  11. sim.setDefault({lineColor:0xFFFFFF, fillColor:0x113355});
  12.  
  13. for (var i:int = 0; i<30; i++){
  14.     var b:QuickObject = sim.addBox({x:Math.random()*10 + 3, y:Math.random()*10 + 3,
  15.                                     width:0.25 + Math.random()*2, height:0.25 + Math.random()*2});
  16. }
  17.  
  18. sim.start();
  19. sim.mouseDrag();

This demo shows how you can render your QuickBox2D simulation to somewhere other than the main timeline. In this case, I render to a MovieClip that has altered z and and rotationX properties.


Have a look at the swf...

Also posted in Box2D, QuickBox2D | Tagged , , , , , | 2 Comments

drawTriangles() Cubes

Actionscript:
  1. private function addCube(xp:Number, yp:Number, zp:Number, w:Number, h:Number, leng:Number):void{
  2.             var hw:Number = w * 0.5;
  3.             var hh:Number = h * 0.5;
  4.             var hl:Number = leng * 0.5;
  5.             var xA:Number = xp - hw;
  6.             var xB:Number = hw + xp;
  7.             var yA:Number = yp - hh;
  8.             var yB:Number = hh + yp;
  9.             var zA:Number = zp - hl;
  10.             var zB:Number = hl + zp;
  11.             _verts.push(xA, yA, zA,
  12.                         xB, yA, zA,
  13.                         xA, yB, zA,
  14.                         xB, yB, zA,
  15.                         xA, yA, zB,
  16.                         xB, yA, zB,
  17.                         xA, yB, zB,
  18.                         xB, yB, zB);
  19.            
  20.             var index:int = _boxIndex * 8;
  21.             var i0:int = index, i1:int = index + 1, i2:int = index + 2;
  22.             var i3:int = index + 3,  i4:int = index + 4, i5:int = index + 5;
  23.             var i6:int = index + 6, i7:int = index + 7;
  24.             _indices.push(i0, i1, i2,
  25.                           i1, i3, i2,
  26.                           i6, i7, i4,
  27.                           i7, i5, i4,
  28.                           i1, i5, i3,
  29.                           i7, i3, i5,
  30.                           i4, i5, i0,
  31.                           i1, i0, i5,
  32.                           i2, i6, i0,
  33.                           i0, i6, i4,
  34.                           i2, i3, i6,
  35.                           i3, i7, i6);
  36.                          
  37.             _faces.push(new Face(), new Face(), new Face(),
  38.                         new Face(),  new Face(), new Face(),
  39.                         new Face(), new Face(), new Face(),
  40.                         new Face(), new Face(), new Face());
  41.             _uvts.push(Math.random(), Math.random(), 0,
  42.                        Math.random(), Math.random(), 0,
  43.                        Math.random(), Math.random(), 0,
  44.                        Math.random(), Math.random(), 0,
  45.                        Math.random(), Math.random(), 0,
  46.                        Math.random(), Math.random(), 0,
  47.                        Math.random(), Math.random(), 0,
  48.                        Math.random(), Math.random(), 0);
  49.             _boxIndex++;
  50.         }

Lately I've been posting large code snippets... so today I'm highlighting part of a larger snippet - The above code is the heart of a small experiment I created this morning. It sets up a cube for use with drawTraingles().

The rest of the code can be read here:
Cubes3D.as

Have a look at the swf here...


I also put it on wonderfl...

Also posted in Graphics, Vector, matrix, motion | Tagged , , | 6 Comments

Distance Spring

Actionscript:
  1. [SWF(backgroundColor=0x222222, width=500, height=500)]
  2. var hsw:Number = stage.stageWidth / 2;
  3. var hsh:Number = stage.stageHeight / 2;
  4. var pointNum:int = 800
  5. var points3D:Vector.<Number> = new Vector.<Number>();
  6. var points3Dout:Vector.<Number> = new Vector.<Number>();
  7. var points2D:Vector.<Number> = new Vector.<Number>();
  8. var uvts:Vector.<Number> = new Vector.<Number>();
  9. var sorted:Array = [];
  10. var pnt:Point = new Point();
  11. var m:Matrix3D = new Matrix3D();
  12. var v:Vector3D = new Vector3D();
  13. for (var i:int = 0; i<pointNum; i++){
  14.     v.y = i * 0.7 - 300
  15.     var t =Math.random()*6.28;
  16.     v.x = 200 * Math.cos(i * 2 * Math.PI / 180);
  17.     v.z = 200 * Math.sin(i * 2 * Math.PI / 180);
  18.     v = m.transformVector(v);
  19.     points3D.push(v.x, v.y, v.z);
  20.     points2D.push(0,0);
  21.     uvts.push(0,0,0);
  22.     sorted.push(new Vector3D());
  23. }
  24. points3D.fixed = true;
  25. points2D.fixed = true;
  26. uvts.fixed = true;
  27. var p:PerspectiveProjection = new PerspectiveProjection();
  28. var proj:Matrix3D = p.toMatrix3D();
  29. var dx:Number = 0, dy:Number = 0;
  30. addEventListener(Event.ENTER_FRAME, onLoop);
  31. function onLoop(evt:Event):void {
  32.     var i:int, j:int;
  33.     dx += (mouseX - dx) / 4;
  34.     dy += (mouseY - dy) / 4;
  35.     m.identity();
  36.     m.appendRotation(getTimer() / 4, Vector3D.Y_AXIS);
  37.     m.transformVectors(points3D, points3Dout);
  38.    
  39.     m.identity();
  40.     m.appendRotation(dx, Vector3D.Z_AXIS);
  41.     m.appendRotation(dy, Vector3D.X_AXIS);
  42.     m.appendTranslation(0, 0, 1000);
  43.     m.append(proj);
  44.     Utils3D.projectVectors(m, points3Dout, points2D, uvts);
  45.     for (i = 0, j = 0; i<points2D.length; i+=2, j++){
  46.         sorted[j].x = points2D[i] + hsw;
  47.         sorted[j].y = points2D[i + 1] + hsh;
  48.         sorted[j].z = uvts[j * 3 + 2];
  49.     }
  50.     sorted.sortOn("z", Array.NUMERIC);
  51.     graphics.clear();
  52.     for(i = 0; i<sorted.length; i++){
  53.         var zpos:Number = sorted[i].z * 10000;
  54.         var c:int = zpos * 14;
  55.         graphics.beginFill(c <<16 | c <<8 | c);
  56.         graphics.drawCircle(sorted[i].x, sorted[i].y, zpos);
  57.         graphics.endFill();
  58.     }
  59. }

A variation on some recent posts... this snippet draws a rotating spring made up of 800 particles...

Have a look at the swf...


Also posted in Graphics, Vector | Tagged , , | 3 Comments