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.