u3d honey hex framework 代码解读记录(三)

// 接着上次的baking函数段 -------------------------------------------------------------------------
// 因为各个烘焙阶段都是使用shader来工作的,所以目前看烘焙相关的代码会觉得不是很清楚
// 等到将这个大的流程粗略过完之后,我会回头将每个shader和对应的函数解读一下
// 大概需要等到下下篇文章了

// Offset heights are baked using the same settings 
//small offset allows with comparison find shadow borders ensuring its sharpness
//big offset ensures shadow body and coverage in irregular terrain and data artifacts
// 此时场景中meshrender的设置保持了BakingHeightStage中的设置:
// 材质中的参数:"_MainTex" = terrainType.height;"_Mixer" = terrainType.mixer;"_GlobalMixer" = mixerRT
// 第一次相机偏移少一点,烘焙出来的影子颜色是比较深的,第二次偏移多一点,影子颜色较浅,最后重叠起来后看上去比较自然
bakingCamera.transform.localPosition = lightSourceDirection.normalized * 0.15f;  //small offset for shadow detail
// 烘焙,还是使用Chunk.TextureSize的1/2作为边长,这样rendertexture的尺寸和函数BakingHeightStage中的高度贴图一样大
BakeTo(ref heightRTOffset1, 1);
// 同样做blur操作,平滑变化趋势
BlurTexture(heightRTOffset1, 1, 1, 1);
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// 第2次阴影贴图烘焙
bakingCamera.transform.localPosition = lightSourceDirection.normalized * 0.3f; //higher offset to get shadow body
BakeTo(ref heightRTOffset2, 1);
BlurTexture(heightRTOffset2, 1, 1, 1);
// 恢复baking相机位置,localPosition为0,表示和worldoven坐标一样了
bakingCamera.transform.localPosition = new Vector3(0.0f, 0.0f, 0.0f);
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// 使用三次烘焙的贴图,生成最后阴影和高度贴图
shadowsAndHeightRT = ProduceShadowsAndHeightTexture(heightRT, heightRTOffset1, heightRTOffset2);
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// Bake Diffuse 
// 将六边形的边的meshrender位置调整到摄像机能看得到的地方,这样在下面BakingDiffuseStage的时候就能渲染hex的边了                  
foreach (MeshRenderer mr in hexOutlineCollection)
{
    Vector3 pos = mr.transform.localPosition;
    pos.z = 5;
    mr.transform.localPosition = pos;
}

ReorderHexesPlacingWaterOnTop();
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

BakingDiffuseStage();
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

//turn off everything from camera view
foreach (MeshRenderer mr in quadCollection)
{
    // 释放材质,将GameObject的active设置为false,意味着后面可以重复使用
    if (mr.GetComponent().material != null) { GameObject.Destroy(mr.GetComponent().material); }
    mr.gameObject.SetActive(false);
}
foreach (MeshRenderer mr in hexOutlineCollection)
{
    // 在上面BakingDiffuseStage的时候hex的边已经烘焙完成,所以这里可以不需要了
    mr.gameObject.SetActive(false);
}

// Copy Height to Texture2D (ARGB) because we cant render directly to Alpha8. 
// 高度贴图复制到texture2d上面去
Texture2D texture;
RenderTexture.active = shadowsAndHeightRT;
texture = new Texture2D(Chunk.TextureSize >> 1, Chunk.TextureSize >> 1, TextureFormat.ARGB32, false);
texture.wrapMode = TextureWrapMode.Clamp;
texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize >> 1, Chunk.TextureSize >> 1), 0, 0);
texture.Apply();                

//Convert height to Alpha8, its reasonably cheap and good even uncompressed format. Not compressed format saves us form artifacts near shaped water borders and mountain tops
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }
// 新的高度贴图,使用alpha8的模式
Texture2D gScale = new Texture2D(Chunk.TextureSize >> 1, Chunk.TextureSize >> 1, TextureFormat.Alpha8, false);
gScale.wrapMode = TextureWrapMode.Clamp;
Color32[] data = texture.GetPixels32();

