Search Unity

Here at Unity, we want to offer you the smoothest experience for making fantastic games. One of the biggest parts of our job is ensuring that Unity is a highly performant platform for you to deploy to mobile devices, but when it comes to getting the best performance out of your games — that’s where you come in. So that you’re not in the dark when it comes to optimising your games, we got together with MadFinger Games, the developers of blockbuster mobile title ‘ShadowGun‘ to offer you a sample level project packed with innovative techniques, to help you learn about the techniques that they employed when creating their game.

We now have a dedicated forum thread for discussing the optimization tips shown in this sample level. Please continue discussion there —

The sample level project can be downloaded via the button below. It should be noted that this is not a gameplay example but purely designed to assist you in learning about optimal mobile techniques —

We’d like to thank MadFinger for their help with this project and for all the information they provide below. For those of you new to some of the terms mentioned here, we include links to external sites explaining the concepts involved:

General knowledge

If you want to have a 3rd person action game with beautiful graphics, complex levels with bunch of enemies with decent A.I. which runs flawlessly on iPhone 3Gs, you must put lot of effort into optimization. For the graphics side, you can optimize the number of triangles, amount of textures in memory and, of course, the shaders. We found that on mobile devices, shaders can be a real performance killer but also an incredible and powerful servant.

Alpha blending kills

The amount of faces using alpha blending, especially when it fills a large portion of the screen, reduces performance on mobile devices dramatically. When a bunch of alpha faces overlap each other and thus increase overdraw, it is a consistent killer for performance on slower devices.

Complex, per-pixel shaders kill framerate

We found that rendering large surfaces (in terms of screen area) with complex fragment shaders kills the framerate even more consistently than alpha blending. Complex fragment shaders should be used for smaller objects only.

Shaders in ShadowGun

Thanks to great cooperation between our programmers and graphic artists, we managed to create shaders that allow us to create advanced effects that do not have great demands on performance. The following shaders were used in the sample level from ShadowGun:

Environment specular maps (Shader Virtual Gloss Per Vertex Additive)

This shader combines Diffuse (RGB channels) and Specular (Alpha Channel). The Specular map uses the alpha channel of the texture to define the glossy vs matte parts of surface as usual, but specularity is not calculated per-pixel, but rather per vertex.

The resulting effect is reasonably convincing and it is significantly faster than using per-pixel specular calculations. It is perfectly suitable for large area meshes (like a level environment). This shader allows us to modify the strength of the specular effect and to approximately determine the direction of «light».

Optimized dynamic character lighting and shading (Lightprobes and BRDF Shader)

The traditional problem with static lighting (Lightmaps) is achieving proper lighting on the dynamic objects (i.e. Player characters and NPCs).

Unity offers a classic solution, which stores lighting information for dynamic objects in proxy objects (Light probes). Dynamic objects then reconstruct incoming light information from nearby probes.

Unity also contains superb character shader, which is well optimized for mobile devices (check out the Mutant character in the sample scene). This shader supports Diffuse, Specular and Normal maps and uses a special script that generates a texture which is used for evaluation of the lighting in BRDF-like fashion. Resulting effect on characters perfectly mimics the next-gen character shading from big console titles.

Fog and volumetric light rays (Shader Blinking Godrays)

When you turn on the global fog in 3d engine, the framerate often drops below a usable level, especially on older mobile devices.

From a graphics point of view however, fog dramatically improves look of the environments, especially helping to perceive the depth and space of a scene. The solution we came up with is the use of simple mesh faces with a transparent texture (Fog planes) instead of global fog. Once a player comes too close to a fog plane, it fades out and moreover, vertices of the fog plane are pulled away (because even a fully transparent alpha surface still consumes lot of render time).

Preparation of the mesh in 3D app to work correctly with shader

  • Vertex color alpha determines which vertices are moveable and which are not (in our case, vertices with black alpha stays, those with white alpha moves).
  • Vertex normal determines the direction of movement.
  • The shader then evaluates distance to the viewer and handles surface fade in/out appropriately.

Its simple to duplicate and distribute Fog planes through the level anywhere its needed. In a very similar way we use this shader for light rays, light source cones and other alpha effects.

Dense animated smoke at plane crash (Shader Scroll 2 Layers Sine Alpha-blended)

Because of density, the smoke particles are pretty expensive, and we found that it is better to use simple mesh with animated smoke texture (e.g. for background fire on crash site).

