🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Rendering Tree Sprites

Published February 02, 2016
Advertisement

It's been awhile since I did anything 2D-related, and I'm a little bit worried that some of those particular hard-earned skills are starting to fade from my weak brain. Last night, I was talking with an individual in the GD.net chat about trees. This person was drawing trees and looking for advice on how to make them, so I shared a little bit about my own personal workflow for 2D trees. I've talked about it somewhat before, but in the interest of archiving one little bit of digital knowledge that I risk losing, I'd like to take the time to talk about it a bit more in-depth.

I first started making trees many years ago, for the original Golem isometric RPG I was working on at the time. In the course of my research at the time, I stumbled across a Python script for Blender that implemented an L-system for generating trees. The script is now long-defunct, being non-functional for anything beyond Blender v2.49. The site for the script is no longer functional. About the only thing that I can find for it anymore is the archive at archive.org, and reading the comments in the sidebar about how progress on the next build is being made and should be released soon is kind of depressing. It's like a reflection of my own life. At any rate, you can see the site for an earlier version still in existence. But the sad fact is, the script is dead. It did serve as a nice introduction to the concept of L-system tree generation, however, and I do keep a copy of it (along with a zip of Blender 2.49b so I can run it). But in the modern world, there are other alternatives.

One of the chief alternatives is ngPlant, an open-source L-system generator. It is still under development, and being a stand-alone tool rather than a script built in Blender itself, it is not tied to any particular Blender version. Using ngPlant, you create a tree then export it to meshes for import to Blender. ngPlant allows the loading and saving of parameter templates, and there is a library site called 3D Plants Collection where .NGP files for ngPlant can be found that implement generators for various real-world plant and tree varieties. The site is somewhat related to the older Blender Greenhouse site.

The ngPlant generator is pretty neat. When you first start the program, you begin with a "tree" that consists of a single branch layer, forming a conical trunk. You can add additional branch layers in a hierarchy, with each successive layer of branches "growing" from the parent. At a certain depth, you convert a layer of branches to leaves. Some variants use multiple leaf layers, with different leaf texture selections for each layer. Trees with leaves and flowers, for example. This journal entry is not intended to be a tutorial for how to use ngPlant. If you are interested, I highly recommend downloading it and playing with some of the templates found at the 3D Plants Collection site to see how the various settings and sliders can be used to generate interesting vegetation forms. It's a fun toy to play with in its own right.

i7fJaKy.png

Once you've tweaked the sliders and made some cool stuff, you can export. I typically export to Alias-Wavefront .OBJ format, simply because Blender has no issues importing in that format. The exporter gives you a few options that can help to optimize your export. For example, full-bodied tree canopies come as a result of having fairly deeply-nested branch levels. By going 3 or 4 levels deep on the nesting, you can get a thick, nicely-formed tree canopy, but the cost is having 3 or 4 levels of branches. As you get deeper in the branch hierarchy, the branches become smaller and more numerous, until you get to the level of twigs. Sadly, exporting all of these layers isn't really optimal. The smaller twig layers contribute a huge amount of geometry, but only a small amount toward the final render's visual appearance. Many of those twigs won't even be visible at all. So ngPlant makes it possible to mark a branch layer as Hidden, and to choose not to export Hidden layers. This way, you can hide the final twig layers so they won't be exported. Exporting will also copy the bark and leaf textures you assign in ngPlant to the folder you select as destination. The templates at the 3D Plants Collection site come with some decent textures, but I highly recommend spending some time with a digital camera and the Gimp, creating a library of your own leaf and bark textures. It gives you an excuse to go out into the hills or woodlands on nice day. While you're out there, take a few pictures of whole trees as well. Having reference is a big help when you are tweaking ngPlant templates to fit what you see.

So, you've built a cool tree in ngPlant and exported it to .OBJ. Time to open up Blender, and import. Now, before you go all crazy with generating trees and trying to render them, it is important that you settle on a consistent lighting scheme so that the are all visually compatible with one another. For this purpose, I will usually create a lighting and rendering rig, setting up the lights and the camera just right, then saving that Blender file and using it as a template when importing trees.

Creating the Lighting Rig

