天天看點

動态加載烘培貼圖與Terrain轉mesh

前言

unity加載烘培貼圖是需要加載場景才可以使用,但如果項目隻使用一個場景或者有許多關卡地形時,明顯通過加載場景來達到更換烘培貼圖的這種做法是不妥當的。而terrain地形在有些安卓機上的支援并不是很好,是以有必要把地形轉為網格。慶幸的是,網上也有這方面的代碼,是以借鑒了網上大神的代碼,整合出一套動态加載烘培貼圖與地形轉網格的方案。

借鑒的網站:

http://www.xuanyusong.com/archives/3807

http://www.cnblogs.com/jietian331/p/5831062.html

一、動态加載烘培貼圖。

1、烘培貼圖的原理

烘培貼圖實際上就是把靜态物體上的陰影與光照儲存成多張png貼圖,并儲存每個靜态物體對應烘培貼圖的編号、裁剪大小與裁剪偏移量。最後,靜态物體與關聯的裁剪紋理在shader裡進行處理。

2、有用的資料

我們知道烘培以後會生成Lightmap Data Asset(5.3之前叫LightmapSnapshot)、貼圖、與SkyboxProbe。實際上真正運作的時候隻需要貼圖以及上面說的每個靜态物體對應烘培貼圖的編号、裁剪大小與裁剪偏移量。是以,我們隻需要把裁剪資訊儲存起來就可以了。

3、資料儲存

我們可以使用檔案來儲存這些資料,但是有一種更友善的做法,也是本文用到的方法。就是通過挂載腳本,把資料存放在腳本中,在做成預置。這樣的做法,就可以把資料完完整整的儲存在預置裡。

二、Terrain轉Mesh

1、實作原理

首先我們要明白,要生成網格需要頂點、三角面,如果有貼圖光照還需要uv和發現。明白了這點,我們就可以從這幾個方向下手。隻要擷取到地圖的寬高作為行與列,用行與列對應的點作為x與z坐标,再根據此點擷取地形高度作為z坐标,就可以生成頂點,再根據頂點來連接配接三角面。uv坐标也類似,隻是把範圍縮放到0與1。

2、有用的資料

生成地形網格,我們需要儲存頂點資料、瓦片貼圖權重、瓦片uv坐标、光照貼圖uv坐标、三角面、以及。由于與地形的光照部分直接使用烘培貼圖,是以法線可以不用記錄。

3、資料儲存

之前曾視圖使用.obj格式來儲存網格資料,但是檢視了一下.obj的資料格式,發現其隻支援一套uv坐标,不支援多套。是以隻能用上面提到的使用腳本預置來儲存資料。

三、使用方法

建立一個空物體,添加PrefabLightmapData腳本,并把所有地形相關物體都放在空物體上,成為其子節點。點選Bake/Bake Prefab Lightmaps便可以。生成以後,需要運作一次才會設定材質。有個地方需要注意,由于項目出于效率的考慮,Directional Mode需要設定為Non-Directional。

四、代碼

1、PrefabLightmapData.cs

#if UNITY_EDITOR
using UnityEditor;
using System.IO;
#endif
using UnityEngine;
using System.Collections.Generic;
[DisallowMultipleComponent, ExecuteInEditMode]
public class PrefabLightmapData : MonoBehaviour
{
    [System.Serializable]
    struct RendererInfo
    {
        public Renderer renderer;
        public int lightmapIndex;
        public Vector4 lightmapOffsetScale;
    }
    [System.Serializable]
    struct TerrainInfo
    {
        public int lightmapIndex;
        public Vector4 lightmapOffsetScale;
    }

    [System.Serializable]
    public struct FogInfo
    {
        public bool fog;
        public FogMode fogMode;
        public Color fogColor;
        public float fogStartDistance;
        public float fogEndDistance;
        public float fogDensity;
    } 

    [SerializeField]
    TerrainInfo[] m_TerrainInfo;
    [SerializeField]
    RendererInfo[] m_RendererInfo;
    [SerializeField]
    Texture2D[] m_Lightmaps;
    [SerializeField]
    Texture2D[] m_Lightmaps2;
    [SerializeField]
    FogInfo m_FogInfo; 

    const string LIGHTMAP_RESOURCE_PATH = "Assets/Resources/Lightmaps/";

    [System.Serializable]
    struct Texture2D_Remap
    {
        public int originalLightmapIndex;
        public Texture2D originalLightmap;
        public Texture2D lightmap0;
        public Texture2D lightmap1;
    }