In our case, the smoke is animated by blending two textures and moving them over each other. There is also vertex alpha involved in order to smooth the edges of the mesh. The mesh is also colored with vertex colors (orange to mimic flames on the ground and greyish as smoke blends to the sky).

Moving Skybox (Shadow Scroll 2 Layers Multiplicative)

For our custom skybox, a similar technique with regards to scrolling in the shader creates a dynamic cloudscape.


Flags and cloths in the wind (Shader Lightmap + Wind)

Any animated objects in the scene brings life to level and when they are animated for «free» by the shader itself. We have animated the flags and cables in the wind in this way. Information which vertices are fluttering in the wind and which are not is defined by the vertex colors alpha in 3d modeling program (white alpha moves, black alpha dont). In shader parameters, user must define the direction and speed of flutter only.

Legal notice

All custom shaders in SHADOWGUN sample level are intelectual property of MADFINGER Games, a.s. and they are free to use (apart from BRDF character shader, which was created by Unity Technologies and is also free to use).
Meshes and textures in the level remains property of MADFINGER Games, a.s. and cannot be used for any commercial purpose without permission from MADFINGER Games, a.s.

68 replies on “ShadowGun: Optimizing for Mobile Sample Level”


thank you for all the work you give away freely, it greatly improvedmy unity projects.

However, I do have a question on how you generated the MutantLookUpTexture_BRDFTex; I’m having a hard time finding how to make the BRDFLit shader work properly.

Thank you in advance for any answer ;)

And don’t forget the interactive fluids surfaces and destructible geometry for the next unity 3D release.

ok got it to work

2-duplicate assets/shaders to some other directory outside of the download
3-open the project — at this point characters show up but it’s mostly pink
4 drag the duplicated assets/shaders into the project tab

I’m only seeing a scene full of lights. I think it’s the same problem everyone else is having. Anyone have any idea how to fix this?

Hi , I have problem with opening the project can you please tell me what to do , or updating the link , I try suggested ways but they didnt work , please help me , thanks alot

What about collision? I noticed you guys had one giant Mesh Collider? (can even see where the rest of the real level went)
Obviously this still ran quiet fast — but what was the choice to have such a large collider instead of partitioning it?
What brought you guys to the conclusion that this was a fast way forward?
Did this mean you had to export the entire level out of Unity and into a 3D app in order to trace over the level?

Really awesome!

The Assets/Assets folder brokes the references with my Mac but after few tricks it works:

1- Copy Assets/Shaders
2- Delete Assets/Shaders
3- Open project
4- Paste Assets/Shaders with Finder
5- Rename Assets/Shaders to Assets2/Shaders
6- Save all
7- Play!


I posted the basic shader I use in my game over in the forum thread where this conversation continues and I’d appreciate it if you would look at the very basic shader I use in my game for the glass.

Hippocoder suggests that shaderlab is basically nonfunctional in ES2.0 but works correctly with ES1.1. That shaderlab is probably converting my shader to something else and stuff gets added when using ES2.0.
I don’t know what to think about that.

On Mac OSX 10.7.3 (all updates) with Unity Pro & Mobile Pro 3.4.2f2, attempting to open this project results in the dialog
«Warning, this project was created with a newer version of Unity. Do you want to Quit or Go ahead anyway (maybe data loss).» or words to that effect.

Choosing Quit, causes Unity to Quit totally(!), but overwrites the ‘last project opened’ configuration anyway,
so you are then always presented with this failed project and the same Hobson’s choice (grrr, this is an existing Unity bug that should have been fixed long ago).

Choosing Go Ahead does open the project but with much weirdness. Only one of the scenes can be selected (both are named ‘_Level’) and has a ton of errors. Some other assets appear duplicated.

Selecting Help->Check for updates, returns a message saying that no updates are available, this is the latest version.

It looks like Madfinger have created this example with a version of Unity that has not yet been publicly released.

[Also, Unity’s Spam protection field on this page does not work, if you leave the field blank, then do as in instructed and use your ‘back’ button to return to this page, newly entered correct sums are never accepted.]

Thanks for the suggestion.

Rendering Path is not an option in an iOS build in the Player Settings. (It is in a PC/Mac build however)
But I can change it on the camera.
(Which makes it odd that the camera has a default Rendering Path option of Use Player Settings but then you go to Player Settings and there is no Rendering Path option with an iOS build.)