The first thing I do is fire up Blender and delete the default cube and camera. Deleting the camera isn't strictly necessary (it's easy enough to reset it's parameters) but it's just a habit I have gotten myself into. I will also usually delete the default point lamp. I want to start with a nice, empty scene. Once I'm there, I create a new camera. The new camera will be created at the origin (assuming you haven't moved the 3D cursor, anyway) pointing downward along the Z axis. Select the camera, press 'r' to rotate, press 'x' to constrain it to rotate around the X axis, and type 90 then enter to rotate it by 90 degrees. Now it will be pointing along the Y axis. Press 'g' to grab it, type 'y' to constrain it to the Y axis, and type, say, -10 to move it 10 units back. Now it will be located away from the origin, pointed back toward the origin.

Now, at the bottom of the render view is a drop-down box:

9eOcOCR.png

This box can be used to select the pivot location for rotation operations. On startup, it is usually set to Median, meaning that rotation operations will pivot around the median center point of the selected object(s). If you choose 3D Cursor instead, then the rotation will be performed around the location of the 3D cursor, which should still be set at the origin. Once changed to 3D Cursor, select the camera again, press 'r' to rotate, 'x' to constrain around X axis, then type -30 and Enter. This rotates the camera up around the origin to a location 30 degrees above the horizon, which is suitable for isometric games using the 2:1 tile ratio that so many games use. Of course, the angle you set here needs to reflect your actual in-game angle.

If I am going to be using the same lighting rig to pre-render landscape tiles and graphics for an isometric game, then at this point I will select the camera, press 'r' to rotate, press 'z' to constrain to the Z axis, and type 45 then enter to rotate the camera around the Z-axis. While not strictly necessary, this step can help in getting the lighting to be consistent between landscape tiles and graphics such as trees.

Once the camera is in place, I go to the Camera options menu:

0nOe1mV.png

There, I change the camera to Orthographic, and change the Orthographic Scale to something reasonable, like 2. I typically export my trees scaled to 1 or 2 units high, since I like to work at smaller scales.

In order to assist in lighting, I typically create a ground plane at the origin. I create a plane, scale it by, say, 4. This will help me when choosing the shadow angle. It will also come in handy later when rendering shadows.

Next, it's time to set up the lights. When rendering isometric graphics like this, I almost always stick to using only Sun lamps. They provide directional lighting, which is necessary when illuminating tile objects that need to tile with one another. In order to keep the lighting consistent between tiles and objects, it is necessary to use the same Sun lamp setup for the trees. So, I'll go ahead and create an initial sun lamp.

Lighting is subjective, and dependent entirely upon the look and feel desired for the game. It is common that I will spend a LOT of time tweaking this part. Lighting rigs can change depending on environment theme as well, so in any given isometric project you might create a dozen of these templates. In order to assist in setting up the lighting conditions, I will usually import a landscape object. Trees work, of course, but they tend to be a tad heavyweight, requiring a bit of time to render, so something lighter might be more appropriate. It's an iterative process: setting lights, rendering, tweaking, etc...

Basically, I'm looking for proper illumination from the chosen viewpoint angle, and a nice-looking shadow cast on the ground plane. I will spend some time tweaking the Size parameter on the Sun lamp, to dial-in the softness of the shadow. Larger sizes equal softer shadows, smaller sizes equal sharper shadows. At these scales, I find that somewhere around 0.01 or 0.05 is a nice place to be, but it again depends on the game.

EVW4yCF.png

I like to use the Blender Cycles renderer, because the shader-like structure of the material system is a nice addition to the pipeline. However, the stochastic nature of the render, with the accompanying introduction of high-frequency noise, means that I have to crank the Samples in the final render up a LOT in order to get tile pieces to properly match their lighting with each other.

bCurbSg.png

Once I'm somewhat satisfied with the lighting rig, then it's time to start working on the materials for the tree. ngPlant exports each branch layer as a separate mesh object. So each branch layer will be a separate mesh. I typically will select all the branch layers and use 'ctrl j' to join them into a single mesh. Add a simple texture mapping material to the trunk, with the bark texture specified as an image source, and render to see how it looks.

gpJjEXV.png

For the leaves, the material is slightly different. It uses the alpha channel as an input to mix between a diffuse shader, whose color comes from the color channel of the texture, and a transparent shader. This implements an alpha mask using the leaf texture's alpha channel. Additionally, a mix shader with a solid color can be used to grant seasonal variation. Or, a mix shader with a noise component can grant color variation throughout the canopy. For example:

dWFhoKi.png

At this point, once I've tweaked the lighting and the shadow, I save the .blend file, because after this I will be making some destructive edits, so this will give a base to work from for both paths.

First of all, in order to make the final tree render, I delete the ground plane. We want a transparent background, so I tick the Transparent check box in render settings:

6Tjv5VZ.png

Also have to select RGBA for the output format, so it will properly save the alpha channel:

ZhdNHaA.png

And render:

rz9Ihct.png

At this point, I'll usually throw together a quick test background in gimp to see what the tree is going to look like on ground that is roughly equivalent to what will be in the game:

klz8A3i.png

That looks okay. This process usually takes a lot of tweaking. I'll render, and decide I don't like the particular angle the tree is at. Or I'll render, paste it on a background, and decide the lighting is off. Or maybe I don't like the foliage colors. Something almost always needs to be changed, sometimes multiple times, but the end result is usually a tree sprite I'm happy with.

But it kinda looks off, doesn't it? It really, really needs a shadow.

In 3D, shadows are Easy(TM). (For certain definitions of the word "easy"). That is, shadowing is a render process integrated into the pipeline and performed at run time. But in a 2D game like this, you can't really do that. That tree sprite lacks depth information that would be required for run-time shadows. Instead, you have to 'bake' the shadow into the sprite, either as part of the sprite itself or as a separate shadow sprite that is drawn prior to the tree sprite. So, to get the shadow, I will load up the base save for the tree, the one that still has the background plane. And then I gotta do some stuff.

This part is why I really, really, REALLY like the Blender Cycles renderer. It makes the shadow phase of a sprite dead simple. Here is what you do:

UQdLnVq.png

You setup a material like the above, and assign it to both the trunk and the leaves of the tree. (It's not 100% accurate for the leaves; for that, you would also need to add alpha masking, but I find that it's close enough and renders faster this way.)

In Blender Cycles, the renderer does its thing using various rays that are traced, or stepped, through the scene. At any given time, a particular ray will be labeled according it its type or phase. The Light Path node lets you select a behavior based on the type of ray currently being processed. In this case, I select base on whether a ray is a Shadow ray or not. If it is a Shadow ray, then the ray is 'drawn' (ie, considered when generating the output). Otherwise, the ray is 'ignored', ie 'drawn' using a completely transparent material. This means that only the shadow will be drawn when rendered; the object itself is rendered completely transparent. Observe:

dYYWq0V.png

Nifty, yeah? After the shadow is rendered, I can take it into the Gimp and do some stuff to it.

First, I adjust the contrast a bit, until the white parts are fully white. If I used a slightly tinted sun lamp, then I also will desaturate the shadow, to remove any color information. Then I invert, so that the white parts become black, the black parts become white, and everything in between is flipped upside down:

BdBAsDG.png

At this point, I will choose a solid color that is close to my desired shadow color, and use the inverted shadow as an alpha mask. Again, this typically takes some tweaking to get it exactly right, but the end result looks something like this:

uzBrRgI.png

Now, you can either use that shadow as a pre-pass sprite, or you can combine the tree and shadow into a single sprite:

64aSk2U.png

The shadow seems a little too dense, so I'd probably go back and adjust the brightness of the inverted shadow mask, iterating on it until I got something I liked.

Time to check it against the test background:

Ufv4vXL.png

The neat thing about doing trees like this is that, once you have done the work for a single tree, you can rotate the tree and re-render to get multiple variations. Rotating the tree makes it look like an entirely new tree in most cases. You can also alter things like the leaf texture, or the foliage blend colors to get different canopy appearances. You can remove the canopy leaves altogether, leaving just the tree trunk (though, if you do this, I suggest un-hiding another level or two of the exported branches, to get more twigs in view.) You have lots of leeway to adjust lighting, materials, shadows, etc... and the process lets you do that quickly, without having to re-draw trees by hand.

Previous Entry ANL Expression Parsing
17 likes 3 comments

Comments

MARS_999

Josh, As usual bad ass! Man you have talent! Keep up the great work! :)

February 03, 2016 07:31 AM
Eck
Very impressive.
February 04, 2016 04:02 AM
Ashaman73

Great work, pls consider putting this in the article section. I think, that a lot of people doing 2d games will benefit from your tutorial here. :D

February 07, 2016 03:14 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement