Description
Fast Decals allows the game to render a large amount of decals - such as blood splatters and blob shadows - at a blazing fast speed, with a single draw call. It also provides an automatic aging mechanism, which slowly fades away old decals when new ones are created. Fast Decals was created mobile devices in mind and is best suited to games with a small world, such as tower defense games etc. Eventhough Fast Decals was created for rendering decals in 3d nothing stops you from using it for 2d tile/sprite rendering.
Version 1.5 now comes with built in texture atlas generation support so you are no longer required to create your own atlases. Just drag and drop your textures and the atlas will be generated automatically. However you can still use your own atlases the same way than before.
Usage example
3d Decal Benchmark
Mesh Renderer with quad mesh x 1024 (small, distant) | Sprite Renderer, 1024 sprites (small, distant) | Fast Decals, 1024 decals (small, distant) |
---|---|---|
Mesh Renderer with quad mesh x 32 (big, closeups) | Fast Decals, 32 decals (big, closeups) |
---|---|
The performance benefit achieved by using Fast Decals roughly starts when you have more than 20 decals to render. As you can see from the benchmark results above the Fast Decals may render decals at best 100x faster than using using a single GameObject for each decal with quad mesh and Unity MeshRenderer. However when the decals fill up the whole screen or the decal count is less than 20 the performance benefit starts to fade.
If all your decals are identical and opaque, the MeshRenderer with quad mesh might be almost as fast since Unity will be able to batch those draw calls together. But when you need to change the decal opacity/alpha like most of the games do, Fast Decals will rock!
NEW: Unity 4.3 introduced the new Sprite Renderer. I did some benchmarking and it seems that Fast Decals is still a lot faster when rendering a lot of distant decals. However I have not yet tested the close up mode for Sprite Renderer since I have lost the old close up benchmark code. The Sprite Renderer above was tested with Unity Pro and the sprite was using the Tight mode.
2d Tile Based Benchmark
Graphics.DrawTexture x 117 (64x64px) | Fast Decals, 117 decals, (64x64px) |
---|---|
As an experiment I tried how Fast Decals would perform if it was used for rendering a small tile based map with 117 visible tiles. I compared it to rendering the tiles one by one with Graphics.DrawTexture with texture atlas. As you can see it performs really good for tile rendering too!
Renderers
There are two different renderers, FastDecalsRenderer and FastDecalsAtlasRenderer. The FastDecalsAtlasRenderer offers a simple way to create a texture atlas from a bunch of textures. Having your decals in a texture atlas is a requirement for fast rendering. If you want to create your texture atlases by hand you may use the FastDecalsRenderer instead.
Basic usage
- Import the Fast Decals package.
- Create an empty GameObject. Drag either FastDecalsRenderer or FastDecalsAtlasRenderer on the GameObject depending if you have created a texture atlas by hand or you want to use the automatic atlas generation tool.
Select desired rendering mode from Alpha, Degenerate, BlockCopy, ArrayCopy and Aging. Use Aging mode for static decals such as blood splatters and after explosion marks on the ground which may slowly fade away in time and any of the other modes for dynamic stuff like blob shadows. You may read more about rendering modes in the next chapter.
Define the material to be used for rendering the decals. For the shader you could use something from the Mobile / Particles group or create your own. For the Aging and Alpha modes the shader must support vertex colors. If your shader supports lighting you should set Generate Normals to true. It is recommended to add your decals in a texture atlas instead of creating many FastDecalsRenderers. You should only create a new renderer when you need to draw the decals with a different shader or when your atlas gets too big. In most cases you should set the offset to 0/0 and tiling to 1/1.
If you are using the FastDecalsAtlasRenderer: After dragging the FastDecalsAtlasRenderer to your GameObject you will notice that it will also have a component called FastDecalsAtlas which will contain the source data for your renderer. To create the texture atlas you should simply drag and drop your textures to the textures slot and click Add single to add it to the atlas. You may also add all textures inside the same folder by clicking the Add all in folder instead of dragging them all one by one. You may anytime add more items or remove items and the atlas will update automatically. You may also name the items to make them easier to access from your scripts. By default the name is stripped from the texture asset filename. By clicking the Atlas button on the component toolbar you will see a preview of the generated atlas and you may also change the atlas texture format, padding and it's maximum size.
- If you are using the FastDecalsRenderer: If you are using a grid based texture atlas you can automatically create texture offsets/tiling data to your decal renderer by defining the horizontal and vertical tile count. When you draw a decal you can simply reference to the decal index. As an example you could set the horizontal and vertical tile count to 8 and use 1024x1024 atlas texture which would allow you to have 64 different decals of size 128x128. Incase you need even bigger atlases, please check your supported device list and their maximum texture size. Most of the newer device support 2048x2048 and some even 4096x4096. It is much better to have a bigger texture atlas than having many FastDecalsRenderers. Incase you want to use custom texture coordinates instead of the grid based, set the horizontal and vertical tile count to 1 and pass the texture offset and texture tiling when actually drawing the decals.
Define the maximum number of decals which you will not exceed. It probably does not matter much if you define 64 or 128 in performance but 5000 is a different story. Smaller is faster.
To actually draw a decal you should get a reference to your renderer and then call DrawDecal function in your Update function. Not in FixedUpdate since it might not be in sync with the rendering and not in LateUpdate since FastDecals will process the draw requests inside it.
public void DrawDecal(Vector3 pos, float size, int index)
public void DrawDecal(Vector3 pos, float size, int index, Quaternion rotation)
public void DrawDecal(Vector3 pos, float size, int index, Vector3 normal)
public void DrawDecal(Vector3 pos, float size, int index, Color32 color)
public void DrawDecal(Vector3 pos, float size, int index, Color32 color, Quaternion rotation)
public void DrawDecal(Vector3 pos, float size, int index, Color32 color, Vector3 normal)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling, Quaternion rotation)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling, Vector3 normal)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling, Color32 color)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling, Color32 color, Quaternion rotation)
public void DrawDecal(Vector3 pos, float size, Vector2 textureOffset, Vector2 textureTiling, Color32 color, Vector3 normal)
public void DrawDecal(Vector3 pos, Vector2 size, int index)
public void DrawDecal(Vector3 pos, Vector2 size, int index, Quaternion rotation)
public void DrawDecal(Vector3 pos, Vector2 size, int index, Vector3 normal)
public void DrawDecal(Vector3 pos, Vector2 size, int index, Color32 color)
public void DrawDecal(Vector3 pos, Vector2 size, int index, Color32 color, Quaternion rotation)
public void DrawDecal(Vector3 pos, Vector2 size, int index, Color32 color, Vector3 normal)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling, Quaternion rotation)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling, Vector3 normal)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling, Color32 color)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling, Color32 color, Quaternion rotation)
public void DrawDecal(Vector3 pos, Vector2 size, Vector2 textureOffset, Vector2 textureTiling, Color32 color, Vector3 normal)
The Draw function will take the decal position, size and tile index as parameters. The size can be defined as a float when you want to want the decal to be a square or with Vector2 when you want to be able to stretch it. You may also rotate the decal by giving a rotation quaternion or by giving a normal the decal will be automatically aligned with it. When rotation or normal are not defined the decal will be aligned on to the ground and the y rotation is randomized.
When you use the FastDecalsRenderer and a grid based texture atlas you should give the requested tile index. For example when using 4x4 grid you may request index between 0 and 15. Incase you are using a none grid based texture atlas you can pass the texture offset and tiling as parameters. When you are using the FastDecalsAtlasRenderer you can retrieve the tile index by name from your FastDecalsAtlas.
The color is defined in Color32 instead of Color since Unity documents say it will perform better. For the Aging and the Alpha mode the shader should take the final alpha from the vertex colors. Fast Decals provides a couple of high performance example shaders inside the package.
Incase you want to suppress all Fast Decals log messages, just check the Suppress Log Messages check button on your FastDecalsRenderer. Log messages are automatically silenced in the release build.
Accessing the atlas items
When you are using the FastDecalsAtlasRenderer you will be also using FastDecalsAtlas. You can access atlas texture coordinates by texture name or texture index. When you are rendering a decal you can just pass the texture index to the FastDecalsAtlasRenderer instead of manually retrieving and passing the uv coordinates from the atlas. Your FastDecalsRenderer will contain a public reference to the atlas which also allows you to share atlas between multiple renderers.
public int GetIndexByName(string name)
public bool GetTexCoordsByIndex(int i, out Vector2 textureOffset, out Vector2 textureTiling)
public bool GetTexCoordsByName(string name, out Vector2 textureOffset, out Vector2 textureTiling)
public string GetNameByIndex(int i)
For best performance you should always cache the texture index when initializing your scene instead of requesting the index by name for each update call.
public class IndexCachingExample : MonoBehaviour {
public FastDecalsAtlasRenderer atlasRenderer;
private int mBlobShadowIndex;
void Awake() {
mBlobShadowIndex = atlasRenderer.atlas.GetIndexByName("BlobShadow");
}
void Update() {
atlasRenderer.DrawDecal(transform.position + Vector3.up * 0.01f, 2.0f, mBlobShadowIndex);
}
}
About Fast Decals, bounds and rendering modes
FastDecalsRenderer creates a single mesh from selected amount of decals. The mesh is dynamically changed. The visibility of the mesh is based on bounds (frustum culling) like any other mesh/rendeder in Unity. The rendering mode defines how the old/unvisible decals are hidden. Basically Alpha, Degenerate, BlockCopy and ArrayCopy modes all behave the same. They all draw decals with lifetime of one frame. BlockCopy and ArrayCopy just takes a bit more from the CPU but less from the GPU and vice versa. The Aging mode is a special mode in which the decals life time depends from the maximum number of decals and from the speed in which new decals are requested to be drawn. The Degenerate mode is probably the fastest and it hides the old/unvisible decals by giving incompatible triangle vertices which will cause the triangle to degenerate. Incase you think the Degenerate mode is not the way to go you may try the Alpha mode instead which will use alpha to hide the old/unvisible triangles. In BlockCopy and ArrayCopy modes only the currently visible triangles are sent to the GPU. You may benchmark the modes to see which mode performs the best in your game. By using native code Fast Decals could be even faster but since you can only use managed code in Unity Indie this is how the magic happens now.
Depending of your game you may need to change the bounds of your renderer. For games with a small world you can simply set your renderer position to the center of your world and the bounds to be roughly the same size than your world. The current renderer bounds are shown with a yellow box inside the editor scene view. In some cases, like fps games and particle rendering you may need to manually update the bounds on each frame to be inside the camera view. This can be most easily achieved by simply parenting the renderer with the used camera.
Another usage example
Faking shadows is a great way to boost your mobile game performance.
Support
Incase you are having problems with Fast Decals, don't hesitate to contact
About the author
See more about the author by clicking here.
'Technical Report > R&D test' 카테고리의 다른 글
GLSL Rotate image shader (0) | 2017.07.21 |
---|---|
Unity Bilboard shader (0) | 2017.07.13 |
approximate oren-nayar shader (0) | 2017.06.19 |
Texture Array (0) | 2016.08.02 |
Ai Standard Material Library (0) | 2016.01.22 |