    static List<Texture2D_Remap> sceneLightmaps = new List<Texture2D_Remap>();

    void Awake()
    {
        ApplyLightmaps(m_RendererInfo, m_TerrainInfo, m_Lightmaps, m_Lightmaps2,m_FogInfo);

        for (int i = ; i < m_TerrainInfo.Length; i++)
        {
            var info = m_TerrainInfo[i];
            TerrainToMeshData terrianMeshData = GetComponentInChildren<TerrainToMeshData>();
            if (terrianMeshData)
                terrianMeshData.setMaterial(info.lightmapIndex);
        }
    }

    static void ApplyLightmaps(RendererInfo[] rendererInfo, TerrainInfo[] terrainInfo, Texture2D[] lightmaps, Texture2D[] lightmaps2, FogInfo fogInfo)
    {
        for (int i = ; i < rendererInfo.Length; i++)
        {
            var info = rendererInfo[i];
            info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
            info.renderer.lightmapIndex = info.lightmapIndex;
        }

        LightmapData[] combinedLightmaps2 = new LightmapData[lightmaps.Length];
        for (int i = ; i < lightmaps.Length; i++)
        {
            combinedLightmaps2[i] = new LightmapData();
            combinedLightmaps2[i].lightmapFar = lightmaps[i];
            combinedLightmaps2[i].lightmapNear = lightmaps2[i];
        }
        LightmapSettings.lightmaps = combinedLightmaps2;

        RenderSettings.fog = fogInfo.fog;
        RenderSettings.fogMode = fogInfo.fogMode;
        RenderSettings.fogColor = fogInfo.fogColor;
        RenderSettings.fogStartDistance = fogInfo.fogStartDistance;
        RenderSettings.fogEndDistance = fogInfo.fogEndDistance;
        RenderSettings.fogDensity = fogInfo.fogDensity;

        LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;

        //bool existsAlready = false;
        //int counter = ;
        //int[] lightmapArrayOffsetIndex;

        //if (rendererInfo == null || rendererInfo.Length == )
        //    return;

        //LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        //var settingslightmaps = LightmapSettings.lightmaps;
        //var combinedLightmaps = new List<LightmapData>();
        //lightmapArrayOffsetIndex = new int[lightmaps.Length];

        //for (int i = ; i < lightmaps.Length; i++)
        //{
        //    existsAlready = false;
        //    for (int j = ; j < settingslightmaps.Length; j++)
        //    {
        //        if (lightmaps[i] == settingslightmaps[j].lightmapFar)
        //        {
        //            lightmapArrayOffsetIndex[i] = j;
        //            existsAlready = true;
        //        }
        //    }

        //    if (!existsAlready)
        //    {
        //        lightmapArrayOffsetIndex[i] = counter + settingslightmaps.Length;
        //        var newLightmapData = new LightmapData();
        //        newLightmapData.lightmapFar = lightmaps[i];
        //        newLightmapData.lightmapNear = lightmaps2[i];
        //        combinedLightmaps.Add(newLightmapData);
        //        ++counter;
        //    }
        //}

        //var combinedLightmaps2 = new LightmapData[settingslightmaps.Length + counter];
        //settingslightmaps.CopyTo(combinedLightmaps2, );

        //if (counter > )
        //{
        //    for (int i = ; i < combinedLightmaps.Count; i++)
        //    {
        //        combinedLightmaps2[i + settingslightmaps.Length] = new LightmapData();
        //        combinedLightmaps2[i + settingslightmaps.Length].lightmapFar = combinedLightmaps[i].lightmapFar;
        //        combinedLightmaps2[i + settingslightmaps.Length].lightmapNear = combinedLightmaps[i].lightmapNear;
        //    }
        //}

        //ApplyRendererInfo(rendererInfo, lightmapArrayOffsetIndex);

        //LightmapSettings.lightmaps = combinedLightmaps2;
    }

    static void ApplyRendererInfo(RendererInfo[] infos, int[] arrayOffsetIndex)
    {
        for (int i = ; i < infos.Length; i++)
        {
            var info = infos[i];
            info.renderer.lightmapIndex = arrayOffsetIndex[info.lightmapIndex];
            info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
        }
    }

#if UNITY_EDITOR
    [MenuItem("Bake/Update Scene with Prefab Lightmaps")]
    static void UpdateLightmaps()
    {
        PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();

        foreach (var instance in prefabs)
        {
            ApplyLightmaps(instance.m_RendererInfo, instance.m_TerrainInfo, instance.m_Lightmaps, instance.m_Lightmaps2, instance.m_FogInfo);
        }

        Debug.Log("Prefab lightmaps updated");
    }

