If you’ve tried written your own custom shaders like I have in Unity, you know how much of a headache they can be sometimes. And sometimes, the solution to a problem seems obvious but you can’t quite figure out how to solve it.
One of these obvious-but-hard-to-solve issues I had while writing a custom water shader recently is having billboards, such as trees and detail objects drawn on the Terrain, rendering behind your shaders (despite being in front or above a certain surface). This results in the scene looking like this.
Trees and detail objects, when drawn on Unity’s Terrain using Terrain painting tools, are automatically converted into billboards when viewed from a certain distance away. The setting is controlled by the Terrain’s Billboard Start property.
This is a problem that occurs with shaders that are assigned to use the Transparent Render Queue. An easy fix would be for me to increase the Billboard Start property on my Terrain, so that my trees don’t turn into billboards at noticeable distances.
However, I decided I wanted to fix this issue for good, instead of designing around the problem. Hence, I did some research, and found a simple solution.
Changing the Render Queue of the Shader
Although shaders have SubShader
tags that reference the different Render Queues (Background, Geometry, AlphaTest, Transparent, Overlay), we can set our shaders to use a custom index relative to each of these queues by adding an integer offset to the Queue tag.
Each of those five Queue SubShader Tags have their own index values — you can refer to them in Unity’s Documentation on Rendering Order. By adding an offset to the SubShader
tag, you can create an unnamed Render Queue that will be rendered based on its index.
For example, a Shader with the Queue Subshader tag Transparent-200
has an index of 2800, and will render before the Transparent Queue.
However, I could not find a way to change the Render Queue of Unity’s Speedtrees. Hence, I will have to change the Render Queue of my own shader instead. I found that a Queue tag of Transparent-200
worked for me. Hence, I tagged my SubShader
as follows:
{ "RenderType" = "Transparent" "Queue" = "Transparent-200" }
Afterwards, when I went back to my scene, my billboards started rendering correctly (i.e. in front of the water when the tree is above water).
But why does this solution work in the first place, and why does this problem occur at all?
Why Unity got the rendering order wrong
Objects that use a Shader with a Render Queue index above 2500 (2501 and onwards) are considered Transparent. Hence, Unity will render them by sorting them based on their distance and render them starting from the objects furthest away. As a result, objects further away are rendered over objects closer to the camera. This distance is calculated based on the distance between an object’s Pivot and the Camera.
For large objects like a water plane, this distance may cause the Unity to incorrectly calculate the depth of some objects relative to our water plane’s, as the distance of the water plane’s position from the camera is always calculated using its Pivot.
The solution provided works as, after some experimentation, I found that the index value of the Queue used by the Speedtrees is around 2901 (or Transparent-99
). Thus, by changing the Render Queue index of the Water Shader to a value of 2900 or lesser, Unity will render the Speedtrees over the Water even if the Speedtrees are further from the camera and thus solving this issue.
I hope this article helped you solve this problem, as well as potentially solve similar problems that might occur with Render Queues!