Changing terrain shader

Is there a way to change the shader used for rendering the built-in terrain? If not, is there a specific reason why it shouldn’t be allowed?

Not that I know of. The terrain engine asks only for a texture. perhaps Antena or HiggyB could help here?

I don’t know much about shaders, but I think if you download the builtin shaders, you can change the shaders under the folder “TerrainShaders”.

I think if you don’t rename them and have them in you asset folder unity will use them.

http://unity3d.com/download_unity/builtin_shaders.zip

You could also possibly export the terrain as an .obj using this script from unifycommunity.com

import System.IO;
import System.Text;

enum SaveFormat {Triangles, Quads}
enum SaveResolution {Full, Half, Quarter, Eighth, Sixteenth}

class ExportTerrain extends EditorWindow {
    var saveFormat = SaveFormat.Triangles;
    var saveResolution = SaveResolution.Half;
    static var terrain : TerrainData;
    
    @MenuItem ("Terrain/Export To Obj...")
    static function Init () {
        terrain = null;
        var terrainObject : Terrain = Selection.activeObject as Terrain;
        if (!terrainObject) {
            terrainObject = Terrain.activeTerrain;
        }
        if (terrainObject) {
            terrain = terrainObject.terrainData;
        }
        EditorWindow.GetWindow(ExportTerrain).Show();
    }
    
    function OnGUI () {
        if (!terrain) {
            GUILayout.Label("No terrain found");
            if (GUILayout.Button("Cancel")) {
                EditorWindow.GetWindow(ExportTerrain).Close();
            }
            return;
        }
        saveFormat = EditorGUILayout.EnumPopup("Export Format", saveFormat);
        saveResolution = EditorGUILayout.EnumPopup("Resolution", saveResolution);
        
        if (GUILayout.Button("Export")) {
            Export();
        }
    }
    
    function Export () {
        var fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj");
        var w = terrain.heightmapWidth;
        var h = terrain.heightmapHeight;
        var meshScale = terrain.size;
        var tRes = Mathf.Pow(2, parseInt(saveResolution));
        meshScale = Vector3(meshScale.x/(w-1), meshScale.y/tRes, meshScale.z/(h-1));
        var uvScale = Vector2(1.0/(w-1), 1.0/(h-1));
        var tData = terrain.GetHeights(0, 0, w, h);
        
        w = (w-1) / tRes + 1;
        h = (h-1) / tRes + 1;
        var tVertices = new Vector3[w * h];
        var tUV = new Vector2[w * h];
        if (saveFormat == SaveFormat.Triangles) {
            var tPolys = new int[(w-1) * (h-1) * 6];
        }
        else {
            tPolys = new int[(w-1) * (h-1) * 4];
        }
        
        // Build vertices and UVs
        for (y = 0; y < h; y++) {
            for (x = 0; x < w; x++) {
                tVertices[y*w + x] = Vector3.Scale(meshScale, Vector3(x, tData[x*tRes,y*tRes], y));
                tUV[y*w + x] = Vector2.Scale(Vector2(x*tRes, y*tRes), uvScale);
            }
        }
    
        var index = 0;
        if (saveFormat == SaveFormat.Triangles) {
            // Build triangle indices: 3 indices into vertex array for each triangle
            for (y = 0; y < h-1; y++) {
                for (x = 0; x < w-1; x++) {
                    // For each grid cell output two triangles
                    tPolys[index++] = (y     * w) + x;
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = (y     * w) + x + 1;
        
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = ((y+1) * w) + x + 1;
                    tPolys[index++] = (y     * w) + x + 1;
                }
            }
        }
        else {
            // Build quad indices: 4 indices into vertex array for each quad
            for (y = 0; y < h-1; y++) {
                for (x = 0; x < w-1; x++) {
                    // For each grid cell output one quad
                    tPolys[index++] = (y     * w) + x;
                    tPolys[index++] = ((y+1) * w) + x;
                    tPolys[index++] = ((y+1) * w) + x + 1;
                    tPolys[index++] = (y     * w) + x + 1;
                }
            }   
        }
    
        // Export to .obj
        try {
            var sw = new StreamWriter(fileName);
            sw.WriteLine("# Unity terrain OBJ File");
            
            // Write vertices
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
            for (i = 0; i < tVertices.Length; i++) {
                var sb = StringBuilder("v ", 20);
                // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format
                // Which is important when you're exporting huge terrains.
                sb.Append(tVertices[i].x.ToString()).Append(" ").
                   Append(tVertices[i].y.ToString()).Append(" ").
                   Append(tVertices[i].z.ToString());
                sw.WriteLine(sb);
            }
            // Write UVs
            for (i = 0; i < tUV.Length; i++) {
                sb = StringBuilder("vt ", 22);
                sb.Append(tUV[i].x.ToString()).Append(" ").
                   Append(tUV[i].y.ToString());
                sw.WriteLine(sb);
            }
            if (saveFormat == SaveFormat.Triangles) {
                // Write triangles
                for (i = 0; i < tPolys.Length; i += 3) {
                    sb = StringBuilder("f ", 43);
                    sb.Append(tPolys[i]+1).Append("/").Append(tPolys[i]+1).Append(" ").
                       Append(tPolys[i+1]+1).Append("/").Append(tPolys[i+1]+1).Append(" ").
                       Append(tPolys[i+2]+1).Append("/").Append(tPolys[i+2]+1);
                    sw.WriteLine(sb);
                }
            }
            else {
                // Write quads
                for (i = 0; i < tPolys.Length; i += 4) {
                    sb = StringBuilder("f ", 57);
                    sb.Append(tPolys[i]+1).Append("/").Append(tPolys[i]+1).Append(" ").
                       Append(tPolys[i+1]+1).Append("/").Append(tPolys[i+1]+1).Append(" ").
                       Append(tPolys[i+2]+1).Append("/").Append(tPolys[i+2]+1).Append(" ").
                       Append(tPolys[i+3]+1).Append("/").Append(tPolys[i+3]+1);
                    sw.WriteLine(sb);
                }      
            }
        }
        catch (err) {
            Debug.Log("Error saving file: " + err.Message);
        }
        sw.Close();
        terrain = null;

        EditorWindow.GetWindow(ExportTerrain).Close();
    }
}

Of course, you may have problems with collisions an mesh colliders after you reimport the terrain.

The terrain shaders can not be altered.

Shaders / materials can only be applied to models etc imported, problem with them is that you will lose the LoD handling in the distance as well as the possibility to paint detail models and trees.

Thanks for the answers guys.

So it’s more or less a no go if I want to keep on using the terrain script features. It’s a pity we can’t access the terrain materials/shaders normally, I’m puzzled why this functionality isn’t exposed.