    [MenuItem("Bake/Bake Prefab Lightmaps")]
    static void GenerateLightmapInfo()
    {
        Debug.ClearDeveloperConsole();

        if (Lightmapping.giWorkflowMode != Lightmapping.GIWorkflowMode.OnDemand)
        {
            Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
            return;
        }

        Lightmapping.Bake();

        string lightMapPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), LIGHTMAP_RESOURCE_PATH);

        if (!Directory.Exists(lightMapPath))
            Directory.CreateDirectory(lightMapPath);

        sceneLightmaps = new List<Texture2D_Remap>();

        //var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
        var sceneName = Path.GetFileNameWithoutExtension(EditorApplication.currentScene);
        var resourcePath = LIGHTMAP_RESOURCE_PATH + sceneName;
        var scenePath = System.IO.Path.GetDirectoryName(EditorApplication.currentScene) + "/" + sceneName + "/";

        PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
        foreach (var instance in prefabs)
        {
            var gameObject = instance.gameObject;
            var rendererInfos = new List<RendererInfo>();
            var terrainInfo = new List<TerrainInfo>();

            var lightmaps = new List<Texture2D>();
            var lightmaps2 = new List<Texture2D>();
            GenerateLightmapInfo(scenePath, resourcePath, gameObject, rendererInfos, terrainInfo, lightmaps, lightmaps2);

            instance.m_RendererInfo = rendererInfos.ToArray();
            instance.m_Lightmaps = lightmaps.ToArray();
            instance.m_Lightmaps2 = lightmaps2.ToArray();
            instance.m_TerrainInfo = terrainInfo.ToArray();

            instance.m_FogInfo = new FogInfo();
            instance.m_FogInfo.fog = RenderSettings.fog;
            instance.m_FogInfo.fogMode = RenderSettings.fogMode;
            instance.m_FogInfo.fogColor = RenderSettings.fogColor;
            instance.m_FogInfo.fogStartDistance = RenderSettings.fogStartDistance;
            instance.m_FogInfo.fogEndDistance = RenderSettings.fogEndDistance;

            var targetPrefab = PrefabUtility.GetPrefabParent(gameObject) as GameObject;
            if (targetPrefab != null)
            {
                //Prefab
                PrefabUtility.ReplacePrefab(gameObject, targetPrefab);
            }

            ApplyLightmaps(instance.m_RendererInfo, instance.m_TerrainInfo, instance.m_Lightmaps, instance.m_Lightmaps2, instance.m_FogInfo);
        }

        Debug.Log("Update to prefab lightmaps finished");
    }

    static void GenerateLightmapInfo(string scenePath, string resourcePath, GameObject root, List<RendererInfo> rendererInfos, List<TerrainInfo> terrainInfo, List<Texture2D> lightmaps, List<Texture2D> lightmaps2)
    {
        var renderers = root.GetComponentsInChildren<Renderer>();
        foreach (Renderer renderer in renderers)
        {
            if (renderer.lightmapIndex != -)
            {
                RendererInfo info = new RendererInfo();
                info.renderer = renderer;
                info.lightmapOffsetScale = renderer.lightmapScaleOffset;

                Texture2D lightmap = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapFar;
                Texture2D lightmap2 = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapNear;
                int sceneLightmapIndex = AddLightmap(scenePath, resourcePath, renderer.lightmapIndex, lightmap, lightmap2);

                info.lightmapIndex = lightmaps.IndexOf(sceneLightmaps[sceneLightmapIndex].lightmap0);
                if (info.lightmapIndex == -)
                {
                    info.lightmapIndex = lightmaps.Count;
                    lightmaps.Add(sceneLightmaps[sceneLightmapIndex].lightmap0);
                    lightmaps2.Add(sceneLightmaps[sceneLightmapIndex].lightmap1);
                }

                rendererInfos.Add(info);
            }
        }

        Terrain terrain = root.GetComponentInChildren<Terrain>();
        if (terrain != null)
        {
            TerrainInfo info = new TerrainInfo();
            info.lightmapOffsetScale = terrain.lightmapScaleOffset;

            Texture2D lightmap = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapFar;
            Texture2D lightmap2 = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapNear;
            int sceneLightmapIndex = AddLightmap(scenePath, resourcePath, terrain.lightmapIndex, lightmap, lightmap2);

            info.lightmapIndex = lightmaps.IndexOf(sceneLightmaps[sceneLightmapIndex].lightmap0);
            if (info.lightmapIndex == -)
            {
                info.lightmapIndex = lightmaps.Count;
                lightmaps.Add(sceneLightmaps[sceneLightmapIndex].lightmap0);
                lightmaps2.Add(sceneLightmaps[sceneLightmapIndex].lightmap1);
            }

            terrainInfo.Add(info);

            //添加一個game object,挂載一個轉換mesh的腳本。
            GameObject terrainScriptObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
            GameObject.DestroyImmediate(terrainScriptObj.GetComponent<MeshFilter>());
            GameObject.DestroyImmediate(terrainScriptObj.GetComponent<MeshRenderer>());
            GameObject.DestroyImmediate(terrainScriptObj.GetComponent<BoxCollider>());
            terrainScriptObj.transform.parent = terrain.transform.parent;
            terrainScriptObj.transform.position = terrain.transform.position;
            terrainScriptObj.name = "TerrainMesh";
            TerrainToMeshData terrainToMesh = terrainScriptObj.AddComponent<TerrainToMeshData>();
            TerrainToMeshData.ConvertTerrianToMesh(terrain.gameObject);

            GameObject.DestroyImmediate(terrain.gameObject);
        } 
    }

    static int AddLightmap(string scenePath, string resourcePath, int originalLightmapIndex, Texture2D lightmap, Texture2D lightmap2)
    {
        int newIndex = -;

        for (int i = ; i < sceneLightmaps.Count; i++)
        {
            if (sceneLightmaps[i].originalLightmapIndex == originalLightmapIndex)
            {
                return i;
            }
        }

        if (newIndex == -)
        {
            var lightmap_Remap = new Texture2D_Remap();
            lightmap_Remap.originalLightmapIndex = originalLightmapIndex;
            lightmap_Remap.originalLightmap = lightmap;

            var filename = scenePath + "Lightmap-" + originalLightmapIndex;

            lightmap_Remap.lightmap0 = GetLightmapAsset(filename + "_comp_light.exr", resourcePath + "_light", originalLightmapIndex, lightmap);
            if (lightmap2 != null)
            {
                lightmap_Remap.lightmap1 = GetLightmapAsset(filename + "_comp_dir.exr", resourcePath + "_dir", originalLightmapIndex, lightmap2);
            }
            sceneLightmaps.Add(lightmap_Remap);
            newIndex = sceneLightmaps.Count - ;
        }

        return newIndex;
    }

    static Texture2D GetLightmapAsset(string filename, string resourcePath, int originalLightmapIndex, Texture2D lightmap)
    {
        //AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
        //var importer = AssetImporter.GetAtPath(filename) as TextureImporter;
        //importer.isReadable = true;
        //AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);

        //var assetLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(filename);

        //var assetPath = resourcePath + "-" + originalLightmapIndex + ".asset";
        //var newLightmap = Instantiate<Texture2D>(assetLightmap);

        //AssetDatabase.CreateAsset(newLightmap, assetPath);

        //newLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);

        //importer.isReadable = false;
        //AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);

        var assetPath = resourcePath + "-" + originalLightmapIndex + ".exr";
        AssetDatabase.CopyAsset(filename, assetPath);

        AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
        var importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
        importer.isReadable = true;
        AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
        var assetLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
        importer.isReadable = false;
        AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
        return assetLightmap;
    }
