Long one incoming, - grab a tea & a cookie or something:
Camera culling is one thing, but there is also Occlusion Culling (OC). Watch out, because if you use very high resolution of OC, it will bite out a HUGE chunk of memory out of you (in return of freeing you a lot of GPU work, giving a lot more spare FPS).
Draw call batching, to send more data at once from CPU to GPU, so that GPU can draw it all at once.
It will free up your CPU, allowing you to use better AI, faster scripts + leaves some space for Operating System itself.
compressed textures take less space on HDD (your final application size is a LOT less), but take longer to unpack before they can be used by the GPU during the game. This leads to longer loading screens)
Non-compressed Texture size (when talking about texture size, always imply its “unpacked”, in other words “non-compressed”, the way in which GPU would use it), - this depicts how much of RAM the game will bite-off.
Exactly same goes for sounds, uncompressed.
If you are using a lot of realtime lights (more than 5), consider using Deferred rendering instead of Forward. Graphics card draws objects one by one, “slapping” them onto screen. As it slaps each one on, it light’s the pixels which the object occupies on the screen. After that, some other object might be “slapped” on top of previous one, and the previous object wouldn’t be seen. Of course this means we waste computing power when lighting such objects which never make it on screen. This is usual, old-way of doing illumination, called Forward-rendering.
But, if we wait until we’ve “slapped” all the objects onto the screen, we can light all the pixels that we know will definitely be seen by the player, and definitely won’t be obscured. This is what Deferred-rendering is about. it takes more RAM and started to be used on mobile devices just recently, because of that.
But with it, we can use 100+ lights at once, rendering at way more than 60fps (Forward would choke at like 20 lights already, especially in scenes where many objects overlap each other from pov of camera).
Look into LOD - level of detail. When objects are far away from the camera and take up only a few pixels on the screen, there is no point of telling GPU to draw all of its polygons. Instead, we can “substitute” those polygons (refereed to as “mesh”) with an approximation 3d-model, which wouldn’t be noticeable at large distances, but would make GPU’s life much easier.
Same applies to textures, with mip-mapping. Lower resolution textures are used on objects which are very far away, helping GPU once again. They are called “mipmaps”, and if enabled, increase the texture size by an extra 1/3 (meaning more for RAM during gameplay + HDD when downloading the game)
Shadow cascades are also important. Those increase the quality of shadows that are nearby, but shrink it when far away, a resembling mipmaps.
If you want to reduce time it takes to load the game (basically chuck all of your textures + meshes from HDD onto RAM + GPU), you can stream from HDD. This can be done by asynchronous scene loading, which unity provides. Be careful, because it could result in HDD not loading things fast enough, where objects begin to “pop-in” as they are loaded into the game.
You should aim at optimising both on GPU and CPU.
pick a device on which you deliver the game (iPhone 5 and above)
Aim to get 70fps at all times (60 + 10 extra in case if game becomes a little laggy), meaning your GPU produces 70 pictures per second, all of which get delivered to the screen in that period of time.
as soon as you are at 70+ fps, you can start using fancier graphics etc, otherwise, you are simply not using the full potential of the GPU on the phone / computer. Most of the screens can’t output more than 60 frames for second, so anything above is pretty much useless, or is a “safety reserve for laggy times”.
Basically, you want to keep GPU busy 100% if possible at all times, but without trying to do 101% percent, because at that point GPU would be bottleneck of the game.
Same goes for the CPU, as well. When both are working to their maximum potential (not faster than the other), you’ve reached the golden middle.
PS. also check out my tutorials Advanced Programming for Games (Igor Aherne, part #9) - YouTube