Tails, You Win – 3D Trails In Papervision


2009-02-16_2218

I’m not really sure where the inspiration came from, but I decided to take a stab at doing Motion Trails with Pv3D.  Instead, I ended up with something more like a tail – but left the class name MotionTrail… Anyways – there isn’t a whole lot to say on the technique I used here – but I will give a general overview just to save you looking at code.  See the Demo.

The tail is defined by Number3D objects which “chase” each other, with the head node being pinned to the target you are chasing.  Each Number3D, which I’ve extended into a Trail3D class (its internal – just look in the MotionTrail.as file) – springs after the node ahead of it.  You can control this “springyness” by adjusting the minEase property of the MotionTrail.  The minEase refers to the ease amount of the very last node on the tail – which increases each node up to the head, which has an ease of 1.  So, the lower you set minEase, the slinkier the tail will end up looking.  You can control how many nodes are in the tail by adjusting the “iterations” parameter of the MotionTail class.  You need at least 2.

The tail is actually a triangle strip, which progressively gets smaller the closer to the end it gets.  This 1) lets you use any material you want, and 2)easily adjust the look and feel of the tail all you want. Feel free to override it to affect the width with speed, distance, or any other cool variables you want.  It looked good just tapering so I left it that way.   The triangle strips vertices are adjusted to their corresponding TrailNode, so they always face the camera.  This keeps the tail visible from any direction.  Again, you could change this if you wanted it to be a more obvious flat tail.  My initial idea was to use the delta properties of the adjacent nodes to determine the nodes up vector (find the cross product between the dx/dy/dz values of the nodes on either side of the current node) – but again – I liked how this looks so I left it.

The demo itself just makes use of some bezier curves to keep things moving smoothly.  You will notice the nodes are flocking – but really it is just setting the control point and end point of each node within a certain range – which changes positions when the first object arrives.  You can press “F” to toggle it so that each node has its own unique path.

Anyways – here is the code,

and another look at the demo

enjoy.



Pixel Bender – The End Of Papervision?


Ha!  Honestly, the title was just to get your attention.  But to answer your next question, no, there isn’t a single line of Papervision in this demo (requires Flash Player 10).  This is, in fact, my first real test with Pixel Bender (and integration with Flash).  I had written one or two a few months ago the day it was released, but this was something that I had hoped would change the way I do some things in Flash.  It was inspired, (and some projection stuff borrowed) by the raycasting work of mike welsh, who made this great raycasting shader, which handled reflections, etc.  While it runs great in the toolkit, it can’t work in Flash (but even if it did you would only get about 1 FPS).  What I hoped would be a fast and elegant solution for a client project turned out to simply be a cool little experiment.  Not much application in the real world I don’t think – at least not till Flash gets some GPU support for it.

So, what did I learn?

1. Normalize doesn’t work in Flash – though it does in the toolkit.  I spent about 4 hours tracking down this nightmare.  My project worked great in the AIF Toolkit, but once I brought it over to Flash, the shading turned into a ghoulish wash.  Turns out that the Flash Player doesn’t read normalize.  Instead, you need to do something like this:

float3 vectorNorm = vector/ length(vector);

Or of course, you just do a /= to the original vector.

2.  No Debugging. This is always a struggle when making a shader.  The way you can debug is by simply outputting colors instead of debug statements.  I did end up making some very cool patterns in my filter by outputting various properties as color – but in the end it would be nice to have a simple trace statement.  For example, instead of doing:

out = sampleNearest(src, outCoord())

You can do

out = pixel4(norm.x, norm.y, norm.z, 1.0);

Then the image itself becomes a debug view of sorts.  Its a little less informative than a trace (though it does show trends well), but it helps tremendously when working with shaders.

3.  CPU != GPU.  Running in CPU mode flipped some normals on me.  It worked right in GPU and in Flash.  I read (somewhere) you should test in both to make sure it works all around.  Not sure if this means there will be problems on different computers or not…

4.  Pixel Bender isn’t nearly as fast as I wanted.  Sorry… its true.  It sucked going from the 800 FPS Toolkit to the 165% CPU destroyer (dual-core).  Turning the window mode to GPU took about 15% off the CPU, but it still wasn’t the performance I was hoping PIxel Bender would bring to the Flash Player.  I found the biggest thing that affected performance was simply how big the image was you were applying the filter too.  Apparently alot of math is faster in AS3 than in Pixel Bender (CPU).  I guess one solution would be to pass in more parameters with some of the math already taken care of.