I only use one camera per scene so it is a quick thing to test, unfortunately no difference in FPS.
armv6 30fps, armv7 16-20fps in the same scene with camera rendering path set to Vertex Lit.


Downloaded *.zyp package, unzipped, double clicked _Level.unity, import went but what I have only got is blue screen in Game screen and in Project is only Shader folder. I’m on Mac OS Lion. What is wrong? It seems that everything is has broken somehow.

I’ve only played a little in Unity, nothing more (just don’t have the time), but ShadowGun is one of the most impressive games for my Motorola DEFY (it’s impressive because it runs on such a low end phone), so I love that this post and the sample are being made available.

@Allan Chaney: I’d still say the most likely culprit is not GLES 1.1 vs. 2.0 per se, but the fact that probably GLES 2.0 version does something more. A simple example: if you use built-in, default Diffuse shader, it does per-pixel lighting on GLES2.0, but per-vertex lighting on GLES1.1 (since it’s impossible to do per-pixel lighting there).

As a quick test, try setting the «Rendering Path» in Project Settings (or in all your cameras) to Vertex Lit. GLES 1.1 effectively always uses it, since it can’t support forward rendering with per-pixel lighting.

Thanks for the replies.

My problem is if I target armv7, my framerate is crippled on iPad 1. It’s easily a 50% performance hit. Targeting Universal armv6 and armv7, both cripples performance and results in a build size that makes it impossible using Unity to hit the 20meg limit for mobile downloads.

My game is based on breaking glass so I use very simple glass shaders to make very low poly objects look like glass with a heavy reliance on the physics system for breakage. Since the whole world is made of glass, I’m using shaders with Tags to designate the render Queue allowing for transparent overlapping objects that I can control. (Background, Background+1, playerQueue, OverLay for in front of player) The shaders are shockingly simple with a simple color property for different colored glass and blending (Blend One One) for additive or Blend ScrAlpha OneMinusAlpha for real alpha blending. Oddly enough (armv6 ES1.1) handles this setup admirably. (armv7 ES2.0) on the other hand seems to immediately hit me with a 50% penalty. I would love to move to armv7 so I can use some of the effects demonstrated by MadFinger. I’ll keep researching.

«I’m trying to stick with OpenGLES 1.1 for performance and size reasons» — do you actually see any performance & size advantages when using GLES1.1? Because provided the two do exactly the same stuff, there should be about zero difference (for several years now, the hardware has been GLES2.0 capable; and internally GLES1.1 emulates everything by creating shaders behind the scenes).

Of course, since with GLES2.0 you get more capabilities, it’s easier to kill performance by doing «stupid things». But if you know what you are doing (see shadowgun for example), there should be zero difference.

@Allan: No. If you want to use shaders you have to use OpenGL ES 2.0. You can do these things on the CPU, but that is highly inefficient.

I suggest that if you want to do highend graphics on mobile that you will start using OpenGL ES 2.0. As you can see in Shadowgun you can have great performance with really high-end visuals with OpenGL ES 2.0.

Just to streamline my question…

Does anyone know of a way to get the MADFINGER/Environment/Lightmap + Wind shader
to work in OpenGL ES 1.1?


Thanks that’s a great tip. I ended up buying Vertex Painter from the Asset Store in order to paint the alpha. But doing it in Blender is a better option.

I received the following error when trying to use the Flag shader («MADFINGER/Environment/Lightmap + Wind») using OpenGL ES1.1.
Material doesn’t have a texture property ‘_MainTex’
UnityEditor.DockArea:OnGUI() (And of course the Flag does not render)

It works fine in OpenGL ES 2.0 but I’m trying to stick with OpenGLES 1.1 for performance and size reasons.

@vw Given you seem to know some shader code, do you know if there is a way to get the above shader to run in OpenGL ES1.1?


Hello, the project with my Mac 10.7.3 and Unity Pro 3.5 have lost all references.
I have tried to download it again, but don’t works.
Could you provide an .unitypackage or how i can rebuilt library references?

Thank you in advance!

You rock guys! I’ve busted my nuts trying to recreate vertex specular and volumetric (shrinking) effects after reading the SHADOWGUN at Unite paper.

And then you just give all of this to us! How awesome is that!

[…] To view video tutorials and download the sample project visit here. […]

@ Will Goldstone
Thanks for the support, u’re ever so attentive!

