Leveraging Vertex Color to dramatically reduce texture count in midcore games
By utilizing Vertex Color and texture atlases, a game can significantly minimize its texture requirements to perhaps 10-15 textures for the entire game. With effective compression, the total size can be contained within 5-10 MB — a stark contrast to the 150 MB that might be expected without this method. This technique is particularly beneficial for midcore games, although developers in the hyper-casual space might also find it valuable to meet store limits on build size.
Sometimes, developers overlook that exported FBX files from 3D packages contain more than just “vertex positions.” They also include vectors for normal directions, vertex colors, texture coordinates, and bone indices with weights.
Why is this important?
For midcore projects, where art can come close to photorealism, aiming to reduce texture counts and lighten device load, this approach is invaluable. Take Railroad Empire as an example, where we craft models from actual drawings and photographs to recreate detailed and historically accurate railroad dioramas for detail-oriented players.
Focusing on “tricks,” let’s delve into Vertex Colors and their application.
It’s essential to note that the techniques discussed are not one-size-fits-all solutions due to the unique nature and requirements of each project. However, Vertex Color can effectively meet various needs without relying on additional textures. For instance, Roughness or Metallic properties can be easily integrated into one of the RGB channels of Vertex Color.
Let’s explore this through a case study of Railroad Empire.
Art request: Enhance certain buildings’ roofs/domes with Specular effects and make the windows shimmer.
Constraints: Only one texture per material and one material per Mesh Renderer array is allowed, and the building’s grade is represented as a single Mesh.
Game engine screenshot:
Here’s how Vertex Color masks are integrated into FBX models:
Accessing this data through HLSL is straightforward and doesn’t require any additional libraries or complex calculations. This forms the foundation for expanding our material functionality.
struct appdata {
float4 vertex : POSITION;
float4 color : COLOR;
};
struct v2f {
float4 pos : POSITION;
float4 color : COLOR;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag(v2f i) : COLOR {
return i.color;
}
Another Vertex Color application: simulating AO
Ambient Occlusion typically adds shadowing to specific areas of a 3D model. Again, Vertex Color aids us by allowing the “coloring” of desired areas with RGB, which the shader can then reference according to the channel we indicate.
This model employs no AO textures.
In Railroad Empire and other projects, we frequently use Vertex Color to simulate Specular effects and for objects with alpha channels, avoiding the creation of excessive textures.
The outcome is a significantly lighter asset load than would be possible without employing these techniques.