using System; using System.IO; using Unity.Mathematics; using UnityEngine.PathTracing.Core; using UnityEngine.PathTracing.Lightmapping; using UnityEngine.Rendering; using UnityEngine.Rendering.Sampling; using UnityEngine.Rendering.UnifiedRayTracing; namespace UnityEngine.PathTracing.Integration { internal static class UVFallbackBufferBuilderShaderIDs { public static readonly int VertexBuffer = Shader.PropertyToID("g_VertexBuffer"); public static readonly int Width = Shader.PropertyToID("g_Width"); public static readonly int Height = Shader.PropertyToID("g_Height"); public static readonly int WidthScale = Shader.PropertyToID("g_WidthScale"); public static readonly int HeightScale = Shader.PropertyToID("g_HeightScale"); public static readonly int UvFallback = Shader.PropertyToID("g_UvFallback"); public static readonly int InstanceWidth = Shader.PropertyToID("g_InstanceWidth"); public static readonly int InstanceHeight = Shader.PropertyToID("g_InstanceHeight"); public static readonly int InstanceOffsetX = Shader.PropertyToID("g_InstanceOffsetX"); public static readonly int InstanceOffsetY = Shader.PropertyToID("g_InstanceOffsetY"); public static readonly int ChunkOffsetX = Shader.PropertyToID("g_ChunkOffsetX"); public static readonly int ChunkOffsetY = Shader.PropertyToID("g_ChunkOffsetY"); public static readonly int ChunkSize = Shader.PropertyToID("g_ChunkSize"); public static readonly int InstanceWidthScale = Shader.PropertyToID("g_InstanceWidthScale"); public static readonly int InstanceHeightScale = Shader.PropertyToID("g_InstanceHeightScale"); } internal class UVFallbackBufferBuilder : IDisposable { private GraphicsBuffer _vertexBuffer; private Material _uvFallbackBufferMaterial; public void Dispose() { _vertexBuffer?.Dispose(); _vertexBuffer = null; } public void Prepare(Material uvFallbackBufferMaterial) { _uvFallbackBufferMaterial = uvFallbackBufferMaterial; } public void Build( CommandBuffer cmd, RenderTexture uvFallbackRT, int width, int height, float widthScale, float heightScale, Mesh uvMesh) { cmd.BeginSample("Build UVFallbackBuffer"); Debug.Assert((UInt64)width * (UInt64)height < uint.MaxValue); Debug.Assert(uvFallbackRT.format == RenderTextureFormat.RGFloat); Debug.Assert(uvFallbackRT.depth > 0); // Clear the fallback buffer to -1, indicating no valid texels cmd.SetRenderTarget(uvFallbackRT); cmd.ClearRenderTarget(false, true, new Color(-1.0f, -1.0f, -1.0f, -1.0f)); // Expand vertices so we can do conservative rasterization var originalVertices = uvMesh.vertices; var originalIndices = uvMesh.triangles; var vertices = new Vector2[originalIndices.Length]; for (int i = 0; i < originalIndices.Length; i++) { vertices[i] = originalVertices[originalIndices[i]]; } // Reallocate the vertex buffer if necessary, write the vertices if (_vertexBuffer == null || _vertexBuffer.count < vertices.Length) { _vertexBuffer?.Dispose(); _vertexBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, vertices.Length, 2 * sizeof(float)); } cmd.SetBufferData(_vertexBuffer, vertices); // Build the fallback buffer using conservative rasterization cmd.SetGlobalBuffer(UVFallbackBufferBuilderShaderIDs.VertexBuffer, _vertexBuffer); cmd.SetGlobalInteger(UVFallbackBufferBuilderShaderIDs.Width, width); cmd.SetGlobalInteger(UVFallbackBufferBuilderShaderIDs.Height, height); cmd.SetGlobalFloat(UVFallbackBufferBuilderShaderIDs.WidthScale, widthScale); cmd.SetGlobalFloat(UVFallbackBufferBuilderShaderIDs.HeightScale, heightScale); cmd.DrawProcedural(Matrix4x4.identity, _uvFallbackBufferMaterial, 0, MeshTopology.Triangles, (int)uvMesh.GetTotalIndexCount()); cmd.EndSample("Build UVFallbackBuffer"); GraphicsHelpers.Flush(cmd); } } internal class UVFallbackBuffer : IDisposable { public RenderTexture UVFallbackRT; public float WidthScale; public float HeightScale; public int Width => UVFallbackRT.width; public int Height => UVFallbackRT.height; public void Dispose() { UVFallbackRT?.Release(); if (UVFallbackRT != null) CoreUtils.Destroy(UVFallbackRT); UVFallbackRT = null; } public bool Build( CommandBuffer commandBuffer, UVFallbackBufferBuilder builder, int width, int height, UVMesh uvMesh) { if (width == 0 || height == 0) return false; // Assume that the largest edge of UV bounds can fit into the texture, // and calculate the scale factor for each dimension to achieve this. // This is accounting for the aspect ratio of the UV bounds. var uvAspectRatio = uvMesh.UVAspectRatio; if (uvAspectRatio >= 1.0f) // width >= height { WidthScale = 1.0f; HeightScale = (width / uvAspectRatio) / height; } else // width < height { WidthScale = (height * uvAspectRatio) / width; HeightScale = 1.0f; } // If we find that the scaled UV bounds still don't fit into the texture, // then we uniformly scale down the bounds to fit. // This is accounting for the aspect ratio of the texture. if (HeightScale > 1) { WidthScale /= HeightScale; HeightScale = 1.0f; } if (WidthScale > 1) { HeightScale /= WidthScale; WidthScale = 1.0f; } UVFallbackRT = new RenderTexture(width, height, 24, RenderTextureFormat.RGFloat, RenderTextureReadWrite.Linear) { name = "UVFallbackRT", hideFlags = HideFlags.HideAndDontSave, enableRandomWrite = true, useMipMap = false, autoGenerateMips = false, }; UVFallbackRT.Create(); builder.Build(commandBuffer, UVFallbackRT, width, height, WidthScale, HeightScale, uvMesh.Mesh); return true; } public void Bind(CommandBuffer cmd, IRayTracingShader shader, Vector2Int instanceOffset) { shader.SetTextureParam(cmd, UVFallbackBufferBuilderShaderIDs.UvFallback, UVFallbackRT); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceWidth, UVFallbackRT.width); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceHeight, UVFallbackRT.height); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceOffsetX, instanceOffset.x); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceOffsetY, instanceOffset.y); shader.SetFloatParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceWidthScale, WidthScale); shader.SetFloatParam(cmd, UVFallbackBufferBuilderShaderIDs.InstanceHeightScale, HeightScale); } public void BindChunked(CommandBuffer cmd, IRayTracingShader shader, Vector2Int instanceOffset, uint2 chunkOffset, uint chunkSize) { Bind(cmd, shader, instanceOffset); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.ChunkOffsetX, (int)chunkOffset.x); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.ChunkOffsetY, (int)chunkOffset.y); shader.SetIntParam(cmd, UVFallbackBufferBuilderShaderIDs.ChunkSize, (int)chunkSize); } public void Bind(CommandBuffer cmd, ComputeShader shader, int kernelIndex, Vector2Int instanceOffset) { cmd.SetComputeTextureParam(shader, kernelIndex, UVFallbackBufferBuilderShaderIDs.UvFallback, UVFallbackRT); cmd.SetComputeIntParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceWidth, UVFallbackRT.width); cmd.SetComputeIntParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceHeight, UVFallbackRT.height); cmd.SetComputeIntParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceOffsetX, instanceOffset.x); cmd.SetComputeIntParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceOffsetY, instanceOffset.y); cmd.SetComputeFloatParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceWidthScale, WidthScale); cmd.SetComputeFloatParam(shader, UVFallbackBufferBuilderShaderIDs.InstanceHeightScale, HeightScale); } } }