These are the steps taken
(1) install 3.5.0f5, w basic license
(2) DL the zip file
(3) Open the _level.unity scene file.
3.5 humming along, rebuild the library…
Click RUN, and get this error «The referenced script on this Behaviour is missing!»

(4) Import the package downloaded «previously».
3.5 happily import all the assets
Click RUN, and scene fire-up.

Play smoothly on iPad G1+ KindleFire…one word IMPRESSIVE!!!! THANK YOU

PS : basic user, will need to DL the package

@Allan, you are right. VertexColor is part of the model.
The VertexPainting tool in Blender doesn’t let you select the alpha color.
Just make a make a copy of the flag shader, change the line in the vertex shader part from
float bendingFact = v.color.a;
float bendingFact = v.color.r;
and then paint with red color in blender instead.

Still dissecting the scene with a focus on the animated flags. Apparently the shader is creating the movement effect based upon the Vertex Color Alpha on the 3d model, according to the writeup. Does anyone know how to change/set the Vertex Color Alpha in Blender 2.5 and above?

I’m trying to mimic what has been done with the flags but since the model is in .fbx format, it’s very difficult to figure out exactly how it was constructed. You can’t import fbx into Blender. As far as I can tell, materials do not need to be imported with the model in order for this to work. So my guess is the Vertex color alpha is somehow being stored in the Mesh information itself. But I don’t know how to set the vertex color alpha on a model using Blender.

@Matthew — you cannot open this in 3.4.2. Ordinarily it is not advised to do this with 3.5 authored projects but especially in this instance, Shadowgun makes use of a lot of 3.5 features — Light Probes to quote one. Any reason you haven’t made the leap to 3.5 yet? keen to hear your thoughts.

Great Assets!, can wait to reverse engineer the scene…

But, looks like i’m the only one who still can’t figure out how-to open in OSX unity3.4.2.
Am i missing something ?

Rebuilding Library because the asset database could not be found!
Monobehaviour has unknown format!:
Base Type:MonoBehaviour ByteSize:-1 MetaFlag:32768

blah blah….

Thanks for providing this.

I don’t understand how you are lighting the flags. I believe it has something to do with the LightmapFar-0.exr file. But I’m not sure how that file was created or how it is being used.

Any tips would be appreciated.

Hmmm the _Level scene doesn’t play nice when opened on my Mac though. Seems to have lost all references to meshes, materials, scripts, etc… :0(

Could we get a Unity Package to import instead? Or am I doing something stupid?!

This is absolutely fantastic! Agree with Hippo, one of the best blog posts I can remember reading.

Huge thanks to Unity and Madfinger for doing this! :0)

@Aubrey: no difference there. The only thing that counts is what operations & at what precision the shader actually performs. Internally, all «fixed function» shaders are turned into vertex+fragment shaders anyway.

Thanks Madfinger and Unity for sharing this. It`s really awesome. The game looks fantastic and have the opportunity to see how is it made is very useful.

How complex would a shader have to be to be one that really kills perf? Like, if I did a very basic unlit shader with just a single texture sample lookup, is that the exact equivalent of the same shader done without fragment shader stuff? Or is there overhead there?

@Arowx: limit of 255 tris is for convex mesh colliders (i.e. mesh colliders you would normally put onto moving objects). The non-moving, non-convex mesh colliders can be arbitrarily large.

Thankyou for such an informative post, great to see your knowledge and hard worked shared for the rest of us.

+1 Jason Amstrad needs a kick in the teeth for being so ungrateful.

Wow! this is amazing thank you Unity and Shadowfinder!

But I notices that they use a separate mesh optimized for collision which has 3,423 tris, I thought collision meshes in Unity were limited to 255 tris (Unity Docs)?

@richard: Fog planes only cover a small part of the screen. They also give you much more artistic control.

The shaders ensure that the fogplanes are shrinked automatically when they get close to the camera thus never eat up fillrate by covering a lot of pixels.

I’m a bit puzzled by how you start out by saying that alpha blending kills, but then you found that using alpha-blending planes for fog is faster than a standard per-vertex fog calculation. What gives?

Great post! Love the tips and tricks for optimization — even for those of us who aren’t using mobile devices. I love being able to throw more stuff in the game without killing frame-rate (especially making the environment come alive). Any idea when the assets will become available?

I still cannot believe that this run on a mobile devices. I still cannot that you guys make some of your asset available for free. These are unavailable learning resources.
The coolest engine in the industry meet the coolest developers in the Industry.
Thank you very much

Comments are closed.