using UnityEngine; using UnityEngine.Rendering; using System; using System.Collections.Generic; using UnityEngine.Experimental.Rendering; namespace UnityEditor.TerrainTools { /// /// A utility class for rendering noise defined by NoiseSettings into various Texture types. /// internal static class NoiseUtils { /// /// Number of passes that the builtin noise blit shader contains. The passes include /// one for blitting 2D noise and one for blitting 3D noise. /// public static readonly int kNumBlitPasses = 2; private static bool supportsCopyTexture3D { get { return (SystemInfo.copyTextureSupport & CopyTextureSupport.Copy3D) != 0; } } private static bool supportsCopyTextureRTToTexture { get { return (SystemInfo.copyTextureSupport & CopyTextureSupport.RTToTexture) != 0; } } /// /// Format to use when rendering a Noise field into a single channel RenderTexture /// public static GraphicsFormat singleChannelFormat => #pragma warning disable 0618 SystemInfo.IsFormatSupported(GraphicsFormat.R16_SFloat, FormatUsage.Render) && #pragma warning restore 0618 SystemInfo.graphicsDeviceType != GraphicsDeviceType.Vulkan && SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES3 // return Terrain height format because values will be packed and unpacked on GPU using Terrain height functions ? GraphicsFormat.R16_SFloat : Terrain.heightmapFormat; /// /// Format to use when rendering a preview of a Noise field into a RenderTexture with NoiseUtils.BlitPreview functions /// public static GraphicsFormat previewFormat => GraphicsFormat.R8G8B8A8_UNorm; static NoiseUtils() { } /*========================================================================= Material Getter Functions =========================================================================*/ private static Material s_defaultPreviewMaterial; private static Material GetDefaultPreviewMaterial() { if (s_defaultPreviewMaterial == null) { s_defaultPreviewMaterial = new Material(Shader.Find("Hidden/TerrainTools/Noise/Preview")); } return s_defaultPreviewMaterial; } /// /// Returns a Material reference to the default blit material for the given Type of FractalType. /// /// Usage is: Material mat = GetDefaultBlitMaterial( typeof(FbmFractalType) ); /// A reference to the default blit Material for the specified Type of FractalType /// The Type for a given FractalType public static Material GetDefaultBlitMaterial(Type fractalType) { return NoiseLib.GetGeneratedMaterial(typeof(NoiseBlitShaderGenerator), fractalType); } /// /// Returns a Material reference to the default blit material for the given NoiseSettings object. /// /// /// Internally, this uses noise.domainSettings.fractalTypeName to get it's FractalType /// /// A reference to the default blit Material for the specified NoiseSettings instance public static Material GetDefaultBlitMaterial(NoiseSettings noise) { IFractalType fractal = NoiseLib.GetFractalTypeInstance(noise.domainSettings.fractalTypeName); if (fractal == null) { return null; } return GetDefaultBlitMaterial(fractal.GetType()); } /*========================================================================= Blit Noise using Preview shader =========================================================================*/ /// /// Blits the source RenderTexture into the destination RenderTexture using the default Preview Blit Material. /// This is the blit Material that is used when rendering the NoiseSettings Preview. /// /// The source RenderTexture used in the Blit operation /// /// The destination RenderTexture in the Blit operation /// This will have the noise preview shader logic applied to it. /// public static void BlitPreview2D(RenderTexture source, RenderTexture destination) { BlitPreview2D(source, destination, GetDefaultPreviewMaterial()); } /// /// Blits the source RenderTexture into the destination RenderTexture using the specified Material. /// /// The source RenderTexture used in the Blit operation /// /// The destination RenderTexture in the Blit operation /// This will have the noise preview shader logic applied to it. /// /// The material to be used in the blit operation public static void BlitPreview2D(RenderTexture src, RenderTexture dest, Material mat) { if (mat == null) { Debug.LogError("NoiseUtils::BlitPreview2D: Provided preview material is NULL"); Graphics.Blit(src, dest); return; } Graphics.Blit(src, dest, mat, 0); } /*========================================================================= Blit raw noise data into texture =========================================================================*/ /// /// Blits 2D noise defined by the given NoiseSettings instance into the destination RenderTexture. /// /// An instance of NoiseSettings defining the type of noise to render /// The destination RenderTexture that the noise will be rendered into. public static void Blit2D(NoiseSettings noise, RenderTexture dest) { Material mat = GetDefaultBlitMaterial(noise); if (mat == null) { return; } Blit2D(noise, dest, mat); } /// /// Blits 2D noise defined by the given NoiseSettings instance into the destination RenderTexture /// using the provided Material. /// /// An instance of NoiseSettings defining the type of noise to render /// The destination RenderTexture that the noise will be rendered into. /// The Material to be used for rendering the noise public static void Blit2D(NoiseSettings noise, RenderTexture dest, Material mat) { int pass = NoiseLib.GetNoiseIndex(noise.domainSettings.noiseTypeName); INTERNAL_Blit2D(noise, dest, mat, pass * kNumBlitPasses + 0); } private static void INTERNAL_Blit2D(NoiseSettings noise, RenderTexture dest, Material mat, int pass) { noise.SetupMaterial(mat); var tempRT = RenderTexture.GetTemporary(dest.descriptor); var prev = RenderTexture.active; RenderTexture.active = tempRT; // keep this Graphics.Blit(tempRT, mat, pass); Graphics.Blit(tempRT, dest); RenderTexture.active = prev; RenderTexture.ReleaseTemporary(tempRT); } /// /// Blits 3D noise defined by the given NoiseSettings instance into the destination RenderTexture. /// /// An instance of NoiseSettings defining the type of noise to render /// The destination RenderTexture that the noise will be rendered into. public static void Blit3D(NoiseSettings noise, RenderTexture dest) { throw new NotImplementedException("NoiseUtils::Blit3D: Function not implemented yet"); // Debug.Assert(dest.dimension == UnityEngine.Rendering.TextureDimension.Tex3D, // "NoiseUtils::Blit3D: Provided RenderTexture is not a 3D texture. You have to manually create it as a volume"); // Material mat = GetDefaultBlitMaterial(noise); // if(mat == null) // { // return; // } // int pass = NoiseLib.GetNoiseIndex(noise.domainSettings.noiseTypeName); // RenderTexture prev = RenderTexture.active; // Graphics.SetRenderTarget(dest, 0, CubemapFace.Unknown, kAllSlices); // // Graphics.Blit( dest, mat, ) // RenderTexture.active = prev; } /// /// Bakes 2D noise defined by the given NoiseSettings instance into a Texture2D instance and returns /// a reference to it. /// /// An instance of NoiseSettings defining the type of noise to bake /// The width of the baked Texture2D /// The height of the baked Texture2D /// The GraphicsFormat for the baked Texture2D. In most cases, you will want to use GraphicsFormat.R16_UNorm /// TextureCreation flags for the baked Texture2D /// A reference to the baked Texture2D instance public static Texture2D BakeToTexture2D(NoiseSettings noise, int width, int height, GraphicsFormat format = GraphicsFormat.R16_UNorm, TextureCreationFlags flags = TextureCreationFlags.None) { RenderTexture rt = RenderTexture.GetTemporary(width, height, 0, GraphicsFormat.R16_UNorm); Texture2D texture = new Texture2D(width, height, format, flags); Blit2D(noise, rt); RenderTexture.active = rt; bool mipChain = ((int)flags & (int)TextureCreationFlags.MipChain) != 0; texture.ReadPixels(new Rect(0, 0, width, height), 0, 0, mipChain); RenderTexture.active = null; RenderTexture.ReleaseTemporary(rt); return texture; } /// /// Bakes 3D noise defined by the given NoiseSettings instance into a Texture3D instance and returns /// a reference to it. /// /// An instance of NoiseSettings defining the type of noise to bake /// The width of the baked Texture3D /// The height of the baked Texture3D /// The depth of the baked Texture3D /// The GraphicsFormat for the baked Texture3D. In most cases, you will want to use GraphicsFormat.R16_UNorm /// TextureCreation flags for the baked Texture3D. /// A reference to the baked Texture3D instance /// /// Be careful when specifying TextureCreation flags. If you specify that mipmaps should be generated for /// a Texture3D, that will use a lot more memory than if you were generating mipmaps for a Texture2D. /// public static Texture3D BakeToTexture3D(NoiseSettings noise, int width, int height, int depth, GraphicsFormat format = GraphicsFormat.R16_UNorm, TextureCreationFlags flags = TextureCreationFlags.None) { Material mat = GetDefaultBlitMaterial(noise); if (mat == null) { return null; } RenderTexture sliceRT = RenderTexture.GetTemporary(width, height, 0, GraphicsFormat.R16_UNorm); Texture2D slice2D = new Texture2D(width, height, format, flags); Color[] colors = new Color[width * height * depth]; noise.SetupMaterial(mat); int pass = NoiseLib.GetNoiseIndex(noise); RenderTexture.active = sliceRT; List sliceColors = new List(depth); for (int i = 0; i < depth; ++i) { float uvy = ((float)i + 0.5f) / depth; mat.SetFloat("_UVY", uvy); Graphics.Blit(null, sliceRT, mat, pass * kNumBlitPasses + 1); slice2D.ReadPixels(new Rect(0, 0, width, height), 0, 0); sliceColors.Add(slice2D.GetPixels(0, 0, width, height)); } int pixPerSlice = width * height; for (int sliceID = 0; sliceID < sliceColors.Count; ++sliceID) { for (int pixelID = 0; pixelID < sliceColors[sliceID].Length; ++pixelID) { int pixel = (pixPerSlice * sliceID) + pixelID; colors[pixel] = sliceColors[sliceID][pixelID]; } } bool mipChain = ((int)flags & (int)TextureCreationFlags.MipChain) != 0; Texture3D texture = new Texture3D(width, height, depth, format, flags); texture.SetPixels(colors); texture.Apply(mipChain); RenderTexture.active = null; RenderTexture.ReleaseTemporary(sliceRT); return texture; } } }