using System; using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; #if UPPipeline using UnityEngine.Rendering.Universal; #endif #if HDPipeline using UnityEngine.Rendering.HighDefinition; #endif namespace Gaia { /// /// Utility class to provide the functionality of performing an "orthographic bake" from anywhere and return a render texture as result. /// In an orthographic bake an orthographic camera is placed above the terrain pointing straight downwards to render the current view to a render texture. /// public class OrthographicBake { static Camera m_orthoCamera; public static RenderTexture m_tmpRenderTexture; public static GaiaSettings m_gaiaSettings; private static List m_deactivatedLights = new List(); private static GameObject m_bakeDirectionalLight; public static int m_HDLODBiasOverride = 1; public static Camera CreateOrthoCam(Vector3 position, float nearClipping, float farClipping, float size, LayerMask cullingMask) { //existing ortho cam? Try to recycle GameObject gameObject = GameObject.Find("OrthoCaptureCam"); if (gameObject == null) { gameObject = new GameObject("OrthoCaptureCam"); } gameObject.transform.position = position; //facing straight downwards gameObject.transform.rotation = Quaternion.Euler(90f, 0f, 0f); //existing Camera? Try to recycle Camera cam = gameObject.GetComponent(); if (cam == null) { cam = gameObject.AddComponent(); } //setup camera the way we need it for the ortho bake - adjust everything to default to make sure there is no interference cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = Color.black; cam.cullingMask = cullingMask; cam.orthographic = true; cam.orthographicSize = size; cam.nearClipPlane = nearClipping; cam.farClipPlane = farClipping; cam.rect = new Rect(0f, 0f, 1f, 1f); cam.depth = 0f; cam.renderingPath = RenderingPath.Forward; //Forward rendering required for orthographic cam.useOcclusionCulling = true; #if UPPipeline if (m_gaiaSettings != null) { UniversalAdditionalCameraData UPAdditionalCameraData = cam.GetUniversalAdditionalCameraData(); UPAdditionalCameraData.SetRenderer(m_gaiaSettings.m_URPOrthoBakeRendererIndex); } #endif #if HDPipeline HDAdditionalCameraData hdData = gameObject.GetComponent(); if (hdData == null) { hdData = cam.gameObject.AddComponent(); } hdData.volumeLayerMask = 0; hdData.backgroundColorHDR = Color.black; hdData.clearColorMode = HDAdditionalCameraData.ClearColorMode.Color; FrameSettings frameSettings = new FrameSettings(); frameSettings.lodBiasMode = LODBiasMode.OverrideQualitySettings; frameSettings.lodBias = m_HDLODBiasOverride; hdData.customRenderingSettings = true; hdData.renderingPathCustomFrameSettings = frameSettings; hdData.renderingPathCustomFrameSettingsOverrideMask.mask[0] = true; hdData.renderingPathCustomFrameSettingsOverrideMask.mask[(int)FrameSettingsField.LODBiasMode] = true; hdData.renderingPathCustomFrameSettingsOverrideMask.mask[(int)FrameSettingsField.LODBias] = true; hdData.renderingPathCustomFrameSettingsOverrideMask.mask[(int)FrameSettingsField.PlanarProbe] = true; hdData.renderingPathCustomFrameSettingsOverrideMask.mask[(int)FrameSettingsField.ReflectionProbe] = true; #endif m_orthoCamera = cam; return cam; } public static void RemoveOrthoCam() { if (m_orthoCamera == null) { return; } if (m_orthoCamera.targetTexture != null) { RenderTexture.ReleaseTemporary(m_orthoCamera.targetTexture); //m_orthoCamera.targetTexture = null; } GameObject.DestroyImmediate(m_orthoCamera.gameObject); } public static void BakeTerrain(Terrain terrain, int Xresolution, int Yresolution, LayerMask cullingMask, string path = null) { CreateOrthoCam(terrain.GetPosition() + new Vector3(terrain.terrainData.size.x / 2f, 0f, terrain.terrainData.size.z / 2f), -(terrain.terrainData.size.y + 200f), 1f, terrain.terrainData.size.x / 2f, cullingMask); RenderTextureDescriptor rtDesc = new RenderTextureDescriptor(); rtDesc.autoGenerateMips = true; rtDesc.bindMS = false; rtDesc.colorFormat = RenderTextureFormat.ARGB32; rtDesc.depthBufferBits = 24; rtDesc.dimension = UnityEngine.Rendering.TextureDimension.Tex2D; rtDesc.enableRandomWrite = false; //rtDesc.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R8_SRGB; rtDesc.height = Yresolution; rtDesc.memoryless = RenderTextureMemoryless.None; rtDesc.msaaSamples = 1; rtDesc.sRGB = true; rtDesc.shadowSamplingMode = UnityEngine.Rendering.ShadowSamplingMode.None; rtDesc.useDynamicScale = false; rtDesc.useMipMap = false; rtDesc.volumeDepth = 1; rtDesc.vrUsage = VRTextureUsage.None; rtDesc.width = Xresolution; m_tmpRenderTexture = RenderTexture.GetTemporary(rtDesc); if (path != null) { RenderToPng(path); } else { RenderToTemporary(); } } private static void RenderToTemporary() { if (m_orthoCamera == null) { Debug.LogError("Orthographic Bake: Camera does not exist!"); return; } m_orthoCamera.targetTexture = m_tmpRenderTexture; m_orthoCamera.Render(); //In the SRPs we get a flipped image when rendering from a camera to a render textures, need to flip it on the Y-axis for or purposes #if HDPipeline || UPPipeline Material flipMat = new Material(Shader.Find("Hidden/Gaia/FlipY")); flipMat.SetTexture("_InputTex", m_tmpRenderTexture); RenderTexture buffer = RenderTexture.GetTemporary(m_tmpRenderTexture.descriptor); Graphics.Blit(m_tmpRenderTexture, buffer, flipMat); Graphics.Blit(buffer, m_tmpRenderTexture); RenderTexture.ReleaseTemporary(buffer); #endif RenderTexture.active = m_tmpRenderTexture; } private static void RenderToPng(string path) { RenderToTemporary(); ImageProcessing.WriteRenderTexture(path, m_tmpRenderTexture, GaiaConstants.ImageFileType.Png, TextureFormat.RGBA32); CleanUpRenderTexture(); } /// /// switches off all active lights in the scene and stores the lights in a list to turn them back on later with LightsOn() /// public static void LightsOff() { m_deactivatedLights.Clear(); var allLights = Resources.FindObjectsOfTypeAll(); foreach (Light light in allLights) { if (light.isActiveAndEnabled) { light.enabled = false; m_deactivatedLights.Add(light); } } } /// /// turns all the lights on again that were disabled with LightsOff before /// public static void LightsOn() { foreach (Light light in m_deactivatedLights) { if (light != null) { light.enabled = true; } } } public static void CleanUpRenderTexture() { m_orthoCamera.targetTexture = null; RenderTexture.active = null; RenderTexture.ReleaseTemporary(m_tmpRenderTexture); } /// /// Creates a directional light pointing straight downwards on the y-axis with the given intensity & color. Use this together with LightsOn & LightsOff to better control lighting during the scene. /// /// Intensity for the directional light. /// Color for the directional light. public static void CreateBakeDirectionalLight(float intensity, Color color) { GameObject lightGO = GameObject.Find(GaiaConstants.BakeDirectionalLight); if (lightGO == null) { lightGO = new GameObject(GaiaConstants.BakeDirectionalLight); } m_bakeDirectionalLight = lightGO; Light light = lightGO.GetComponent(); if (light == null) { light = lightGO.AddComponent(); } light.shadows = LightShadows.None; light.type = LightType.Directional; light.transform.rotation = Quaternion.Euler(90, 0, 0); light.intensity = intensity; #if HDPipeline HDAdditionalLightData lightData = light.GetComponent(); if (lightData == null) { lightData = light.gameObject.AddComponent(); } if (lightData != null) { lightData.lightUnit = LightUnit.Lux; lightData.intensity = intensity; } #endif light.color = color; } /// /// Removes the bake directional light (Created with CreateBakeDirectionalLight()) from the scene again. /// public static void RemoveBakeDirectionalLight() { if (m_bakeDirectionalLight != null) { GameObject.DestroyImmediate(m_bakeDirectionalLight); } } } }