I’m making a WebGL game, so I had the same problem.
If I placed the trees manually by hand, it seems to use GPU instancing, even with the default materials (Optimised Bark/Leaf without Enable Instancing). Whereas the terrain placement trees had 9 draw calls for each tree, due to shadows and multiple materials, etc. So, it seems like the optimised materials can instance, but not if they’re placed by the terrain system.
I replaced the terrain-placed trees with my tree creator prefab using the script below, but the tree looked too bright at certain angles, so I switched the Optimised Leaf material with an instancing-enabled Nature/Tree Creator Leaves material, by replacing it in the Mesh Renderer.
Now I can draw a whole forest with 22 added draw calls, and 7k tris, and don’t even have to bother will billboarding because they’re so low-poly. I can also use the terrain tree painter and just set tree distance to 0 when I want to hide them.
Use this script to replace the terrain-placed trees with your prefab asset. It still needs options, such as replacing different detail meshes with different prefabs. That requires using TreeInstance.prototypeIndex (what number detail mesh it is).
http://answers.unity3d.com/questions/723266/converting-all-terrain-trees-to-gameobjects.html?childToView=1419858#answer-1419858
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
// Replaces Unity terrain trees with prefab GameObject.
// http://answers.unity3d.com/questions/723266/converting-all-terrain-trees-to-gameobjects.html
[ExecuteInEditMode]
public class TreeReplacerS : EditorWindow {
[Header("Settings")]
public GameObject _tree;
[Header("References")]
public Terrain _terrain;
//============================================
[MenuItem("Window/My/TreeReplacer")]
static void Init()
{
TreeReplacerS window = (TreeReplacerS)GetWindow(typeof(TreeReplacerS));
}
void OnGUI()
{
_terrain = (Terrain)EditorGUILayout.ObjectField(_terrain, typeof(Terrain), true);
_tree = (GameObject)EditorGUILayout.ObjectField(_tree, typeof(GameObject), true);
if (GUILayout.Button("Convert to objects"))
{
Convert();
}
if (GUILayout.Button("Clear generated trees"))
{
Clear();
}
}
//============================================
public void Convert()
{
TerrainData data = _terrain.terrainData;
// Create parent
GameObject parent = GameObject.Find("TREES_GENERATED");
if (parent == null)
{
parent = new GameObject("TREES_GENERATED");
}
// Create trees
foreach (TreeInstance tree in data.treeInstances)
{
Vector3 position = new Vector3(tree.position.x * data.detailWidth - (data.size.x / 2), tree.position.y * data.size.y - (data.size.y / 2), tree.position.z * data.detailHeight - (data.size.z / 2));
// Instantiate as Prefab if is one, if not, instantiate as normal
GameObject go = PrefabUtility.InstantiatePrefab(_tree) as GameObject;
if (go != null)
{
go.transform.position = position;
go.transform.parent = parent.transform;
}
else
{
Instantiate(_tree, position, Quaternion.identity, parent.transform);
}
}
}
public void Clear()
{
DestroyImmediate(GameObject.Find("TREES_GENERATED"));
}
}