Some Info About the Demo

I guess first thing to point out is that I use two input images.  src is first input image4, and the one that is automatically passed as an input to the shader.  This image4 is simply the object you are applying the shader too.  The second input, texture, is the texture we want to use for the mapping.  By using two images, we don’t have to process every pixel of the texture mapped, if we only want a 512×512 sphere.  In this demo the src image is only 350×350 – much better performance than running it on 1000×500.

Next, the performance sucks.  Well, to be fair, it runs okay – but it won’t run on older computers at all, and runs at max CPU on the newer ones.  I have tried some things to speed this up, such as taking the rotation matrix outside the filter and passing it in, but the change was negligible (I didn’t see a difference).  So, I opted to create the matrix in the filter per pixel and just keep the filter as self-contained as possible.  If anyone has some tips/tricks to make this filter run faster – let me know!

I’m happy with how it looks – sorry for some of the UV code in the shader itself – in order to remove some precision errors, I had to swap the side axis used for determining rotation depending on the location of the hit.  Makes things look better, though code looks a little worse.

Anyways:

Check out the Demo

and

Get the Source (once again, requires Flash CS4 and Flash Player 10)

Enjoy



Reflections in Papervision3D


reflect3.png

After posting my shadow experiment, Patrick Matte posed a question wondering if I would be able to do real-time reflections in a similar manner. The next day I had it done, along with some nice iterations along the way: orthographic and perspective projection (I can release those later if anyone really wants them). I've been sitting on it every since and finally decided I would take the time to write a little description into how its done and give the code to those who are interested (and I fixed up some code for backface culling in the reflection this morning).

So, the concept of shooting a ray onto a plane still holds true with the reflection stuff, but we handle the rays a little differently than we do shadows. With shadows we simply project the vertices directly onto the plane. This works because we don't care about the perspective distortion we will see on the plane face, since the shadow is black. A reflection is more than a projection. The reflected object is actually cast "inside" the material - so it maintains its own perspective despite the perspective of the plane you are projecting into. So, the problem was, how can i negate the perspective of the plane to make a real reflection?

The answer was pretty cool, and I am very happy with the result. Instead of projecting a vertex directly onto the plane, I multiply the vertex by a reflection matrix. The reflection matrix allows me to flip any object over any plane. Once i get the vertex in "reflected" space, I then cast a ray from my camera to the reflected vertex. Once I have this ray, I can find where it intersects the plane i want to show the reflection on - and then determine where on that material the ray hits. Since the material will be drawn from the camera's perspective, I can draw my reflected space from the camera's perspective as well - directly into the material. It's a little confusing, but it works awesome. Here is a little graphic I made to try and help explain:

reflectui.png

The red square is the object we want to reflect. The blue cube is the object in "reflected" space. The blue cube doesn't actually exist, mind you, all the vertices are flipped there via matrices. The thin red lines shooting from the camera are the rays, and the faint red box is the reflection drawn into the plane. Sorry its not a better graphic - I don't use Illustrator that much...

Anyways - thats about all there is to say about the theory behind it. Here is a brief rundown of the class:

Actionscript:
  1. //CREATE A NEW PlaneReflection
  2. // parameters:
  3. // - id: string
  4. // - blendMode : string
  5. // - alpha : Number (0-1)
  6. // - filters : array
  7. var reflector:PlaneReflection = new PlaneReflection("reflex", "multiply", 1, [new BlurFilter()]);
  8.  
  9. //ADD A MODEL TO THE "REFLECTION LIST"
  10. reflector.addModel(sphere);
  11.  
  12. //RENDER THE REFLECTION
  13. // paramaters:
  14. // - camera  : CameraObject3D - camera you are currently using
  15. // - plane : Plane - a plane to draw into - must have a MovieMaterial applied to it (animated = true);
  16. reflector.render(camera, plane);

That should be enough to get you started! Check out the demo, have fun with it. You will notice I used the planes own material as a displacement map for the reflection - that gives it the nice bumpmap feeling. Planes are still backwards (sorry) - so there is a special case to flip culling if you have a plane object.