#endif

}
           

2、TerrainToMeshData.cs

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
[DisallowMultipleComponent, ExecuteInEditMode]
public class TerrainToMeshData : MonoBehaviour
{
    [SerializeField]
    Vector3[] m_Vertices;
    [SerializeField]
    Vector3[] m_Normals;    //法線可以不用,因為直接用烘培貼圖。
    [SerializeField]
    Vector2[] m_Uvs;
    [SerializeField]
    Vector2[] m_Uv2s;
    [SerializeField]
    Vector4[] m_AlphasWeight;
    [SerializeField]
    int[] m_Triangles;
    [SerializeField]
    Texture2D[] m_Texture;

    void Start()
    {
        MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
        MeshRenderer meshRender = gameObject.GetComponent<MeshRenderer>();
        if (meshFilter == null)
            meshFilter = gameObject.AddComponent<MeshFilter>();

        if (meshRender == null)
            meshRender = gameObject.AddComponent<MeshRenderer>();


        Mesh mesh = new Mesh();
        mesh.vertices = m_Vertices;
        mesh.uv = m_Uvs;
        mesh.uv2 = m_Uv2s;
        mesh.triangles = m_Triangles;
        mesh.tangents = m_AlphasWeight;
        //mesh.normals = m_Normals;
        mesh.Optimize();

        meshFilter.sharedMesh = mesh;
    }

