using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace _Project.Scripts.LevelGenerating { // ===================================================================== // Запекает освещение комнаты (Direct Spot/Point + Floor Bounce GI + Room Corner AO) // напрямую в RenderTexture каждого меша и выключает реальные компоненты Light! // ===================================================================== public static class RuntimeRoomLightmapBaker { private static Material _bakeMaterial; private static readonly int LightmapTexProp = Shader.PropertyToID("_RuntimeLightmap"); private static readonly Vector4[] LightPositions = new Vector4[16]; private static readonly Vector4[] LightDirections = new Vector4[16]; private static readonly Vector4[] LightColors = new Vector4[16]; public static void BakeRoom(RoomView view, RoomLightConfig config) { if (config == null || !config.bakeToRuntimeLightmap) return; if (_bakeMaterial == null) { var shader = Shader.Find("Hidden/LevelGenerating/UVRoomBaker"); if (shader == null) { Debug.LogError("RuntimeRoomLightmapBaker: не найден шейдер Hidden/LevelGenerating/UVRoomBaker!"); return; } _bakeMaterial = new Material(shader); } // 1. Собираем все источники света в этой комнате (до 16 штук) var lights = view.GetComponentsInChildren(false); int count = Mathf.Min(lights.Length, 16); if (count == 0) return; for (int i = 0; i < count; i++) { var l = lights[i]; Vector3 pos = l.transform.position; Vector3 dir = l.transform.forward; LightPositions[i] = new Vector4(pos.x, pos.y, pos.z, l.range); if (l.type == LightType.Spot) { float cosOuter = Mathf.Cos(l.spotAngle * 0.5f * Mathf.Deg2Rad); float innerAngle = l.innerSpotAngle > 0f ? l.innerSpotAngle : l.spotAngle * 0.8f; float cosInner = Mathf.Cos(innerAngle * 0.5f * Mathf.Deg2Rad); LightDirections[i] = new Vector4(dir.x, dir.y, dir.z, cosOuter); Color c = l.color * l.intensity; LightColors[i] = new Vector4(c.r, c.g, c.b, cosInner); } else // Point Light { LightDirections[i] = new Vector4(0f, -1f, 0f, -2f); // -2 = флаг Point Light Color c = l.color * l.intensity; LightColors[i] = new Vector4(c.r, c.g, c.b, -1f); } } // 2. Передаём параметры комнаты и света в шейдер запекания Vector3 rMin = view.OriginMeters; Vector3 rMax = rMin + view.SizeMeters; _bakeMaterial.SetInt("_LightCount", count); _bakeMaterial.SetVectorArray("_LightPositions", LightPositions); _bakeMaterial.SetVectorArray("_LightDirections", LightDirections); _bakeMaterial.SetVectorArray("_LightColors", LightColors); _bakeMaterial.SetVector("_RoomMin", new Vector4(rMin.x, rMin.y, rMin.z, 0f)); _bakeMaterial.SetVector("_RoomMax", new Vector4(rMax.x, rMax.y, rMax.z, 0f)); _bakeMaterial.SetVector("_BounceAndAmbient", new Vector4( config.ceilingBounceIntensity, config.bakedAmbientIntensity, config.cornerAOStrength, rMin.y)); // 3. Собираем все рендереры комнаты (пол, потолок, стены) var targets = new List(); targets.AddRange(view.FloorRenderers); targets.AddRange(view.CeilingRenderers); targets.AddRange(view.WallRenderers); var cmd = new CommandBuffer { name = $"BakeRoom_{view.Index}" }; var mpb = new MaterialPropertyBlock(); int res = Mathf.Clamp(config.lightmapResolution, 32, 1024); foreach (var r in targets) { if (r == null) continue; var rt = new RenderTexture(res, res, 0, RenderTextureFormat.ARGBHalf) { name = $"LM_{view.name}_{r.name}", filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp }; rt.Create(); view.BakedLightmaps.Add(rt); cmd.SetRenderTarget(rt); cmd.ClearRenderTarget(false, true, Color.black); cmd.DrawRenderer(r, _bakeMaterial, 0, 0); // Назначаем напрямую в инстанс материала (100% работает с любым Shader Graph и SRP Batcher!) r.material.SetTexture(LightmapTexProp, rt); } // Выполняем запекание на GPU мгновенно Graphics.ExecuteCommandBuffer(cmd); cmd.Release(); // 4. САМОЕ ГЛАВНОЕ ДЛЯ FPS: Выключаем реальные источники света после запекания! if (config.disableRealtimeLightsAfterBake) { foreach (var l in lights) { l.enabled = false; } } } } }