using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Experimental.Rendering; using System.Text.RegularExpressions; #if UNITY_EDITOR using UnityEditor.SceneManagement; #endif namespace UnityEngine.Rendering.HighDefinition { /// /// Various utility functions for HDRP. /// public class HDUtils { internal const PerObjectData k_RendererConfigurationBakedLighting = PerObjectData.LightProbe | PerObjectData.Lightmaps | PerObjectData.LightProbeProxyVolume; internal const PerObjectData k_RendererConfigurationBakedLightingWithShadowMask = k_RendererConfigurationBakedLighting | PerObjectData.OcclusionProbe | PerObjectData.OcclusionProbeProxyVolume | PerObjectData.ShadowMask; /// Returns the render configuration for baked static lighting, this value can be used in a RendererListDesc call to render Lit objects. public static PerObjectData GetBakedLightingRenderConfig() => k_RendererConfigurationBakedLighting; /// Returns the render configuration for baked static lighting with shadow masks, this value can be used in a RendererListDesc call to render Lit objects when shadow masks are enabled. public static PerObjectData GetBakedLightingWithShadowMaskRenderConfig() => k_RendererConfigurationBakedLightingWithShadowMask; /// Default HDAdditionalReflectionData static internal HDAdditionalReflectionData s_DefaultHDAdditionalReflectionData { get { return ComponentSingleton.instance; } } /// Default HDAdditionalLightData static internal HDAdditionalLightData s_DefaultHDAdditionalLightData { get { return ComponentSingleton.instance; } } /// Default HDAdditionalCameraData static internal HDAdditionalCameraData s_DefaultHDAdditionalCameraData { get { return ComponentSingleton.instance; } } static List m_TempCustomPassVolumeList = new List(); static Texture3D m_ClearTexture3D; static RTHandle m_ClearTexture3DRTH; /// /// Default 1x1x1 3D texture initialized with Color.clear. /// public static Texture3D clearTexture3D { get { if (m_ClearTexture3D == null) { m_ClearTexture3D = new Texture3D(1, 1, 1, TextureFormat.ARGB32, false) { name = "Transparent Texture 3D" }; m_ClearTexture3D.SetPixel(0, 0, 0, Color.clear); m_ClearTexture3D.Apply(); RTHandles.Release(m_ClearTexture3DRTH); m_ClearTexture3DRTH = null; } return m_ClearTexture3D; } } /// /// Default 1x1x1 3D RTHandle initialized with Color.clear. /// public static RTHandle clearTexture3DRTH { get { if (m_ClearTexture3DRTH == null || m_ClearTexture3D == null) // Need to check regular texture as the RTHandle won't null out on domain reload { RTHandles.Release(m_ClearTexture3DRTH); m_ClearTexture3DRTH = RTHandles.Alloc(clearTexture3D); } return m_ClearTexture3DRTH; } } /// /// Returns the HDRP default blit material. /// /// Dimension of the texture to blit, either 2D or 2D Array. /// Blit only a single slice of the array if applicable. /// public static Material GetBlitMaterial(TextureDimension dimension, bool singleSlice = false) { HDRenderPipeline hdPipeline = RenderPipelineManager.currentPipeline as HDRenderPipeline; if (hdPipeline != null) { return hdPipeline.GetBlitMaterial(dimension == TextureDimension.Tex2DArray, singleSlice); } return null; } /// /// Current HDRP settings. /// public static RenderPipelineSettings hdrpSettings { get { return HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings; } } static MaterialPropertyBlock s_PropertyBlock = new MaterialPropertyBlock(); internal static List GetRenderPipelineMaterialList() { var baseType = typeof(RenderPipelineMaterial); var assembly = baseType.Assembly; var types = assembly.GetTypes() .Where(t => t.IsSubclassOf(baseType)) .Select(Activator.CreateInstance) .Cast() .ToList(); // Note: If there is a need for an optimization in the future of this function, user can // simply fill the materialList manually by commenting the code abode and returning a // custom list of materials they use in their game. // // return new List // { // new Lit(), // new Unlit(), // ... // }; return types; } internal static int GetRuntimeDebugPanelWidth(HDCamera hdCamera) { // 600 is the panel size from 'DebugUI Panel' prefab + 10 pixels of padding int width = DebugManager.instance.displayRuntimeUI ? 610 : 0; return Math.Min(hdCamera.actualWidth, width); } /// Get the aspect ratio of a projection matrix. /// /// internal static float ProjectionMatrixAspect(in Matrix4x4 matrix) => - matrix.m11 / matrix.m00; internal static Matrix4x4 ComputePixelCoordToWorldSpaceViewDirectionMatrix(float verticalFoV, Vector2 lensShift, Vector4 screenSize, Matrix4x4 worldToViewMatrix, bool renderToCubemap, float aspectRatio = -1, bool isOrthographic = false) { Matrix4x4 viewSpaceRasterTransform; if (isOrthographic) { // For ortho cameras, project the skybox with no perspective // the same way as builtin does (case 1264647) viewSpaceRasterTransform = new Matrix4x4( new Vector4(-2.0f * screenSize.z, 0.0f, 0.0f, 0.0f), new Vector4(0.0f, -2.0f * screenSize.w, 0.0f, 0.0f), new Vector4(1.0f, 1.0f, -1.0f, 0.0f), new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); } else { // Compose the view space version first. // V = -(X, Y, Z), s.t. Z = 1, // X = (2x / resX - 1) * tan(vFoV / 2) * ar = x * [(2 / resX) * tan(vFoV / 2) * ar] + [-tan(vFoV / 2) * ar] = x * [-m00] + [-m20] // Y = (2y / resY - 1) * tan(vFoV / 2) = y * [(2 / resY) * tan(vFoV / 2)] + [-tan(vFoV / 2)] = y * [-m11] + [-m21] aspectRatio = aspectRatio < 0 ? screenSize.x * screenSize.w : aspectRatio; float tanHalfVertFoV = Mathf.Tan(0.5f * verticalFoV); // Compose the matrix. float m21 = (1.0f - 2.0f * lensShift.y) * tanHalfVertFoV; float m11 = -2.0f * screenSize.w * tanHalfVertFoV; float m20 = (1.0f - 2.0f * lensShift.x) * tanHalfVertFoV * aspectRatio; float m00 = -2.0f * screenSize.z * tanHalfVertFoV * aspectRatio; if (renderToCubemap) { // Flip Y. m11 = -m11; m21 = -m21; } viewSpaceRasterTransform = new Matrix4x4(new Vector4(m00, 0.0f, 0.0f, 0.0f), new Vector4(0.0f, m11, 0.0f, 0.0f), new Vector4(m20, m21, -1.0f, 0.0f), new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); } // Remove the translation component. var homogeneousZero = new Vector4(0, 0, 0, 1); worldToViewMatrix.SetColumn(3, homogeneousZero); // Flip the Z to make the coordinate system left-handed. worldToViewMatrix.SetRow(2, -worldToViewMatrix.GetRow(2)); // Transpose for HLSL. return Matrix4x4.Transpose(worldToViewMatrix.transpose * viewSpaceRasterTransform); } internal static float ComputZPlaneTexelSpacing(float planeDepth, float verticalFoV, float resolutionY) { float tanHalfVertFoV = Mathf.Tan(0.5f * verticalFoV); return tanHalfVertFoV * (2.0f / resolutionY) * planeDepth; } /// /// Blit a texture using a quad in the current render target. /// /// Command buffer used for rendering. /// Source texture. /// Scale and bias for the input texture. /// Scale and bias for the output texture. /// Mip level to blit. /// Enable bilinear filtering. public static void BlitQuad(CommandBuffer cmd, Texture source, Vector4 scaleBiasTex, Vector4 scaleBiasRT, int mipLevelTex, bool bilinear) { s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBiasTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBiasRt, scaleBiasRT); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevelTex); cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), bilinear ? 3 : 2, MeshTopology.Quads, 4, 1, s_PropertyBlock); } /// /// Blit a texture using a quad in the current render target. /// /// Command buffer used for rendering. /// Source texture. /// Source texture size. /// Scale and bias for sampling the input texture. /// Scale and bias for the output texture. /// Mip level to blit. /// Enable bilinear filtering. /// Padding in pixels. public static void BlitQuadWithPadding(CommandBuffer cmd, Texture source, Vector2 textureSize, Vector4 scaleBiasTex, Vector4 scaleBiasRT, int mipLevelTex, bool bilinear, int paddingInPixels) { s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBiasTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBiasRt, scaleBiasRT); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevelTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitTextureSize, textureSize); s_PropertyBlock.SetInt(HDShaderIDs._BlitPaddingSize, paddingInPixels); if (source.wrapMode == TextureWrapMode.Repeat) cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), bilinear ? 7 : 6, MeshTopology.Quads, 4, 1, s_PropertyBlock); else cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), bilinear ? 5 : 4, MeshTopology.Quads, 4, 1, s_PropertyBlock); } /// /// Blit a texture using a quad in the current render target, by performing an alpha blend with the existing content on the render target. /// /// Command buffer used for rendering. /// Source texture. /// Source texture size. /// Scale and bias for sampling the input texture. /// Scale and bias for the output texture. /// Mip level to blit. /// Enable bilinear filtering. /// Padding in pixels. public static void BlitQuadWithPaddingMultiply(CommandBuffer cmd, Texture source, Vector2 textureSize, Vector4 scaleBiasTex, Vector4 scaleBiasRT, int mipLevelTex, bool bilinear, int paddingInPixels) { s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBiasTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBiasRt, scaleBiasRT); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevelTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitTextureSize, textureSize); s_PropertyBlock.SetInt(HDShaderIDs._BlitPaddingSize, paddingInPixels); if (source.wrapMode == TextureWrapMode.Repeat) cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), bilinear ? 12 : 11, MeshTopology.Quads, 4, 1, s_PropertyBlock); else cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), bilinear ? 10 : 9, MeshTopology.Quads, 4, 1, s_PropertyBlock); } /// /// Blit a texture (which is a Octahedral projection) using a quad in the current render target. /// /// Command buffer used for rendering. /// Source texture. /// Source texture size. /// Scale and bias for sampling the input texture. /// Scale and bias for the output texture. /// Mip level to blit. /// Enable bilinear filtering. /// Padding in pixels. public static void BlitOctahedralWithPadding(CommandBuffer cmd, Texture source, Vector2 textureSize, Vector4 scaleBiasTex, Vector4 scaleBiasRT, int mipLevelTex, bool bilinear, int paddingInPixels) { s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBiasTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBiasRt, scaleBiasRT); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevelTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitTextureSize, textureSize); s_PropertyBlock.SetInt(HDShaderIDs._BlitPaddingSize, paddingInPixels); cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), 8, MeshTopology.Quads, 4, 1, s_PropertyBlock); } /// /// Blit a texture (which is a Octahedral projection) using a quad in the current render target, by performing an alpha blend with the existing content on the render target. /// /// Command buffer used for rendering. /// Source texture. /// Source texture size. /// Scale and bias for sampling the input texture. /// Scale and bias for the output texture. /// Mip level to blit. /// Enable bilinear filtering. /// Padding in pixels. public static void BlitOctahedralWithPaddingMultiply(CommandBuffer cmd, Texture source, Vector2 textureSize, Vector4 scaleBiasTex, Vector4 scaleBiasRT, int mipLevelTex, bool bilinear, int paddingInPixels) { s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBiasTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBiasRt, scaleBiasRT); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevelTex); s_PropertyBlock.SetVector(HDShaderIDs._BlitTextureSize, textureSize); s_PropertyBlock.SetInt(HDShaderIDs._BlitPaddingSize, paddingInPixels); cmd.DrawProcedural(Matrix4x4.identity, GetBlitMaterial(source.dimension), 13, MeshTopology.Quads, 4, 1, s_PropertyBlock); } /// /// Blit a RTHandle texture. /// /// Command Buffer used for rendering. /// Source RTHandle. /// Scale and bias for sampling the input texture. /// Mip level to blit. /// Enable bilinear filtering. public static void BlitTexture(CommandBuffer cmd, RTHandle source, Vector4 scaleBias, float mipLevel, bool bilinear) { s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevel); BlitTexture(cmd, source, scaleBias, GetBlitMaterial(TextureXR.dimension), bilinear ? 1 : 0); } /// /// Blit a 2D texture and depth buffer. /// /// Command Buffer used for rendering. /// Source Texture for color. /// Source RenderTexture for depth. /// Scale and bias for sampling the input texture. /// Mip level to blit. /// Enable bilinear filtering. internal static void BlitColorAndDepth(CommandBuffer cmd, Texture sourceColor, RenderTexture sourceDepth, Vector4 scaleBias, float mipLevel, bool blitDepth) { HDRenderPipeline hdPipeline = RenderPipelineManager.currentPipeline as HDRenderPipeline; if (hdPipeline != null) { Material mat = hdPipeline.GetBlitColorAndDepthMaterial(); s_PropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, mipLevel); s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBias); s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, sourceColor); if (blitDepth) s_PropertyBlock.SetTexture(HDShaderIDs._InputDepth, sourceDepth, RenderTextureSubElement.Depth); cmd.DrawProcedural(Matrix4x4.identity, mat, blitDepth ? 1 : 0, MeshTopology.Triangles, 3, 1, s_PropertyBlock); } } /// /// Blit a RTHandle texture /// /// Command Buffer used for rendering. /// Source RTHandle. /// Scale and bias for sampling the input texture. /// Material to invoke when blitting. /// Pass idx within the material to invoke. static void BlitTexture(CommandBuffer cmd, RTHandle source, Vector4 scaleBias, Material material, int pass) { s_PropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, scaleBias); s_PropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source); cmd.DrawProcedural(Matrix4x4.identity, material, pass, MeshTopology.Triangles, 3, 1, s_PropertyBlock); } // In the context of HDRP, the internal render targets used during the render loop are the same for all cameras, no matter the size of the camera. // It means that we can end up rendering inside a partial viewport for one of these "camera space" rendering. // In this case, we need to make sure than when we blit from one such camera texture to another, we only blit the necessary portion corresponding to the camera viewport. // Here, both source and destination are camera-scaled. /// /// Blit a RTHandle to another RTHandle. /// This will properly account for partial usage (in term of resolution) of the texture for the current viewport. /// /// Command Buffer used for rendering. /// Source RTHandle. /// Destination RTHandle. /// Mip level to blit. /// Enable bilinear filtering. public static void BlitCameraTexture(CommandBuffer cmd, RTHandle source, RTHandle destination, float mipLevel = 0.0f, bool bilinear = false) { Vector2 viewportScale = new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y); // Will set the correct camera viewport as well. CoreUtils.SetRenderTarget(cmd, destination); BlitTexture(cmd, source, viewportScale, mipLevel, bilinear); } /// /// Blit a RTHandle to another RTHandle. /// This will properly account for partial usage (in term of resolution) of the texture for the current viewport. /// This overloads allows the user to override the default blit shader /// /// Command Buffer used for rendering. /// Source RTHandle. /// Destination RTHandle. /// The material to use when blitting /// pass to use of the provided material public static void BlitCameraTexture(CommandBuffer cmd, RTHandle source, RTHandle destination, Material material, int pass) { Vector2 viewportScale = new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y); // Will set the correct camera viewport as well. CoreUtils.SetRenderTarget(cmd, destination); BlitTexture(cmd, source, viewportScale, material, pass); } /// /// Blit a RTHandle to another RTHandle. /// This will properly account for partial usage (in term of resolution) of the texture for the current viewport. /// This overload allows user to override the scale and bias used when sampling the input RTHandle. /// /// Command Buffer used for rendering. /// Source RTHandle. /// Destination RTHandle. /// Scale and bias used to sample the input RTHandle. /// Mip level to blit. /// Enable bilinear filtering. public static void BlitCameraTexture(CommandBuffer cmd, RTHandle source, RTHandle destination, Vector4 scaleBias, float mipLevel = 0.0f, bool bilinear = false) { // Will set the correct camera viewport as well. CoreUtils.SetRenderTarget(cmd, destination); BlitTexture(cmd, source, scaleBias, mipLevel, bilinear); } /// /// Blit a RTHandle to another RTHandle. /// This will properly account for partial usage (in term of resolution) of the texture for the current viewport. /// This overload allows user to override the viewport of the destination RTHandle. /// /// Command Buffer used for rendering. /// Source RTHandle. /// Destination RTHandle. /// Viewport of the destination RTHandle. /// Mip level to blit. /// Enable bilinear filtering. public static void BlitCameraTexture(CommandBuffer cmd, RTHandle source, RTHandle destination, Rect destViewport, float mipLevel = 0.0f, bool bilinear = false) { Vector2 viewportScale = new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y); CoreUtils.SetRenderTarget(cmd, destination); cmd.SetViewport(destViewport); BlitTexture(cmd, source, viewportScale, mipLevel, bilinear); } // These method should be used to render full screen triangles sampling auto-scaling RTs. // This will set the proper viewport and UV scale. /// /// Draw a full screen triangle with a material. /// This will automatically set the viewport of the destination RTHandle based on the current camera parameters. /// /// Command Buffer used for rendering. /// Material used for rendering. /// Destination RTHandle. /// Optional material property block. /// Optional pass index to use. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RTHandle colorBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { CoreUtils.SetRenderTarget(commandBuffer, colorBuffer); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draw a full screen triangle with a material. /// This will automatically set the viewport of the destination RTHandle based on the current camera parameters. /// /// Command Buffer used for rendering. /// Material used for rendering. /// Destination RTHandle. /// Destination Depth Stencil RTHandle. /// Optional material property block. /// Optional pass index to use. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RTHandle colorBuffer, RTHandle depthStencilBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { CoreUtils.SetRenderTarget(commandBuffer, colorBuffer, depthStencilBuffer); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draw a full screen triangle with a material. /// This will automatically set the viewport of the destination RTHandle based on the current camera parameters. /// /// Command Buffer used for rendering. /// Material used for rendering. /// Array of RenderTargetIdentifier for multiple render target rendering. /// Destination Depth Stencil RTHandle. /// Optional material property block. /// Optional pass index to use. public static void DrawFullScreen(CommandBuffer commandBuffer, Material material, RenderTargetIdentifier[] colorBuffers, RTHandle depthStencilBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { CoreUtils.SetRenderTarget(commandBuffer, colorBuffers, depthStencilBuffer); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draw a full screen triangle with a material. /// This will render into the destination texture with the specified viewport. /// /// Command Buffer used for rendering. /// Destination viewport. /// Material used for rendering. /// Destination RenderTargetIdentifier. /// Optional Material Property block. /// Optional pass index to use. /// Optional depth slice to render to. public static void DrawFullScreen(CommandBuffer commandBuffer, Rect viewport, Material material, RenderTargetIdentifier destination, MaterialPropertyBlock properties = null, int shaderPassId = 0, int depthSlice = -1) { CoreUtils.SetRenderTarget(commandBuffer, destination, ClearFlag.None, 0, CubemapFace.Unknown, depthSlice); commandBuffer.SetViewport(viewport); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } /// /// Draw a full screen triangle with a material. /// This will render into the destination texture with the specified viewport. /// /// Command Buffer used for rendering. /// Destination viewport. /// Material used for rendering. /// Destination Depth Stencil RTHandle. /// Destination RenderTargetIdentifier. /// Optional Material Property block. /// Optional pass index to use. public static void DrawFullScreen(CommandBuffer commandBuffer, Rect viewport, Material material, RenderTargetIdentifier destination, RTHandle depthStencilBuffer, MaterialPropertyBlock properties = null, int shaderPassId = 0) { CoreUtils.SetRenderTarget(commandBuffer, destination, depthStencilBuffer, ClearFlag.None, 0, CubemapFace.Unknown, -1); commandBuffer.SetViewport(viewport); commandBuffer.DrawProcedural(Matrix4x4.identity, material, shaderPassId, MeshTopology.Triangles, 3, 1, properties); } // Returns mouse coordinates: (x,y) in pixels and (z,w) normalized inside the render target (not the viewport) internal static Vector4 GetMouseCoordinates(HDCamera camera) { // We request the mouse post based on the type of the camera Vector2 mousePixelCoord = MousePositionDebug.instance.GetMousePosition(camera.screenSize.y, camera.camera.cameraType == CameraType.SceneView); return new Vector4(mousePixelCoord.x, mousePixelCoord.y, RTHandles.rtHandleProperties.rtHandleScale.x * mousePixelCoord.x / camera.screenSize.x, RTHandles.rtHandleProperties.rtHandleScale.y * mousePixelCoord.y / camera.screenSize.y); } // Returns mouse click coordinates: (x,y) in pixels and (z,w) normalized inside the render target (not the viewport) internal static Vector4 GetMouseClickCoordinates(HDCamera camera) { Vector2 mousePixelCoord = MousePositionDebug.instance.GetMouseClickPosition(camera.screenSize.y); return new Vector4(mousePixelCoord.x, mousePixelCoord.y, RTHandles.rtHandleProperties.rtHandleScale.x * mousePixelCoord.x / camera.screenSize.x, RTHandles.rtHandleProperties.rtHandleScale.y * mousePixelCoord.y / camera.screenSize.y); } // This function check if camera is a CameraPreview, then check if this preview is a regular preview (i.e not a preview from the camera editor) internal static bool IsRegularPreviewCamera(Camera camera) { if (camera.cameraType == CameraType.Preview) { camera.TryGetComponent(out var additionalCameraData); return (additionalCameraData == null) || !additionalCameraData.isEditorCameraPreview; } return false; } // We need these at runtime for RenderPipelineResources upgrade internal static string GetHDRenderPipelinePath() => "Packages/com.unity.render-pipelines.high-definition/"; internal static string GetCorePath() => "Packages/com.unity.render-pipelines.core/"; // It returns the previously set RenderPipelineAsset, assetWasFromQuality is true if the current asset was set through the quality settings internal static RenderPipelineAsset SwitchToBuiltinRenderPipeline(out bool assetWasFromQuality) { var graphicSettingAsset = GraphicsSettings.renderPipelineAsset; assetWasFromQuality = false; if (graphicSettingAsset != null) { // Check if the currently used pipeline is the one from graphics settings if (GraphicsSettings.currentRenderPipeline == graphicSettingAsset) { GraphicsSettings.renderPipelineAsset = null; return graphicSettingAsset; } } // If we are here, it means the asset comes from quality settings var assetFromQuality = QualitySettings.renderPipeline; QualitySettings.renderPipeline = null; assetWasFromQuality = true; return assetFromQuality; } // Set the renderPipelineAsset, either on the quality settings if it was unset from there or in GraphicsSettings. // IMPORTANT: RenderPipelineManager.currentPipeline won't be HDRP until a camera.Render() call is made. internal static void RestoreRenderPipelineAsset(bool wasUnsetFromQuality, RenderPipelineAsset renderPipelineAsset) { if (wasUnsetFromQuality) { QualitySettings.renderPipeline = renderPipelineAsset; } else { GraphicsSettings.renderPipelineAsset = renderPipelineAsset; } } internal struct PackedMipChainInfo { public Vector2Int textureSize; public Vector2Int hardwareTextureSize; public int mipLevelCount; public Vector2Int[] mipLevelSizes; public Vector2Int[] mipLevelOffsets; private bool m_OffsetBufferWillNeedUpdate; public void Allocate() { mipLevelOffsets = new Vector2Int[15]; mipLevelSizes = new Vector2Int[15]; m_OffsetBufferWillNeedUpdate = true; } // We pack all MIP levels into the top MIP level to avoid the Pow2 MIP chain restriction. // We compute the required size iteratively. // This function is NOT fast, but it is illustrative, and can be optimized later. public void ComputePackedMipChainInfo(Vector2Int viewportSize) { // No work needed. if (viewportSize == mipLevelSizes[0]) return; bool isHardwareDrsOn = DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled(); hardwareTextureSize = isHardwareDrsOn ? DynamicResolutionHandler.instance.ApplyScalesOnSize(viewportSize) : viewportSize; Vector2 textureScale = isHardwareDrsOn ? new Vector2((float)viewportSize.x / (float)hardwareTextureSize.x, (float)viewportSize.y / (float)hardwareTextureSize.y) : new Vector2(1.0f, 1.0f); mipLevelSizes[0] = hardwareTextureSize; mipLevelOffsets[0] = Vector2Int.zero; int mipLevel = 0; Vector2Int mipSize = hardwareTextureSize; do { mipLevel++; // Round up. mipSize.x = Math.Max(1, (mipSize.x + 1) >> 1); mipSize.y = Math.Max(1, (mipSize.y + 1) >> 1); mipLevelSizes[mipLevel] = mipSize; Vector2Int prevMipBegin = mipLevelOffsets[mipLevel - 1]; Vector2Int prevMipEnd = prevMipBegin + mipLevelSizes[mipLevel - 1]; Vector2Int mipBegin = new Vector2Int(); if ((mipLevel & 1) != 0) // Odd { mipBegin.x = prevMipBegin.x; mipBegin.y = prevMipEnd.y; } else // Even { mipBegin.x = prevMipEnd.x; mipBegin.y = prevMipBegin.y; } mipLevelOffsets[mipLevel] = mipBegin; hardwareTextureSize.x = Math.Max(hardwareTextureSize.x, mipBegin.x + mipSize.x); hardwareTextureSize.y = Math.Max(hardwareTextureSize.y, mipBegin.y + mipSize.y); } while ((mipSize.x > 1) || (mipSize.y > 1)); textureSize = new Vector2Int((int)((float)hardwareTextureSize.x * textureScale.x), (int)((float)hardwareTextureSize.y * textureScale.y)); mipLevelCount = mipLevel + 1; m_OffsetBufferWillNeedUpdate = true; } public ComputeBuffer GetOffsetBufferData(ComputeBuffer mipLevelOffsetsBuffer) { if (m_OffsetBufferWillNeedUpdate) { mipLevelOffsetsBuffer.SetData(mipLevelOffsets); m_OffsetBufferWillNeedUpdate = false; } return mipLevelOffsetsBuffer; } } internal static int DivRoundUp(int x, int y) => (x + y - 1) / y; internal static bool IsQuaternionValid(Quaternion q) => (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]) > float.Epsilon; internal static void CheckRTCreated(RenderTexture rt) { // In some cases when loading a project for the first time in the editor, the internal resource is destroyed. // When used as render target, the C++ code will re-create the resource automatically. Since here it's used directly as an UAV, we need to check manually if (!rt.IsCreated()) rt.Create(); } internal static float ComputeViewportScale(int viewportSize, int bufferSize) { float rcpBufferSize = 1.0f / bufferSize; // Scale by (vp_dim / buf_dim). return viewportSize * rcpBufferSize; } internal static float ComputeViewportLimit(int viewportSize, int bufferSize) { float rcpBufferSize = 1.0f / bufferSize; // Clamp to (vp_dim - 0.5) / buf_dim. return (viewportSize - 0.5f) * rcpBufferSize; } internal static Vector4 ComputeViewportScaleAndLimit(Vector2Int viewportSize, Vector2Int bufferSize) { return new Vector4(ComputeViewportScale(viewportSize.x, bufferSize.x), // Scale(x) ComputeViewportScale(viewportSize.y, bufferSize.y), // Scale(y) ComputeViewportLimit(viewportSize.x, bufferSize.x), // Limit(x) ComputeViewportLimit(viewportSize.y, bufferSize.y)); // Limit(y) } // Note: If you add new platform in this function, think about adding support in IsSupportedBuildTarget() function below internal static bool IsSupportedGraphicDevice(GraphicsDeviceType graphicDevice) { return (graphicDevice == GraphicsDeviceType.Direct3D11 || graphicDevice == GraphicsDeviceType.Direct3D12 || graphicDevice == GraphicsDeviceType.PlayStation4 || graphicDevice == GraphicsDeviceType.PlayStation5 || graphicDevice == GraphicsDeviceType.XboxOne || graphicDevice == GraphicsDeviceType.XboxOneD3D12 || graphicDevice == GraphicsDeviceType.GameCoreXboxOne || graphicDevice == GraphicsDeviceType.GameCoreXboxSeries || graphicDevice == GraphicsDeviceType.Metal || graphicDevice == GraphicsDeviceType.Vulkan // Switch isn't supported currently (19.3) /* || graphicDevice == GraphicsDeviceType.Switch */); } #if UNITY_EDITOR // This function can't be in HDEditorUtils because we need it in HDRenderPipeline.cs (and HDEditorUtils is in an editor asmdef) internal static bool IsSupportedBuildTarget(UnityEditor.BuildTarget buildTarget) { return (buildTarget == UnityEditor.BuildTarget.StandaloneWindows || buildTarget == UnityEditor.BuildTarget.StandaloneWindows64 || buildTarget == UnityEditor.BuildTarget.StandaloneLinux64 || buildTarget == UnityEditor.BuildTarget.Stadia || buildTarget == UnityEditor.BuildTarget.StandaloneOSX || buildTarget == UnityEditor.BuildTarget.WSAPlayer || buildTarget == UnityEditor.BuildTarget.XboxOne || buildTarget == UnityEditor.BuildTarget.GameCoreXboxOne || buildTarget == UnityEditor.BuildTarget.GameCoreXboxSeries || buildTarget == UnityEditor.BuildTarget.PS4 || buildTarget == UnityEditor.BuildTarget.PS5 || // buildTarget == UnityEditor.BuildTarget.iOS || // IOS isn't supported // buildTarget == UnityEditor.BuildTarget.Switch || // Switch isn't supported buildTarget == UnityEditor.BuildTarget.CloudRendering); } internal static bool AreGraphicsAPIsSupported(UnityEditor.BuildTarget target, ref GraphicsDeviceType unsupportedGraphicDevice) { foreach (var graphicAPI in UnityEditor.PlayerSettings.GetGraphicsAPIs(target)) { if (!HDUtils.IsSupportedGraphicDevice(graphicAPI)) { unsupportedGraphicDevice = graphicAPI; return false; } } return true; } internal static OperatingSystemFamily BuildTargetToOperatingSystemFamily(UnityEditor.BuildTarget target) { switch (target) { case UnityEditor.BuildTarget.StandaloneOSX: return OperatingSystemFamily.MacOSX; case UnityEditor.BuildTarget.StandaloneWindows: case UnityEditor.BuildTarget.StandaloneWindows64: return OperatingSystemFamily.Windows; case UnityEditor.BuildTarget.StandaloneLinux64: case UnityEditor.BuildTarget.Stadia: return OperatingSystemFamily.Linux; default: return OperatingSystemFamily.Other; } } internal static bool IsSupportedBuildTargetAndDevice(UnityEditor.BuildTarget activeBuildTarget, out GraphicsDeviceType unsupportedGraphicDevice) { unsupportedGraphicDevice = SystemInfo.graphicsDeviceType; // If the build target matches the operating system of the editor // and if the graphic api is chosen automatically, then only the system's graphic device type matters // otherwise, we need to iterate over every graphic api available in the list to track every non-supported APIs // if the build target does not match the editor OS, then we have to check using the graphic api list bool autoAPI = UnityEditor.PlayerSettings.GetUseDefaultGraphicsAPIs(activeBuildTarget) && (SystemInfo.operatingSystemFamily == HDUtils.BuildTargetToOperatingSystemFamily(activeBuildTarget)); if (autoAPI ? HDUtils.IsSupportedGraphicDevice(SystemInfo.graphicsDeviceType) : HDUtils.AreGraphicsAPIsSupported(activeBuildTarget, ref unsupportedGraphicDevice) && HDUtils.IsSupportedBuildTarget(activeBuildTarget) && HDUtils.IsOperatingSystemSupported(SystemInfo.operatingSystem)) return true; return false; } #endif internal static bool IsMacOSVersionAtLeast(string os, int majorVersion, int minorVersion, int patchVersion) { int startIndex = os.LastIndexOf(" "); var parts = os.Substring(startIndex + 1).Split('.'); int currentMajorVersion = Convert.ToInt32(parts[0]); int currentMinorVersion = Convert.ToInt32(parts[1]); int currentPatchVersion = Convert.ToInt32(parts[2]); if (currentMajorVersion < majorVersion) return false; if (currentMajorVersion > majorVersion) return true; if (currentMinorVersion < minorVersion) return false; if (currentMinorVersion > minorVersion) return true; if (currentPatchVersion < patchVersion) return false; if (currentPatchVersion > patchVersion) return true; return true; } internal static bool IsOperatingSystemSupported(string os) { // Metal support depends on OS version: // macOS 10.11.x doesn't have tessellation / earlydepthstencil support, early driver versions were buggy in general // macOS 10.12.x should usually work with AMD, but issues with Intel/Nvidia GPUs. Regardless of the GPU, there are issues with MTLCompilerService crashing with some shaders // macOS 10.13.x should work, but active development tests against current OS // // Has worked experimentally with iOS in the past, but it's not currently supported // if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) { if (os.StartsWith("Mac") && !IsMacOSVersionAtLeast(os, 10, 13, 0)) return false; } return true; } /// /// Extract scale and bias from a fade distance to achieve a linear fading starting at 90% of the fade distance. /// /// Distance at which object should be totally fade /// [OUT] Slope of the fading on the fading part /// [OUT] Ordinate of the fading part at abscissa 0 internal static void GetScaleAndBiasForLinearDistanceFade(float fadeDistance, out float scale, out float bias) { // Fade with distance calculation is just a linear fade from 90% of fade distance to fade distance. 90% arbitrarily chosen but should work well enough. float distanceFadeNear = 0.9f * fadeDistance; scale = 1.0f / (fadeDistance - distanceFadeNear); bias = -distanceFadeNear / (fadeDistance - distanceFadeNear); } /// /// Compute the linear fade distance /// /// Distance from the object to fade from the camera /// Distance at witch the object is totally faded /// Computed fade factor internal static float ComputeLinearDistanceFade(float distanceToCamera, float fadeDistance) { float scale; float bias; GetScaleAndBiasForLinearDistanceFade(fadeDistance, out scale, out bias); return 1.0f - Mathf.Clamp01(distanceToCamera * scale + bias); } /// /// Compute the linear fade distance between two position with an additional weight multiplier /// /// Object/camera position /// Camera/object position /// Weight multiplior /// Distance at witch the object is totally faded /// Computed fade factor internal static float ComputeWeightedLinearFadeDistance(Vector3 position1, Vector3 position2, float weight, float fadeDistance) { float distanceToCamera = Vector3.Magnitude(position1 - position2); float distanceFade = ComputeLinearDistanceFade(distanceToCamera, fadeDistance); return distanceFade * weight; } internal static bool WillCustomPassBeExecuted(HDCamera hdCamera, CustomPassInjectionPoint injectionPoint) { if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.CustomPass)) return false; bool executed = false; CustomPassVolume.GetActivePassVolumes(injectionPoint, m_TempCustomPassVolumeList); foreach (var customPassVolume in m_TempCustomPassVolumeList) { if (customPassVolume == null) return false; executed |= customPassVolume.WillExecuteInjectionPoint(hdCamera); } return executed; } internal static bool PostProcessIsFinalPass(HDCamera hdCamera) { // Post process pass is the final blit only when not in developer mode. // In developer mode, we support a range of debug rendering that needs to occur after post processes. // In order to simplify writing them, we don't Y-flip in the post process pass but add a final blit at the end of the frame. return !Debug.isDebugBuild && !WillCustomPassBeExecuted(hdCamera, CustomPassInjectionPoint.AfterPostProcess); } // These two convertion functions are used to store GUID assets inside materials, // a unity asset GUID is exactly 16 bytes long which is also a Vector4 so by adding a // Vector4 field inside the shader we can store references of an asset inside the material // which is actually used to store the reference of the diffusion profile asset internal static Vector4 ConvertGUIDToVector4(string guid) { Vector4 vector; byte[] bytes = new byte[16]; for (int i = 0; i < 16; i++) bytes[i] = byte.Parse(guid.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); unsafe { fixed(byte* b = bytes) vector = *(Vector4*)b; } return vector; } internal static string ConvertVector4ToGUID(Vector4 vector) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); unsafe { byte* v = (byte*)&vector; for (int i = 0; i < 16; i++) sb.Append(v[i].ToString("x2")); var guidBytes = new byte[16]; System.Runtime.InteropServices.Marshal.Copy((IntPtr)v, guidBytes, 0, 16); } return sb.ToString(); } /// /// Normalize the input color. /// /// Input color. /// Normalized color. public static Color NormalizeColor(Color color) { Vector4 ldrColor = Vector4.Max(color, Vector4.one * 0.0001f); color = (ldrColor / ColorUtils.Luminance(ldrColor)); color.a = 1; return color; } /// /// Draw a renderer list. /// /// Current Scriptable Render Context. /// Command Buffer used for rendering. /// Renderer List to render. [Obsolete("Please use CoreUtils.DrawRendererList instead.")] public static void DrawRendererList(ScriptableRenderContext renderContext, CommandBuffer cmd, RendererList rendererList) { CoreUtils.DrawRendererList(renderContext, cmd, rendererList); } // $"HDProbe RenderCamera ({probeName}: {face:00} for viewer '{viewerName}')" internal unsafe static string ComputeProbeCameraName(string probeName, int face, string viewerName) { // Interpolate the camera name with as few allocation as possible const string pattern1 = "HDProbe RenderCamera ("; const string pattern2 = ": "; const string pattern3 = " for viewer '"; const string pattern4 = "')"; const int maxCharCountPerName = 40; const int charCountPerNumber = 2; probeName = probeName ?? string.Empty; viewerName = viewerName ?? "null"; var probeNameSize = Mathf.Min(probeName.Length, maxCharCountPerName); var viewerNameSize = Mathf.Min(viewerName.Length, maxCharCountPerName); int size = pattern1.Length + probeNameSize + pattern2.Length + charCountPerNumber + pattern3.Length + viewerNameSize + pattern4.Length; var buffer = stackalloc char[size]; var p = buffer; int i, c, s = 0; for (i = 0; i < pattern1.Length; ++i, ++p) *p = pattern1[i]; for (i = 0, c = Mathf.Min(probeName.Length, maxCharCountPerName); i < c; ++i, ++p) *p = probeName[i]; s += c; for (i = 0; i < pattern2.Length; ++i, ++p) *p = pattern2[i]; // Fast, no-GC index.ToString("2") var temp = (face * 205) >> 11; // 205/2048 is nearly the same as /10 *(p++) = (char)(temp + '0'); *(p++) = (char)((face - temp * 10) + '0'); s += charCountPerNumber; for (i = 0; i < pattern3.Length; ++i, ++p) *p = pattern3[i]; for (i = 0, c = Mathf.Min(viewerName.Length, maxCharCountPerName); i < c; ++i, ++p) *p = viewerName[i]; s += c; for (i = 0; i < pattern4.Length; ++i, ++p) *p = pattern4[i]; s += pattern1.Length + pattern2.Length + pattern3.Length + pattern4.Length; return new string(buffer, 0, s); } // $"HDRenderPipeline::Render {cameraName}" internal unsafe static string ComputeCameraName(string cameraName) { // Interpolate the camera name with as few allocation as possible const string pattern1 = "HDRenderPipeline::Render "; const int maxCharCountPerName = 40; var cameraNameSize = Mathf.Min(cameraName.Length, maxCharCountPerName); int size = pattern1.Length + cameraNameSize; var buffer = stackalloc char[size]; var p = buffer; int i, c, s = 0; for (i = 0; i < pattern1.Length; ++i, ++p) *p = pattern1[i]; for (i = 0, c = cameraNameSize; i < c; ++i, ++p) *p = cameraName[i]; s += c; s += pattern1.Length; return new string(buffer, 0, s); } internal static float ClampFOV(float fov) => Mathf.Clamp(fov, 0.00001f, 179); internal static UInt64 GetSceneCullingMaskFromCamera(Camera camera) { #if UNITY_EDITOR if (camera.overrideSceneCullingMask != 0) return camera.overrideSceneCullingMask; if (camera.scene.IsValid()) return EditorSceneManager.GetSceneCullingMask(camera.scene); #if UNITY_2020_1_OR_NEWER switch (camera.cameraType) { case CameraType.SceneView: return SceneCullingMasks.MainStageSceneViewObjects; default: return SceneCullingMasks.GameViewObjects; } #else return 0; #endif #else return 0; #endif } internal static HDAdditionalCameraData TryGetAdditionalCameraDataOrDefault(Camera camera) { if (camera == null || camera.Equals(null)) return s_DefaultHDAdditionalCameraData; if (camera.TryGetComponent(out var hdCamera)) return hdCamera; return s_DefaultHDAdditionalCameraData; } static Dictionary graphicsFormatSizeCache = new Dictionary { // Init some default format so we don't allocate more memory on the first frame. {GraphicsFormat.R8G8B8A8_UNorm, 4}, {GraphicsFormat.R16G16B16A16_SFloat, 8}, {GraphicsFormat.RGB_BC6H_SFloat, 1}, // BC6H uses 128 bits for each 4x4 tile which is 8 bits per pixel }; /// /// Compute the size in bytes of a GraphicsFormat. Does not works with compressed formats. /// /// /// Size in Bytes internal static int GetFormatSizeInBytes(GraphicsFormat format) { if (graphicsFormatSizeCache.TryGetValue(format, out var size)) return size; // Compute the size by parsing the enum name: Note that it does not works with compressed formats string name = format.ToString(); int underscoreIndex = name.IndexOf('_'); name = name.Substring(0, underscoreIndex == -1 ? name.Length : underscoreIndex); // Extract all numbers from the format name: int bits = 0; foreach (Match m in Regex.Matches(name, @"\d+")) bits += int.Parse(m.Value); size = bits / 8; graphicsFormatSizeCache[format] = size; return size; } internal static void DisplayMessageNotification(string msg) { Debug.LogError(msg); #if UNITY_EDITOR foreach (UnityEditor.SceneView sv in UnityEditor.SceneView.sceneViews) sv.ShowNotification(new GUIContent(msg)); #endif } internal static string GetUnsupportedAPIMessage(string graphicAPI) { // If we are in the editor they are many possible targets that does not matches the current OS so we use the active build target instead #if UNITY_EDITOR var buildTarget = UnityEditor.EditorUserBuildSettings.activeBuildTarget; string currentPlatform = buildTarget.ToString(); var osFamily = BuildTargetToOperatingSystemFamily(buildTarget); #else string currentPlatform = SystemInfo.operatingSystem; var osFamily = SystemInfo.operatingSystemFamily; #endif string os = null; switch (osFamily) { case OperatingSystemFamily.MacOSX: os = "Mac"; break; case OperatingSystemFamily.Windows: os = "Windows"; break; case OperatingSystemFamily.Linux: os = "Linux"; break; } string msg = "Platform " + currentPlatform + " with graphics API " + graphicAPI + " is not supported with HDRP"; // Display more information to the users when it should have use Metal instead of OpenGL if (graphicAPI.StartsWith("OpenGL")) { if (SystemInfo.operatingSystem.StartsWith("Mac")) msg += ", use the Metal graphics API instead"; else if (SystemInfo.operatingSystem.StartsWith("Windows")) msg += ", use the Vulkan graphics API instead"; } msg += ".\nChange the platform/device to a compatible one or remove incompatible graphics APIs.\n"; if (os != null) msg += "To do this, go to Project Settings > Player > Other Settings and modify the Graphics APIs for " + os + " list."; return msg; } internal static void ReleaseComponentSingletons() { ComponentSingleton.Release(); ComponentSingleton.Release(); ComponentSingleton.Release(); } } }