#if UNITY_EDITOR using System.IO; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; /// Temporarily force Ground to visualize bakedGI only, to verify lightmap sampling. public static class DiagnoseHomeCampGiViz { const string ScenePath = "Assets/Bundles/Scenes/Town/HomeCamp/HomeCamp.unity"; const string Review = "D:/UD/AI/AIGame/Tools/output/homecamp_review"; const string ShaderPath = "Assets/Bundles/Shaders/XShader/XTerrain.shader"; public static string Run() { Directory.CreateDirectory(Review); var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); var root = scene.GetRootGameObjects().Single(g => g.name == "HomeCamp"); var ground = root.transform.Find("Ground")?.GetComponentInChildren(); if (!ground) return "FAIL: no ground"; var mesh = ground.GetComponent()?.sharedMesh; var uv1 = mesh != null && mesh.uv2 != null && mesh.uv2.Length == mesh.vertexCount; var uv0 = mesh != null ? mesh.uv.Length : 0; // Inject a debug return of bakedGI into XTerrain fragment temporarily via material replacement: // use Unlit with lightmap — simpler: create temp material using URP/Lit which we know samples LM. var lit = new Material(Shader.Find("Universal Render Pipeline/Lit")); lit.SetColor("_BaseColor", Color.white); lit.SetFloat("_Smoothness", 0f); var prev = ground.sharedMaterial; ground.sharedMaterial = lit; var cam = root.GetComponentInChildren(); Capture(cam, Review + "/diag_ground_urp_lit.png"); // Also capture original ground.sharedMaterial = prev; Capture(cam, Review + "/diag_ground_xterrain.png"); // Sample lightmap at ground renderer scaleOffset a few UV points var lm = LightmapSettings.lightmaps[0].lightmapColor; var so = ground.lightmapScaleOffset; string Samples(string label, float u, float v) { // Unity UV: v=0 bottom. Texture2D GetPixel v from bottom if we use GetPixelBilinear with Unity convention... float uu = u * so.x + so.z; float vv = v * so.y + so.w; var rt = RenderTexture.GetTemporary(lm.width, lm.height, 0, RenderTextureFormat.ARGBFloat); Graphics.Blit(lm, rt); var prevRt = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(lm.width, lm.height, TextureFormat.RGBAFloat, false, true); tex.ReadPixels(new Rect(0, 0, lm.width, lm.height), 0, 0); tex.Apply(); RenderTexture.active = prevRt; RenderTexture.ReleaseTemporary(rt); // Texture2D: (0,0) bottom-left in GetPixelBilinear? Actually GetPixelBilinear y=0 is bottom. var c = tex.GetPixelBilinear(uu, vv); Object.DestroyImmediate(tex); float l = c.r * .3f + c.g * .59f + c.b * .11f; return label + "=(" + uu.ToString("F2") + "," + vv.ToString("F2") + ")->" + l.ToString("F3"); } Object.DestroyImmediate(lit); return "uv1=" + uv1 + " uv0Count=" + uv0 + " verts=" + (mesh ? mesh.vertexCount : 0) + " idx=" + ground.lightmapIndex + " so=" + so + " " + Samples("center", .5f, .5f) + " " + Samples("near", .5f, .2f) + " " + Samples("far", .5f, .8f) + " " + Samples("left", .2f, .5f) + " " + Samples("right", .8f, .5f); } static void Capture(Camera camera, string file) { var rt = RenderTexture.GetTemporary(720, 1280, 24, RenderTextureFormat.ARGB32); var old = RenderTexture.active; var oldT = camera.targetTexture; var oldA = camera.aspect; var tex = new Texture2D(720, 1280, TextureFormat.RGB24, false); try { camera.aspect = 720f / 1280f; camera.targetTexture = rt; RenderTexture.active = rt; camera.Render(); tex.ReadPixels(new Rect(0, 0, 720, 1280), 0, 0); tex.Apply(); File.WriteAllBytes(file, tex.EncodeToPNG()); } finally { camera.targetTexture = oldT; camera.aspect = oldA; RenderTexture.active = old; RenderTexture.ReleaseTemporary(rt); Object.DestroyImmediate(tex); } } } #endif