    public void setMaterial(int lightMapIndex)
    {
        MeshRenderer meshRender = gameObject.GetComponent<MeshRenderer>();
        if (meshRender == null)
            meshRender = gameObject.AddComponent<MeshRenderer>();

        var mat = new Material(Shader.Find("shader/Transparent/TerrainDiffuseLightMap"));
        for (int i = ; i < m_Texture.Length; i++)
        {
            //目前隻支援4張貼圖最多
            if (i > )
                break;
            mat.SetTexture("_Texture" + i, m_Texture[i]);
        }
        mat.SetTexture("_LightMap", LightmapSettings.lightmaps[lightMapIndex].lightmapFar);
        meshRender.material = mat;
    }

    public static void ConvertTerrianToMesh(GameObject _terrainObj = null)
    {
        TerrainToMesh(_terrainObj);
    }

    private static void TerrainToMesh(GameObject _terrainObj)
    {
        var terrainObj = _terrainObj;
        if (terrainObj == null)
        {
            Debug.Log("terrainObj == null");
        }

        var terrain = terrainObj.GetComponent<Terrain>();
        if (terrain == null)
        {
            Debug.Log("terrain == null");
            return;
        }

        var terrainData = terrain.terrainData;
        if (terrainData == null)
        {
            Debug.Log("terrainData == null");
            return;
        }
        //将頂點數稀釋 vertexCountScale*vertexCountScale 倍
        int vertexCountScale = ;
        int w = terrainData.heightmapWidth;
        int h = terrainData.heightmapHeight;
        Vector3 size = terrainData.size;

        float[, ,] alphaMapData = terrainData.GetAlphamaps(, , terrainData.alphamapWidth, terrainData.alphamapHeight);
        Vector3 meshScale = new Vector3(size.x / (w - f) * vertexCountScale, , size.z / (h - f) * vertexCountScale);
        //此處有問題,若每個圖檔大小不一,則出問題。日後改善
        Vector2 uvScale = new Vector2(f / (w - f), f / (h - f)) * vertexCountScale * (size.x / terrainData.splatPrototypes[].tileSize.x);
        Vector2 uv2Scale = new Vector2(f / (w - f), f / (h - f));

        w = (w - ) / vertexCountScale + ;
        h = (h - ) / vertexCountScale + ;
        Vector3[] vertices = new Vector3[w * h];
        Vector2[] uvs = new Vector2[w * h];
        Vector2[] uv2s = new Vector2[w * h];

        // 隻支援4張圖檔,每個頂點每個圖檔所占比重
        Vector4[] alphasWeight = new Vector4[w * h];
        Vector3[] normals = new Vector3[w * h];


        for (int i = ; i < w; i++)
        {
            for (int j = ; j < h; j++)
            {
                int index = j * w + i;
                float z = terrainData.GetHeight(i * vertexCountScale, j * vertexCountScale);
                vertices[index] = Vector3.Scale(new Vector3(i, z, j), meshScale);
                uvs[index] = Vector2.Scale(new Vector2(i, j), uvScale);
                uv2s[index] = Vector2.Scale(new Vector2(i, j), uv2Scale);

                // alpha map
                int i2 = (int)(i * terrainData.alphamapWidth / (w - f));
                int j2 = (int)(j * terrainData.alphamapHeight / (h - f));
                i2 = Mathf.Min(terrainData.alphamapWidth - , i2);
                j2 = Mathf.Min(terrainData.alphamapHeight - , j2);
                var alpha0 = alphaMapData[j2, i2, ];
                var alpha1 = alphaMapData[j2, i2, ];
                var alpha2 = alphaMapData[j2, i2, ];
                var alpha3 = f;
                if (terrainData.splatPrototypes.Length > )
                    alpha3 = alphaMapData[j2, i2, ];
                alphasWeight[index] = new Vector4(alpha0, alpha1, alpha2, alpha3);
            }
        }

        /*
         * 三角形
         *     b       c
         *      *******
         *      *   * *
         *      * *   *
         *      *******
         *     a       d
         */
        int[] triangles = new int[(w - ) * (h - ) * ];
        int triangleIndex = ;
        for (int i = ; i < w - ; i++)
        {
            for (int j = ; j < h - ; j++)
            {
                int a = j * w + i;
                int b = (j + ) * w + i;
                int c = (j + ) * w + i + ;
                int d = j * w + i + ;

                triangles[triangleIndex++] = a;
                triangles[triangleIndex++] = b;
                triangles[triangleIndex++] = c;

                triangles[triangleIndex++] = a;
                triangles[triangleIndex++] = c;
                triangles[triangleIndex++] = d;

                //計算法線
                //var side1 = vertices[b] - vertices[a];
                //var side2 = vertices[c] - vertices[a];
                //var perp = Vector3.Cross(side1, side2);
                //perp /= perp.magnitude;
                //normals[a] = perp;
            }
        }

        Mesh mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.uv = uvs;
        mesh.triangles = triangles;
        //這裡用切線來記錄,但是跟切線毛關系都沒,隻是為了傳給shader
        mesh.tangents = alphasWeight;
        mesh.normals = normals;
        mesh.RecalculateNormals();
        mesh.Optimize();

        TerrainToMeshData[] prefabs = FindObjectsOfType<TerrainToMeshData>();
        prefabs[].m_AlphasWeight = alphasWeight;
        prefabs[].m_Texture = new Texture2D[terrainData.splatPrototypes.Length];
        //prefabs[0].m_Normals = mesh.normals;
        prefabs[].m_Uvs = uvs;
        prefabs[].m_Uv2s = uv2s;
        prefabs[].m_Vertices = vertices;
        prefabs[].m_Triangles = triangles;
        prefabs[].transform.parent = terrainObj.transform.parent;
        prefabs[].transform.position = terrainObj.transform.position;
        prefabs[].gameObject.layer = terrainObj.layer;
        for (int i = ; i < terrainData.splatPrototypes.Length; i++)
        {
            prefabs[].m_Texture[i] = terrainData.splatPrototypes[i].texture;
        }
    }
}

           

