This is a question I have the answer for, and it’s more fitting to Unity Answers than the forums.
When placing trees on the terrain using the terrain engine, the trees, when becoming billboards, have a bright ‘glow’ outline to them.
This is something that bothered my team for a long time, and we haven’t found any information about it online. To illustrate the problem:

We figured out the problem was with the alpha-blending part of the shader, and it was extremely easy to fix. We just downloaded the built-in terrain billboard shader and added the following line of code: AlphaTest Greater 0.9
after the blending mode definition Blend SrcAlpha OneMinusSrcAlpha
You can find the Fixed shader here: Just copy and paste this into a shader file and you’re done.
I just ran into this problem when switching to linear lighting in unity 5.
The above solution didn’t solve it, after much digging it turns out the background color of the render texture unity uses for tree billboards isn’t black (it’s (0.025,0.025,0.025)).
In gamma space this doesn’t present itself as a problem. However in linear space, this is converted to gamma space at the end of the render pipeline bringing it to a much more visible grey (0.19,0.19,0.19)
To solve this I modified the TreeCreatorLeavesRendertex.shader to convert the billboard render texture to an approximation of gamma color space with
c.rgb = sqrt(c.rgb);
c.rgb = max(c.rgb,0.025); //Ensure our tree is never rendered darker than the background color
towards the end of the fragment shader
Then do the opposite operation in the BillboardTree.shader
col.rgb = col.rgb * col.rgb;
Not a perfect solution, but will work for us until speedtree stops performing so poorly
[UPDATED 22.03.17] I’ve used this shader, plus:
-
TOD_TreeBillboardState. Means need to Colorize or not (Night or Day), set through Shader.SetGlobalFloat
-
TOD_TreeBillboardColor. Which Color to use for fill. Uses white-to-black Gradient, based on Night-time. Set through Shader.SetGlobalColor
Gradient t instructions:
if (Cycle.Hour > 17f)
t = Mathf.Lerp(0f, .5f, (Cycle.Hour - 17f) / 7f);
else
t = Mathf.Lerp(.5f, 1f, Cycle.Hour / 7f);
!All is Updating every frame, with day-night-cycle script. No performance problems.
Shader "Hidden/TerrainEngine/BillboardTree" {
Properties {
_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
}
SubShader {
Tags { "Queue" = "Transparent-1" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
Pass {
Tags { "RequireOption" = "SoftVegetation" }
// Dont write to the depth buffer
ZWrite off
// Set up alpha blending
Blend SrcAlpha OneMinusSrcAlpha
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
Float TOD_TreeBillboardState;
fixed4 TOD_TreeBillboardColor;
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR0;
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
};
v2f vert (appdata_tree_billboard v) {
v2f o;
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.color = v.color;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
sampler2D _MainTex;
fixed4 frag(v2f input) : SV_Target
{
fixed4 col = tex2D( _MainTex, input.uv);
if (TOD_TreeBillboardState > .5)
if (col.r < .18 && col.a < .9)
col.rgb *= TOD_TreeBillboardColor.rgb;
col.rgb *= input.color.rgb;
clip(col.a);
UNITY_APPLY_FOG(input.fogCoord, col);
return col;
}
ENDCG
}
Pass {
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR0;
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
};
v2f vert (appdata_tree_billboard v) {
v2f o;
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.color = v.color;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
sampler2D _MainTex;
fixed4 frag(v2f input) : SV_Target
{
fixed4 col = tex2D( _MainTex, input.uv);
col.rgb *= input.color.rgb;
clip(col.a - .99);
UNITY_APPLY_FOG(input.fogCoord, col);
return col;
}
ENDCG
}
}
Fallback Off
}
Another pretty simple solution for that is changing the alpha cutout directly in the shader. Simply go to “clip(col.a)” and add -0.9 to it. This will increase the cutout and removes the grey outline.
An easy way to fix this is to go to terrain settings and change the billboard to about halfway, and if it still appears, put it up all the way.
After hours of searching and having no clue how to write shaders myself I managed to find a fix.
The original solution posted by the topic creator did two things wrong for me (2018.4.1f1):
So to fix the issue with the outline I implemented TimNick151297’s advice, adding -0.9 to “clip(col.a)”.
To fix the fact that fog is not being applied I took the code for Unity’s default billboardtree shader which I found online here: Unity-Built-in-Shaders/DefaultResourcesExtra/TerrainShaders/Trees/BillboardTree.shader at master · TwoTailsGames/Unity-Built-in-Shaders · GitHub
and merged it with the code provided above (along with the outline fix).
I appreciate this is a pretty old topic but it’s one of the first results on google when searching this issue, figured it would save someone out there the headache I had trying to fix this today.
To implement this fixed shader, simply go to any of your project folders and Right Click - Create - Shader - Standard Surface Shader. Name the shader BillboardTree. Unity will automatically pick this up and use it instead of the default shader it’s currently using.
Here’s the fixed code, working perfectly in 2018.4.1f1:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Hidden/TerrainEngine/BillboardTree" {
Properties {
_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
}
SubShader {
Tags { "Queue" = "Transparent-100" "IgnoreProjector"="True" "RenderType"="TreeBillboard" }
Pass {
ColorMask rgb
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater 0.9
ZWrite Off Cull Off
CGPROGRAM
#pragma vertex vert
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
#pragma fragment frag
#pragma multi_compile_fog
struct v2f {
float4 pos : POSITION;
fixed4 color : COLOR0;
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
};
v2f vert (appdata_tree_billboard v) {
v2f o;
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = UnityObjectToClipPos (v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.color = v.color;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
sampler2D _MainTex;
fixed4 frag(v2f input) : COLOR
{
fixed4 col = tex2D( _MainTex, input.uv);
col.rgb *= input.color.rgb;
clip(col.a-0.9);
UNITY_APPLY_FOG(input.fogCoord, col);
return col;
}
ENDCG
}
}
SubShader {
Tags { "Queue" = "Transparent-100" "IgnoreProjector"="True" "RenderType"="TreeBillboard" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma exclude_renderers shaderonly
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : POSITION;
fixed4 color : COLOR0;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_tree_billboard v) {
v2f o;
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = UnityObjectToClipPos (v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.color = v.color;
return o;
}
ENDCG
ColorMask rgb
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off Cull Off
AlphaTest Greater 0.9
SetTexture [_MainTex] { combine texture * primary, texture }
}
}
Fallback Off
}