View the demo (click and drag to orbit)

and

Get the source

[update]

Here is the updated source to work with the new core changes after Revision 754 (QuadTree support).  This also includes Refraction support - I didn't build it out terribly though since i'm too busy, but look at lines 94 and 95 to set what the refraction should look like.  Feel free to update the class to use member variables.

enjoy!

Get the Updated Source

[/update]



Great White, Meet Effects


demo1.jpg

So, after getting asked for the hundredth time the other day when I was going to integrate some of the features of the Effects Branch to Great White - I decided it was about time to do so. Things are settling in with my new job, and I've finished up my other obligations as well - so I put in the the time.

And now... They have been merged!!!

There are A LOT of changes though: creating layers, controlling layers, creating dynamic layers, sorting layers... its all different. Fortunately, I think for the better. I've comped up a set of VERY simple demos (all in one class) and will go over some of the new functionality here - just for you. Keep in mind this is still beta - so PLEASE report any bugs to me here or on the list.

--ViewportLayer has now replaced RenderLayer

Thats right - sorry. Forgot everything you ever knew about RenderLayers and RenderLayer syntax. Now - instead of layers being controlled by the objects - they are entirely controlled by the viewport. This means that you can create a layer for an object across multiple viewports.

Here's how you can create a new ViewportLayer:

Actionscript:
  1. var layer:ViewportLayer = viewport.getChildLayer(do3d, true);
  2. //now you can mess with your ViewportLayer all you want...
  3. layer.alpha = 0.5;

getChildLayer() is the function to receive or create a new ViewportLayer. The second parameter, createNew, specifies if a new ViewportLayer should be created if one is not found for the specified DisplayObject3D. If you set createNew to false- getChildLayer will simply return the ViewportLayer for the DO3D if it exists, otherwise it will return null.

Note that your DO3D is now going to be rendered to the layer created in getChildLayer(). If you want to add other DO3Ds to this layer, you can do so by using the following function of ViewportLayer:

Actionscript:
  1. //add a new DO3D to the layer created above.
  2. layer.addDisplayObject3D(new Sphere(new ColorMaterial()));

Likewise, you can remove DO3Ds from rendering to a specific layer by using removeDisplayObject3D.

--Nesting

Something else cool about ViewportLayers is they can be nested. Each ViewportLayer handles the sorting of its own children. So lets say you want to use INDEX sort for a COLLADA object with a fair number of z-fighting children - but you want that COLLADA to be z-sorted with the rest of your world. Now, you can.

Check out initNestingDemo() from the source. You will notice that we can create an empty ViewportLayer and add it to the viewport.containerSprite (also a ViewportLayer). Then we can create children inside that layer that will be INDEX sorted. Finally we create an external layer that will be Z sorted:

Actionscript:
  1. //s, s2, and s3 are three spheres added to the scene.
  2.  
  3. //create an empty viewport layer and add it to the container
  4. var parentLayer:ViewportLayer = new ViewportLayer(viewport, null);
  5. viewport.containerSprite.addLayer(parentLayer);
  6. parentLayer.sortMode = ViewportLayerSortMode.INDEX_SORT;
  7.  
  8. //create new layers inside the empty parentLayer and set some layer indexes for them
  9. parentLayer.getChildLayer(s, true).layerIndex = 1;
  10. parentLayer.getChildLayer(s2, true).layerIndex = 2;
  11.  
  12. //add s3 to a different layer off the main container - this will be z-sorted with parentLayer (with two index sorted children)
  13. viewport.getChildLayer(s3, true);

--useOwnContainer

This property gives you the ability to tell Papervision to dynamically create a layer for your DisplayObject3D when it is rendered in the viewport. By setting DisplayObject3D.useOwnContainer to true, your object will be rendered with its own ViewportLayer. Moreover, you can assign properties to your DisplayObject3D that will be passed along to your generated Layer. These properties are:

  • filters
  • blendMode
  • alpha

By setting these properties, you can add any filter to the layer, change the blendMode of your Layer, and control the alpha of the layer. Keep in mind that you can change these properties at runtime, as a new layer is generated every render, and it will inherit the updated settings. Here is a simple snippet of using these new properties:

Actionscript:
  1. var p:Plane = new Plane(new ColorMaterial());
  2. p.useOwnContainer = true;
  3. p.filters = [new BlurFilter(int(Math.random()*16)+2, int(Math.random()*16)+2)];
  4. p.blendMode= BlendMode.ADD;
  5. p.alpha = Math.random()+0.1;

You can see that we are creating a new plane, setting it to create a unique container, and then set an Additive blend mode, have a random alpha, and a random blur. In my code demo you will see that i create a large number of planes and change their filter's blur based on screenDepth (distance from the camera). Be sure to play with it.

--Effects

Effects have changed somewhat for GreatWhite too - but not too much. You still have BitmapEffectLayer - but it now extends ViewportLayer. You simply need to create a BitmapEffectLayer, at it to the containerSprite, and add your DisplayObject3Ds using addDisplayObject3D:

Actionscript:
  1. //create a new BitmapEffectLayer
  2. var bfx:BitmapEffectLayer = new BitmapEffectLayer(viewport, 500, 500);
  3. viewport.containerSprite.addLayer(bfx);
  4.  
  5. var s:Sphere = new Sphere(new WireframeMaterial());
  6. scene.addChild(s);
  7.  
  8. //add the sphere to the layer
  9. bfx.addDisplayObject3D(s);
  10.  
  11. //add a blur effect to the layer
  12. bfx.addEffect(new BitmapLayerEffect(new BlurFilter(2, 2, 8)));

You can add effects to other ViewportLayers by simply using the filters property inherent to all Sprites.

--Selective Layer Rendering

One other thing that has been added is the ability to render only selected ViewportLayers. I added this with the following scenario in mind: You have a large room, but the camera doesn't move much. You don't want to have to create a new Viewport for the floor, walls, etc - but also don't want them rendered every frame. Well, now you can tell your renderer which layers you want updated, and which to ignore. You can see this in the source under initLayerRenderDemo().

So how do we do it? All we need to do is create an Array with the ViewportLayers we want rendered - then call renderer.renderLayers(). Thats it! Here's a quick look:

Actionscript:
  1. layersToRender = new Array();
  2. layersToRender.push(viewport.getChildLayer(s, true));
  3. viewport.getChildLayer(s2, true);
  4. viewport.getChildLayer(s3, true);
  5. //pass the layer for s only to be rendered
  6. renderer.renderLayers(scene, camera, viewport, layersToRender);

Please note, that at this time, interaction will be lost for objects that aren't rendered again. I'll look into fixing this down the line.

--A Few Final Notes

Those sum up some of the biggest changes - there is some other stuff that might popup - if you have any questions, or need help figuring out whats going on - just let me know. Here are a few final tidbits:

DisplayObject3D.container - this references the last ViewportLayer your DO3D was rendered too. It is null before your object is rendered for the first time.

DisplayObject3D.createViewportLayer() - This function is another way to create a ViewportLayer. The first parameter is the viewport you want the layer created in. The second parameter specifies if you should set this to be the layer for all children of the current DO3D.

ViewportLayer.forceDepth - set this to true to set how far into the Z order your layer is rendered. Say, you know your floor should always be sorted 4500 away from the camera - but other things can sort around that. set forceDepth = true, and screenDepth = 4500 - and you've got it.

ViewportLayer.layerIndex - I mentioned it before - but you set this property when you have your layer.sortMode == RenderLayerSortMode.INDEX_SORT. Higher numbers will be higher on the DisplayList.

And again - this is beta - things will probably change. But let me or the team know if you find any bugs!!!

Get the Source

Enjoy!



Lab – Pixel Particles 3D


particles3d.png

This is just a new experiment I made testing how well the pixel3d classes could stand up as a true particle system.  I was happy with the result.   Click to change particle stream color.

It grows 6,000 particles, has 2 fx layers, 3 total effects applied at 600x500 (no clipping).  I had it running with 6 gravity spheres floating around pushing and pulling the pixels, but all it did was lessen the effect (so I took them out).

See it here

(you can see gravity only stuff going on at a semi-done experiment here)

 [update]

I've uploaded a new experiment with particle color controlled by sound.  Check it out.

[/update]