gScale.SetPixels32(data);
gScale.Apply();

gScale.name = "Height" + currentChunk.position;

//if this is chunk refresh we need to destroy old texture soon
if (currentChunk.height != null) currentChunk.texturesForCleanup.Add(currentChunk.height);
// 将此高度贴图赋值给当前chunk。后面获取当前坐标的高度时,实际是将坐标映射到这张图的uv值,然后通过alpha8的值来决定高度
currentChunk.height = gScale;

//source texture will not be used anymore
GameObject.Destroy(texture);
if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// baking 函数段结束 -------------------------------------------------------------------------

/// 
/// Shortcut to do another render with the same settings (camera settings my be different, but scene is the same)
/// 
/// 
/// 
/// 
// 函数很简单,和之前的函数BakingHeightStage最后几条语句是一样的
void BakeTo(ref RenderTexture target, int downscale)
{
    if (target != null) target.Release();

    target = RenderTargetManager.GetNewTexture(Chunk.TextureSize >> downscale, Chunk.TextureSize >> downscale, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 1);
    target.wrapMode = TextureWrapMode.Clamp;
    bakingCamera.targetTexture = target;

    bakingCamera.Render();
}

/// 
/// Setting up and rendering using set of the heights textures producing shadows/light values and moving height to alpha channel for further conversion to Alpha8
/// 
/// 
/// 
/// 
/// 
// 通过sharder来将3个贴图处理成最终的一张贴图,需要结合shader来分析
RenderTexture ProduceShadowsAndHeightTexture(RenderTexture terrainHeight,
                                    RenderTexture terrainHeightOff1,
                                    RenderTexture terrainHeightOff2)
{
    // worldoven对象的material,包含了shader:ShadowsAndHeightShader
    shadowsAndHeightMaterial.SetTexture("_Height", terrainHeight);
    shadowsAndHeightMaterial.SetTexture("_Height1", terrainHeightOff1);
    shadowsAndHeightMaterial.SetTexture("_Height2", terrainHeightOff2);

    // 获取一个新的rendertexture
    RenderTexture rt = RenderTargetManager.GetNewTexture(terrainHeight.width, terrainHeight.height, 0, RenderTextureFormat.ARGB32);
    //simpler baking for older GPUs, in case they have problem with bilinear filtering in render targets
                
    terrainHeight.filterMode = FilterMode.Bilinear;
    terrainHeightOff1.filterMode = FilterMode.Bilinear;
    terrainHeightOff1.filterMode = FilterMode.Bilinear;
    rt.filterMode = FilterMode.Bilinear;
    
    // 通过shader生成最终贴图rt
    Graphics.Blit(null, rt, shadowsAndHeightMaterial, 0);

    // 清空参数
    shadowsAndHeightMaterial.SetTexture("_Height", null);
    shadowsAndHeightMaterial.SetTexture("_Height1", null);
    shadowsAndHeightMaterial.SetTexture("_Height2", null);
    return rt;
}

