Time for one more step in this snowy experiment. I can’t help feeling like I’m dropped of the wagon right now with all kick-ass 3D demos around for the upcoming flash 3D API molehill. Of course, it’s not just about the amount of triangles, but also the creativity and love put into those you got. Anyway, I hope that you will like this piece while we all waiting for the salvation. Just put a smooth-modifier in your mental stack.
To mention some of the additions to this version, I have added collada-animations, a background view, different camera-angles including the typical fish-eye lens seen in many snowboard-videos. But the main feature is that you now can control it and make nice bezier curves in the snow yourself. You have three different inputs to play with:
Steer with the arrow-keys. Press down-arrow to bend your knees and push the snow a little harder. With this input-method it’s simple to keep a continuous pace, but it feels a little static.
In my opinion a more dynamic way to get smoother turns and more control. Until you found your pace it can look a little funny, because the engine need a previous turn to calculate the leaning angle. Try to move the mouse back and fourth as an arc across the slope to get a balance of speed and pressure, simulating the g-force when surfing through the snow.
This is quite fun to play with. I like the idea of moving your own body to control the movement. The face-detection-algorithm steals some CPU, so it’s maybe not the best choice. Use the same technique as with the mouse. Y-axis controls the pressure. Calibrate yourself against the webcam preview image to find a good position.
Now, lets make some turns.
I don’t have a multi-touch trackpad, otherwise that would have been cool to control each foot with a finger, like a finger board. Or a wacom-board, with different levels of pressure.
Would be even cooler to control the board in 3D. Or maybe connect a wii-control to it?
To get a natural and correct looking turn, the character have to lean the body through to whole turn, almost before the turn even starting. So how do we know which phase of the turn we’re in? We could “record” the position and interploate and adjust it afterwards, like a bezier-drawing application. But I do not want this delay as it’s affect the response and feeling of riding in real-time. I came up with a different approach. I have two markers, xMin and xMax. Each time a the turn reach it’s maximum or minimum position the values are updated. I then have an estimated range of the next turn (if I assume that the next turn will be the same length). The current x-position is then compared with the estimate range and I got a normalized value between 0-1. Now I can ease that value with a Sine.easeInOut-function. That eased value is then used for the leaning angle. If you do a shorter or longer turn than expected it will of course look different, or before you find your pace, but it is still looking ok.
That’s all for now. Thanks for reading!
I have some improvements for you since last time! New stuff in this version is terrain, a rider and some snow-particles. In this demo, try to combine different values to see how the rider and the snow is affected. All parameters is connected together, so you can get pretty cool results.
I mentioned that I could add effects to each layer in the heightmap/texture-comp. I now added perlin noise to the base-layer. The offset for each octave is the same as the track-layer-scroll so they are in sync with each other. By resizing the y-scale of the noise the terrain got more stretched look, like ridges, or dunes, created by the wind. It’s a simple example of distortion and more parameters and effects will be added to create different slopes. I also read out the height-value at the snowboarders position. Notice how the rider floats on top of the terrain minus the snow-depth and depending on the current pressure.
The guy is a fully rigged model imported from 3d studio max. I have just begun to test the bone-support in Away3D, that is way he is looking like a rookie. For now I just control joint rotations, but later on I will try to add bones-animations and poses, especially if I add jumps and tricks. I tried to move the joints up and down, but I could not sort that out with correct IK and without destroying the mesh. I’m sure that method would be pretty CPU-intensive as well with all the recursive translations going on in IK. I have to dig deeper into that later.
Just for the record, I’m in fact a skier, so maybe the movements are completely wrong since I never even tried a snowboard
I also added some snow-particles, or animated sprites to be more specific. The animation is dynamic depending on the speed and the pressure. If more pressure and the snow is deep, the particle will fly longer.
I guess next time I will try to make the rider controllable.
The winter is just around the corner. If you, as myself, enjoying alpine winter sports, this little toy will get you into the mood. Imagine that you have climbed all the way up the mountain, before the lifts starts feeding the mountain with people. You want to be sure that you get a ride on fresh untouched snow all the way down. You now standing on the top and watching the sun rise behind a nearby peak. Googles on. Gears on the back. It’s you and the mountain… I love that feeling!
This is the first part of this experiment of making a off-piste simulator/game in Away3D. The first step is to create the slope and generate tracks in the snow. Check it out the first test
I got inspired by @tonylukasavage and his morphing mesh experiments with HeightMapModifier in Away3D.
Some notes about the texture; I start of with a bitmap filled with the color-value 0×80000. That fill represents the center of the offset. I then draw the track to another BitmapData ranging from black to red. To make the scrolling effect I found a method that I’m never used before: BitmapData.scroll(x,y). The two layers is comped together to a single bitmap. One advantage of using layers is that I can add noise, shadows and other effects to each layer. The heightmap is then converted to a normalmap with NormalMapUtil and a texture with paletteMap. Now I got what I need to create a nice phong-shaded Pixelbender material. The grid has very few triangles (25×25 segments), I have to save some cpu for the character and the other stuff. I’m not doing any real-time triangulations either, so in some cases the vertices starts to jump around. I have to accept that for now.
I don’t really know the outcome of all the future parts yet, but next up is to add a character and some controls.
I realize all my posts so far has one thing in common. Perlin noise. So why make an exception this time. Remember the post about fire? Make the animation go in the opposite direction, change the colors and some parameters, put in on a texture and mix with blendmodes and filters and you got yourself another element of nature. Water.
When I play first-person-shooters like Just Cause, Far Cry or Uncharted 2 there is one thing that I always remember most. Water-effects. I forget about the story and just play around with it. I hope you will remember this one
The cliff and water is simple models created in 3D Studio Max, then imported as a scene into Away3D with the Loader3D. The materials are converted to PhongMaterials. I tried to use a normal-map and a pixelbender-material on the cliff to get a wet look, but it was to CPU-intense together with the other effects, so that had to go.
The trick to make the water refract the light was to render the view twice. One with the waterfall alone and with just black and red colors. And another pass with the final result. Then the first pass is used to create a displacementMapFilter on the view. It’s quite slow, but I couldn’t resist to activate it by default when the visual result is much better. Try to switch it off to see the difference in quality and speed.
Update: I strongly recommend using Flash player 10.1 with this one, it’s a huge difference in speed! And how come Chrome is so damn faster than the rest? I added a slider for the texture resolution as well, so that performance can be adjusted. Lame, I know…
I found the texture for the cliff over here.
Procedural graphics is surely my favorite. The concept of create visual things with just code. Really comes in handy when your a crappy designer and still want to do visual stuff. Here you can see the result of yet another experiment on this track. It’s a eye-shader for generating procedural eyes. The 3D-engine of choice is Away3d. I use a CompositeMaterial consisting a PhongMultiPassMaterial and a SphericEnvMapPBMaterial (for the environment reflections). The bitmap that is used in the material is generated by a haxe-swf that is loaded in runtime. Just as previous experiments. The same bitmap is used as a base for the spherical normalmap. That adds a nice displacement to the surface. The iris is quite simple and could have more layers and complexity to get a more realistic pattern. Anyway, the texture looks something like this.
When wrapping this on a sphere it will fit seamlessly . I know, the blood-vessels aren’t that great. Perhaps I should use lines with turbulence instead.
Design your own eye
Here is a tool where you can try the different settings and create a unique eye.
This “mars-attacks”-demo follows the mouse. To get two eyes I duplicates the output from the view. Thats why he can’t look at his nose (if existed). Notice that he is reacting to light, sort of…
Last time I made some wood. Now let’s burn that up. Still experimenting with the same 3D perlin noise as before, but this time animated. When you can take control over each pixel of noise it’s easy to add extra rules to form the shape of the noise such as turbulence based on y-value or an offset animation along the z-axis to gain some depth. Applying “ridges” to the perlin noise (invert all negative values when the range is between -1 to 1) makes the flames look more interesting. The bitmap generated is just 80×80 and 2 octaves of Perlin noise, so the possibilities are quite limited.
Click here or on the fireplace above to get warm.
If you want to see how it works, here is a zip with sources for a simple example. No graphics or UI is included though. The noise is compiled with Haxe, but a swf is included if you want to skip that.
See this post for additional credits.
My next track on my journey through procedural domains goes through the woods. Not so revolutionary maybe, but I haven’t seen it in flash before and that’s enough challenge for me. And it’s a good way to learn the basics of textures. Here is the first tests:
For the sphere, I use the spherical mapping from the previous post. The cube has one texture for each side. One benefit if using a 3D procedural texture is that the wrapping is accurate and it looks like a block of massive wood. I can also use the optimized 3D Perlin noise as turbulence to make the grains look more natural.
Simple tree texture designer
I have added a few parameters to change, but several features could be added like: more object types, ring-types (tri,saw, sine), base texture blending.
The images in the last post was created with this tool. I use the heightmap to create a normal map with a excellent class called SphericalDispToNormConverter. So since I now have a heightmap and a normal map, Away3D can do the rest. The material used in this example is the PhongMultiPassMaterial. When “displacement map” is disabled a simple BitmapMaterial is used. The @away3D -team has really done a great job with the pixelbender materials.
Among the settings you can choose between a regular perlin noise or a ridged one. You will notice that the difference is pretty big. Also, check out the different presets.
The resolution on the heightmap is 400×200, but scaled down in the UI. (By the way I can’t tell you enough how I love the minimalcomps by mr @bit101.) The heightmap-generation is pretty heavy on the CPU, so choose browser with care and stay away from debug-players
What do you think? Time for a generative material-library for Away3D?
I’m not quite done experimenting with 3d perlin noise. Another cool feature that I haven’t seen in flash is to wrap the noise on a sphere seamlessly. Here is the result:
Click the spheres for a slightly larger version
To get the texture to wrap seamless without distortion, we can use the 3d nature of the noise in a interesting way. If we evaluating the 3d-position of each point on the surface of a sphere we can get the noise in that particular point. To convert the point from 3d-space to 2d-space some trigonometry is needed. My math-skills really suck, but my cut-n-paste skills are excellent. Once again, LibNoise showed me the way with 5 lines of code. First I only got chaotic noise with some sort of repeating pattern. It took some time to figure out that I had to convert the numbers to a positive range.
lat = py / height * 180- 90;
lon = px / width * 360-180;
r = Math.cos(DEG_TO_RAD * lat);
//range between 0-1
_x = (r * Math.cos (DEG_TO_RAD * lon) +1)*.5
_y = (Math.sin (DEG_TO_RAD * lat)+1)*.5
_z = (r * Math.sin(DEG_TO_RAD * lon)+1)*.5
For the sphere and the material/lighting I use Away3d. In the next post I will show you more about that and the tool for creating the different materials. Oh, and it’s looks a lot better animated
It’s finally time to render a landscape with all the different parts that I have covered so far.
I’m really curious of your output and render-time, so please upload it to the gallery when you created your own landscape. The gallery will be published as soon as I got a collection of images.
This tool is in a very experimental phase and this applications using pretty much memory and CPU-power. There is plenty of memory-leaks and optimization that has to be fixed. But for now, I don’t want to focus on that. Some calculations freezing the player for a while. But I think that the tolerance-level is higher if the user can see the progress both with a bar and status-notifications. Notice the effect when drawing the geometry above sea-level. It’s completely cosmetic and waste of CPU but makes it looks more advanced
I have also added the possibility to do some adjustments on the result. For now you can change the colorMatrix (brightness, contrast, hue and saturation) and add some sharpening.
Save / upload
If you have a flickr account, you can save it to your own photostream. Or upload to the public gallery, also using flickr, but a account dedicated to FlashTerrain. Or of course, encode the image to a jpg and save it to your desktop.
Credits and useful links so far
AS3Dmod library: http://code.google.com/p/as3dmod/
Gradient color component: http://mariosh.com/components/gradient-editor/
Advanced color picker: http://www.sephiroth.it/file_detail.php?id=150#
Multipart URLLoader Class by Eugene Zatepyakin (in-spirit): http://code.google.com/p/in-spirit/wiki/MultipartURLLoader
Flickr authorization control: http://www.mikechambers.com/blog/2008/08/12/flex-based-flickr-api-authorization-control/
Next step is to distribute the tree we just created on to the terrain. I use the heightmap as the source. I create an array with objects representing all pixels. The reason for this is that I now can filter and sort these points. First, filter out all points in the defined range, based on the y-value and the range defined in the editor. Then, sorting out a specified amount of random points, sort them on the z-value for correct render order. Before this last sorting I can add all different objects to the same batch. I’m sure there are much better and faster methods than this. Analyzing the terrain for more realistic distribution should make it more interesting. When rendering, I have to make a render for each position and merge the result with the final image. It will not work to just paste the result on top of the terrain. The tree is sometimes behind the terrain and partly hidden. And if the terrain is included in each pass, next render will cover the previous. So after some experimenting with masks, pixel bender matte calculation, my solution was this:
- Render the object by itself in a transparent viewport.
- Add the terrain and set the material to a white color material and viewport background color to white. Move the object to a new position and render.
- Get the boundingbox of all none white pixels with getColorBoundsRect. This is the rectangle that we want to work with. Draw a new BitmapData with this rectangle from the viewport. Lets call it maskTree, the object with white surroundings.
- Crop the transparent pass with the same clipping-rectangle as maskTree. Lets call the result transparentTree.
- Add effects like fog and filters like blur to transparentTree.
- If the tree got covered by any terrain, the edges now has straight cuts because we just use a rectangle clipping. We have to fix that. Loop through all pixels in the maskTree . If its completely white in the mask, its a transparent pixel in the transparent image, so set that pixel transparent in transparentTree. Left is a masked image, but with correct multiplied alpha colors.
- Merge transparentTree with the final result. To do this a put the final canvas and the new object in sprites and cache them with draw() to a new final canvas
- Send an event so the user can follow the progress and start over with next iteration.
I tried to use a matte pixelbender-filter so that I just had to render the viewport once, but the alpha-channel in flash is pre-multiplied (correct me if I’m wrong here), so the edges became white when blending it with the final canvas. One downside is that it’s pretty slow. Another is the z-sorting issues that occurs. Maybe a down-sampled heightmap can be used instead when setting the position in 3d space.
Shadows and effects
Because the result of each tree is a single transparent bitmap, I can use different effects on each tree. A blur-filter based on the distance to the camera is added. A small random value on the hue/saturation make the trees more random. If fog is enabled a color tint is added. I didn’t find out how to change the color-transformation correctly , so I found a workaround by setting the tint with TweenMax and change the progress to 0-1 depending on the z-position and fog amount. Have to work more on this later on.
I mentioned the shadow-layers in one of the previous posts. To add every shadow to the final result, I make a temporary container and add a sprite with a radial gradient fill for every tree and cache that with bitmapData.draw(). This is then baked into the texture with the multiply blendmode.
Things to improve:
- There is plenty of optimization here to do here. This is the most time-consuming part.
- Check visibility against the clipping planes and skip those objects.
- Adjust all the effects with better z-depth-control.
- Let the position of the sun affect the brightness.
- More realistic distribution such as slope detection or algorithms.
This part was the reason I started all this. It’s thanks to the work of Den Ivanov and an experiment of his: http://www.cleoag.ru/labs/flex/plant3d/. To create 3d-objects manually in papervision3D is a little bit to advanced for me, so thanks for sharing once again. I added some features and made it possible to customize it. Here is some samples:
I used 3d studio max to create the different leafs. You can adjust the leaf with various settings like width, height and bend-modifier ( credits to Bartek Drozdz, AS3mod is great!). It would be cool to create leaf manually by drawing a outline, adding vertices, triangulate and upload textures in runtime. But I have to experiment with triangulation and uv-mapping first. The rules for generating the tree is stored in a 2d-array like this:
_branchMap = [
[0, 0, 2, 2, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ]
This trunk uses 2 levels of recursive depths. One trunk and two levels of branches. Each array decide in which joint a new branch is growing while we iterate through the segments. The stored value is how many new branches there will be at that joint. The length of each array reflects how many segments there will be in that level. Oops, I got carried away with boring details… I’m not even pleased with this solution. I think I have to use a completely different approach. A L-system maybe? But there is a lot of things that I want to first, so I put it on hold for now. Still, you can get a lot of different trees by adjusting the few settings available.
There is pretty much research around, for example:
I quickly put together a standalone tool so you can try to change the settings available. Some parameters in the building process has random values (like branch-rotation), so when changing the branch and trunk settings the tree will be different every time.
- Leaf editor.
- New tree generation algorithm.
- Different tree-characteristics depending of the value of the normalmap when distributing on the mesh.
- More types of vegetation like grass, bushes.
Have you seen the previous posts about creating terrain with flash? Check them out here: http://www.inear.se/?cat=6
Okay, lets add some water. This time an ocean.
One way would be to set set each vertex under a certain level to the same y-value, but I wanted to control the geometry under the surface as well. The SplitMesh util in papervision solved that for me. First I split the mesh and then adding another plane in that intersection. It was a little tricky to get the z-sorting right when previewing all parts together. One problem was to assign a viewport layer to the new objects. The triangles had different parents so some triangles got hidden. One solution was to clone the object and it’s material, but that was cpu- and memory-consuming in my case. Instead, I modified the splitMesh-method to change the parent of those triangles that where cut. The split-util is quite slow for large meshes, so it freezes the player for a while. Unfortunately I’m not an expert on splitting for-loops into chunks, so I have to live with that for now. Added a waiting-screen instead.
As with the terrain, the sea texture is a composition of several planes and materials put in different viewport layers for z-sorting and blend mode effects. The first plane contains the diffuse color. You can also use a reflection of the sky instead of the color value (this makes the sun reflect in the surface ). With BitmapData.draw() I create a bitmap which in turn get the alpha channel from a transparency map, in this case a gradient fill where we set the alpha (water transparency) and range (z-depth). The terrain under sea level becomes visible, and the cut become less sharp. The details below sea level is so flat and small, so I don’t bother using a displacement map to simulate light refracting through water.
The waves is generated by, can you guess? Perlin Noise. Just as with the clouds a bevel-filter adds a kind of volume to it.
Geometry above sea level is also cloned with inverted vertices. Then a displacement-map based on perlin noise is added on the viewport. There you got some nice reflections.
- More realistic bump map.
- Sun reflections in waves (glitter). I tried a similar lightmap as the terrain uses but I can’t make the tiling correct.
- Foam ( along beach lines it would be easy to use the heightmap and a modify the paletteMap with transparent and white colors ) to create white waves around it.