3、TerrainDiffuseLightMap.shader

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "shader/Transparent/TerrainDiffuseLightMap" {
    Properties
    {
        _Texture0 ("Texture 1", D) = "white" {}
        _Texture1 ("Texture 2", D) = "white" {}
        _Texture2 ("Texture 3", D) = "white" {}
        _Texture3 ("Texture 4", D) = "white" {}
        _LightMap ("Lightmap (RGB)", D) = "white" {}
    }

    SubShader
    {
        Tags {"RenderType"="Opaque"}
        LOD 

        Pass
        {
            CGPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag

            #include "UnityCG.cginc"

            sampler2D _Texture0;
            sampler2D _Texture1;
            sampler2D _Texture2;
            sampler2D _Texture3;
            sampler2D _LightMap;
            float4 _LightMap_ST;
            float4 _Texture0_ST;
            float4 _Texture1_ST;
            float4 _Texture2_ST;
            float4 _Texture3_ST;

            struct VertexData
            {
                float4 Pos : POSITION;
                float4 Tangent : TANGENT;
                float2 uv : TEXCOORD0;
                float2 uv2 : TEXCOORD1;
            };

            struct V2F
            {
                float4 Pos : SV_POSITION;
                float4 color : COLOR;
                float2 uv : TEXCOORD0; 
                float2 uv2 : TEXCOORD1;
            };

            V2F Vert(VertexData v)
            {
                V2F o;
                o.Pos = mul(UNITY_MATRIX_MVP, v.Pos);
                o.color = v.Tangent;
                o.uv = v.uv;
                o.uv2 = v.uv2;
                return o;
            }

            float4 Frag(V2F i) : COLOR
            {
                float4 t0 = tex2D(_Texture0, TRANSFORM_TEX(i.uv, _Texture0));
                float4 t1 = tex2D(_Texture1, TRANSFORM_TEX(i.uv, _Texture1));
                float4 t2 = tex2D(_Texture2, TRANSFORM_TEX(i.uv, _Texture2));
                float4 t3 = tex2D(_Texture3, TRANSFORM_TEX(i.uv, _Texture3));
                fixed4 texcolor = t0 * i.color.x + t1 * i.color.y + t2 * i.color.z + t3 * i.color.w;
                UNITY_OPAQUE_ALPHA(texcolor.a);

                texcolor.rgb *= DecodeLightmap(tex2D(_LightMap, TRANSFORM_TEX(i.uv2, _LightMap)));  
                return texcolor;
            }

            ENDCG
        }
    } 
    FallBack "Diffuse"
}
           

繼續閱讀