/// 
/// Functionality which places sea hexes to the top and scales them up. This way they can produce beaches easier
/// 
/// 
void ReorderHexesPlacingWaterOnTop()
{
    // quadCollection包含了当前chunk中所有hex的meshrender
    foreach (MeshRenderer mr in quadCollection)
    {
        // 将GameObject active设为false,这样下面调用GetFreeRenderer的就又把这些GameObject取出来了
        mr.gameObject.SetActive(false);
    }

    Chunk c = currentChunk;
    Rect r = c.GetRect();
    Vector2 center = r.center;

    //we want now sea hexes to be on top of all other hexes. No changes otherwise.
    //for this purpose we will split hexes into two lists. sort them separately and then merge
    // seaType进sea list,其他进 none sea list
    List hexes = c.hexesCovered.Values.ToList();
    List seaHexes = hexes.FindAll(o => o.terrainType.source.seaType);
    List nonSeaHexes = hexes.FindAll(o => !o.terrainType.source.seaType);

    seaHexes = seaHexes.OrderBy(x => x.orderPosition).ToList();
    nonSeaHexes = nonSeaHexes.OrderBy(x => x.orderPosition).ToList();

    // 先插入sea list,再插入non sea list,这样seatype的顺序就在前面了
    hexes.Clear();
    hexes.AddRange(seaHexes);
    hexes.AddRange(nonSeaHexes);

    foreach (KeyValuePair pair in c.hexesCovered)
    {
        // 由于上面将所有GameObject的active设为false,所有又再次取了出来
        MeshRenderer mr = GetFreeRenderer();
        Hex h = pair.Value;
        Vector2 pos = h.GetWorldPosition() - center;

        // 得到排序
        int index = hexes.IndexOf(h);

        if (h.terrainType.source.seaType)
        {
            //expand area covered by water hex so that it can draw better beaches
            // 如果是sea type的GameObject,放大1.25倍
            mr.transform.localScale = Vector3.one * Hex.hexTextureScale * 2f * 1.25f;
        }

        // 顺序(index)作为z轴的参考,越靠后,高度越高,所以陆地都在海洋上面了
        mr.transform.localPosition = new Vector3(pos.x, pos.y, 20 + index * 5);
        // 随机角度
        mr.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, h.rotationAngle);

        // 重新设置hex和meshrender的对应关系
        displayCollection[h] = mr;
    }
}

/// 
/// Preparation and render of the diffuse texture
/// 
/// 
// 需要结合shader来看,除了shader之外,大体和之前的几个bakingstage没有什么区别
void BakingDiffuseStage()
{
    //Hexes
    foreach (KeyValuePair pair in displayCollection)
    {
        if (pair.Value.material != null) { GameObject.Destroy(pair.Value.material); }

        pair.Value.material = diffuseMaterial;
        Material m = pair.Value.material;
        m.SetTexture("_MainTex", pair.Key.terrainType.diffuse);
        m.SetTexture("_Mixer", pair.Key.terrainType.mixer);
        m.SetTexture("_Height", pair.Key.terrainType.height);
        m.SetTexture("_GlobalMixer", mixerRT);
        m.SetTexture("_ShadowsAndHeight", shadowsAndHeightRT);
        m.SetFloat("_Sea", pair.Key.terrainType.source.seaType ? 1f : 0f);
        m.SetFloat("_Centralization", 1.0f);
    }

    //move river smoothener behind camera. It doesn't have diffuse to draw
    GameObject root = GameObject.Find("RiverSmoothener");
    root.transform.localPosition = root.transform.localPosition - Vector3.forward * 20;

    //River
    TerrainDefinition riverDef = TerrainDefinition.definitions.Find(o => o.source.mode == MHTerrain.Mode.IsRiverType);
    Texture riverDiffuse = riverDef.diffuse;
    Texture riverMixer = riverDef.mixer;
    Texture riverHeight = riverDef.height;
    foreach (MeshRenderer river in riverSections)
    {
        if (river.material != null) { GameObject.Destroy(river.material); }

        river.material = diffuseMaterial;
        Material m = river.material;
        m.SetTexture("_MainTex", riverDiffuse);
        m.SetTexture("_Mixer", riverMixer);
        m.SetTexture("_Height", riverHeight);
        m.SetTexture("_GlobalMixer", mixerRT);
        m.SetTexture("_ShadowsAndHeight", shadowsAndHeightRT);
        m.SetFloat("_Sea", 0f);
        m.SetFloat("_Centralization", 0.0f);
    }

    if (diffuseRT != null) diffuseRT.Release();

    diffuseRT = RenderTargetManager.GetNewTexture(Chunk.TextureSize, Chunk.TextureSize, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 1);
    diffuseRT.wrapMode = TextureWrapMode.Clamp;
    bakingCamera.targetTexture = diffuseRT;

    bakingCamera.Render();
}

你可能感兴趣的:(工具)