using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.UnifiedRayTracing; namespace UnityEngine.PathTracing.Lightmapping { // Detects pixels where multiple UV charts have overlapping bilinear neighborhoods. internal class UVOverlapDetection : IDisposable { private static class ShaderProperties { public static int TextureSize = Shader.PropertyToID("_TextureSize"); public static int PerPixelChart = Shader.PropertyToID("_PerPixelChart"); public static int InstanceIndex = Shader.PropertyToID("_InstanceIndex"); public static int EdgeCount = Shader.PropertyToID("_EdgeCount"); public static int TriangleEdges = Shader.PropertyToID("_TriangleEdges"); public static int ChartIndices = Shader.PropertyToID("_ChartIndices"); public static int OverlapPixels = Shader.PropertyToID("_OverlapPixels"); public static int OverlapInstances = Shader.PropertyToID("_OverlapInstances"); public static int TileX = Shader.PropertyToID("_TileX"); public static int TileY = Shader.PropertyToID("_TileY"); public static int TileSize = Shader.PropertyToID("_TileSize"); } private int _lightmapResolution; private ComputeShader _shader; private NativeArray _triangleEdges; private NativeArray _chartIndices; private GraphicsBuffer _triangleEdgesBuffer; private GraphicsBuffer _chartIndicesBuffer; private GraphicsBuffer _perPixelChart; private GraphicsBuffer _overlapPixelsBuffer; private GraphicsBuffer _overlapInstancesBuffer; private int _overlapKernel; private uint _overlapKernelSize; public void Initialize(ComputeShader shader, uint lightmapResolution, uint maxEdgeCount, uint instanceCount) { _lightmapResolution = (int)lightmapResolution; _shader = shader; _triangleEdges = new NativeArray((int)maxEdgeCount,Allocator.Persistent); _chartIndices = new NativeArray((int)maxEdgeCount, Allocator.Persistent); _triangleEdgesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _triangleEdges.Length, UnsafeUtility.SizeOf()); _chartIndicesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _chartIndices.Length, sizeof(uint)); _perPixelChart = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _lightmapResolution*_lightmapResolution, sizeof(uint)); _overlapPixelsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _lightmapResolution*_lightmapResolution, sizeof(uint)); _overlapInstancesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)instanceCount, sizeof(uint)); _overlapKernel = shader.FindKernel("MarkBilinearOverlaps"); shader.GetKernelThreadGroupSizes(_overlapKernel, out _overlapKernelSize, out _, out _); // Initialize buffers _overlapPixelsBuffer.SetData(new uint[_lightmapResolution*_lightmapResolution]); _overlapInstancesBuffer.SetData(new uint[instanceCount]); var initPerPixelChart = new uint[_lightmapResolution * _lightmapResolution]; Array.Fill(initPerPixelChart, uint.MaxValue); _perPixelChart.SetData(initPerPixelChart); } public void MarkOverlapsInInstance( CommandBuffer cmd, Mesh uvMesh, NativeArray vertexToChartIndex, float4 occupiedST, uint instanceIndex, uint chartIndexOffset) { // Get the start and end pos of every edge, and the chart index for each edge. var indices = uvMesh.triangles; var vertices = uvMesh.vertices; for (uint triangleIdx = 0; triangleIdx < indices.Length / 3; triangleIdx++) { for (uint edgeOffset = 0; edgeOffset < 3; edgeOffset++) { uint baseTriangleIdx = triangleIdx * 3; int startVertexIdx = indices[baseTriangleIdx + edgeOffset]; int endVertexIdx = indices[baseTriangleIdx + ((edgeOffset + 1) % 3)]; float3 start = vertices[startVertexIdx]; float3 end = vertices[endVertexIdx]; start.xy = (start.xy * occupiedST.xy + occupiedST.zw) * _lightmapResolution; end.xy = (end.xy * occupiedST.xy + occupiedST.zw) * _lightmapResolution; _triangleEdges[(int)(baseTriangleIdx + edgeOffset)] = new float4(start.x, start.y, end.x, end.y); uint chartIdx = chartIndexOffset + vertexToChartIndex[startVertexIdx]; _chartIndices[(int)(baseTriangleIdx + edgeOffset)] = chartIdx; } } cmd.SetBufferData(_triangleEdgesBuffer, _triangleEdges); cmd.SetBufferData(_chartIndicesBuffer, _chartIndices); // If the lightmap resolution is over this constant, we split the dispatch into multiple // smaller dispatches over tiles, to prevent dispatches that are too large. Otherwise in // the worst case, every lightmap texel can be checked in one dispatch. const uint tileSize = 1024; // Mark overlaps int edgeCount = indices.Length; cmd.SetComputeIntParam(_shader, ShaderProperties.InstanceIndex, (int)instanceIndex); cmd.SetComputeIntParam(_shader, ShaderProperties.EdgeCount, edgeCount); cmd.SetComputeIntParam(_shader, ShaderProperties.TextureSize, _lightmapResolution); cmd.SetComputeIntParam(_shader, ShaderProperties.TileSize, (int)tileSize); cmd.SetComputeBufferParam(_shader, _overlapKernel, ShaderProperties.TriangleEdges, _triangleEdgesBuffer); cmd.SetComputeBufferParam(_shader, _overlapKernel, ShaderProperties.ChartIndices, _chartIndicesBuffer); cmd.SetComputeBufferParam(_shader, _overlapKernel, ShaderProperties.PerPixelChart, _perPixelChart); cmd.SetComputeBufferParam(_shader, _overlapKernel, ShaderProperties.OverlapPixels, _overlapPixelsBuffer); cmd.SetComputeBufferParam(_shader, _overlapKernel, ShaderProperties.OverlapInstances, _overlapInstancesBuffer); int dispatchSize = GraphicsHelpers.DivUp(edgeCount, _overlapKernelSize); uint tileCountOnEachDim = GraphicsHelpers.DivUp((uint)_lightmapResolution, tileSize); for (uint tileY = 0; tileY < tileCountOnEachDim; tileY++) { for (uint tileX = 0; tileX < tileCountOnEachDim; tileX++) { cmd.SetComputeIntParam(_shader, ShaderProperties.TileX, (int)tileX); cmd.SetComputeIntParam(_shader, ShaderProperties.TileY, (int)tileY); cmd.DispatchCompute(_shader, _overlapKernel, dispatchSize, 1, 1); } } } public void CompactAndReadbackOverlaps( CommandBuffer cmd, out uint[] uniqueOverlapPixelIndices, out ulong[] uniqueOverlapInstanceIndices) { // Make sure all kernels are finished GraphicsHelpers.Flush(cmd); // Readback overlap buffers uint[] overlapPixels = new uint[_overlapPixelsBuffer.count]; _overlapPixelsBuffer.GetData(overlapPixels); uint[] overlapInstances = new uint[_overlapInstancesBuffer.count]; _overlapInstancesBuffer.GetData(overlapInstances); // Deduplicate overlaps List uniqueOverlapPixelIndicesSet = new List(); List uniqueOverlapInstanceIndicesSet = new List(); for (uint pixelIndex = 0; pixelIndex < _overlapPixelsBuffer.count; pixelIndex++) { if (overlapPixels[pixelIndex] != 0) uniqueOverlapPixelIndicesSet.Add(pixelIndex); } for (uint instanceIndex = 0; instanceIndex < _overlapInstancesBuffer.count; instanceIndex++) { if (overlapInstances[instanceIndex] != 0) uniqueOverlapInstanceIndicesSet.Add(instanceIndex); } uniqueOverlapPixelIndices = new uint[uniqueOverlapPixelIndicesSet.Count]; uniqueOverlapPixelIndicesSet.CopyTo(uniqueOverlapPixelIndices); uniqueOverlapInstanceIndices = new ulong[uniqueOverlapInstanceIndicesSet.Count]; uniqueOverlapInstanceIndicesSet.CopyTo(uniqueOverlapInstanceIndices); // Sort to keep the order deterministic Array.Sort(uniqueOverlapPixelIndices); Array.Sort(uniqueOverlapInstanceIndices); } public void Dispose() { if (_triangleEdges.IsCreated) _triangleEdges.Dispose(); if (_chartIndices.IsCreated) _chartIndices.Dispose(); if (_triangleEdgesBuffer != null && _triangleEdgesBuffer.IsValid()) _triangleEdgesBuffer.Dispose(); if (_chartIndicesBuffer != null && _chartIndicesBuffer.IsValid()) _chartIndicesBuffer.Dispose(); if (_perPixelChart != null && _perPixelChart.IsValid()) _perPixelChart.Dispose(); if (_overlapPixelsBuffer != null && _overlapPixelsBuffer.IsValid()) _overlapPixelsBuffer.Dispose(); if (_overlapInstancesBuffer != null && _overlapInstancesBuffer.IsValid()) _overlapInstancesBuffer.Dispose(); } #if UNITY_EDITOR public static ComputeShader LoadShader() { return UnityEditor.AssetDatabase.LoadAssetAtPath( "Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/Lightmapping/BilinearOverlaps.compute"); } #endif } }