From 8ca58a1f13309ca90d11b737874f65ff6e3156b0 Mon Sep 17 00:00:00 2001 From: rsformoso Date: Wed, 18 Mar 2026 16:49:41 +0800 Subject: [PATCH] Interesting cel-shaded like effect --- Assets/vfx-graph-gs/Main.unity | 2 +- Assets/vfx-graph-gs/Scripts/SplatImporter.cs | 195 +++++++++++++------ Assets/vfx-graph-gs/SplatVFX.vfx | 6 +- 3 files changed, 144 insertions(+), 59 deletions(-) diff --git a/Assets/vfx-graph-gs/Main.unity b/Assets/vfx-graph-gs/Main.unity index 05617f9..7b86ca9 100644 --- a/Assets/vfx-graph-gs/Main.unity +++ b/Assets/vfx-graph-gs/Main.unity @@ -157,7 +157,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: -973084862543737302, guid: 25c54d8c074f08447ab1c822a6a052fa, type: 3} propertyPath: m_PropertySheet.m_Float.m_Array.Array.data[0].m_Value - value: 64 + value: 32 objectReference: {fileID: 0} - target: {fileID: -973084862543737302, guid: 25c54d8c074f08447ab1c822a6a052fa, type: 3} propertyPath: m_PropertySheet.m_Uint.m_Array.Array.data[0].m_Overridden diff --git a/Assets/vfx-graph-gs/Scripts/SplatImporter.cs b/Assets/vfx-graph-gs/Scripts/SplatImporter.cs index 193a8b6..c438094 100644 --- a/Assets/vfx-graph-gs/Scripts/SplatImporter.cs +++ b/Assets/vfx-graph-gs/Scripts/SplatImporter.cs @@ -22,6 +22,8 @@ using UnityEngine.Playables; using Unity.Android.Gradle.Manifest; using UnityEngine.Timeline; using Unity.Jobs; +using System.Xml.Schema; +using Unity.Android.Gradle; namespace SplatVfx.Editor { @@ -110,9 +112,24 @@ namespace SplatVfx.Editor { public uint NumElements; public uint BinaryDataStartOffset; - public int[] ElementByteOffsets; - public TypeEnum[] ElementTypes; + public NativeArray ElementByteOffsets; + public NativeArray ElementTypes; public int BytesPerElement; + + public HeaderData(int count) + { + NumElements = 0; + BinaryDataStartOffset = 0; + ElementByteOffsets = new NativeArray(count, Allocator.Persistent); + ElementTypes = new NativeArray(count, Allocator.Persistent); + BytesPerElement = 0; + } + + public void Dispose() + { + ElementByteOffsets.Dispose(); + ElementTypes.Dispose(); + } } private static (string headerString, int endHeaderPosition) GetHeader(string filePath) @@ -133,20 +150,19 @@ namespace SplatVfx.Editor } // Old logic that works because AppendLine adds another \n - // TODO: Fix this logic. Start with using Append instead of AppendLine + // TODO: Fix this logic int endHeaderPosition = headerBuilder.Length - headerBuilder.ToString().Split('\n').Length + 1; + Debug.Log($"Total filesize: {fileStream.Length}"); + Debug.Log($"Byte stream start position: {endHeaderPosition}"); return (headerBuilder.ToString(), endHeaderPosition); } private static HeaderData ParseMetadata(string headerString) { - HeaderData result = new(); + HeaderData result = new(59); string line; - result.ElementByteOffsets = new int[59]; - result.ElementTypes = new TypeEnum[59]; - using var reader = new StringReader(headerString); while ((line = reader.ReadLine()) != null) @@ -170,13 +186,14 @@ namespace SplatVfx.Editor { int ix = (int)dataName; result.ElementByteOffsets[ix] = result.BytesPerElement; - result.ElementTypes[ix] = dataType; + result.ElementTypes[ix] = (int)dataType; } result.BytesPerElement += TypeToByteLengths[dataType]; break; } } + Debug.Log($"Bytes per Element: {result.BytesPerElement}"); return result; } @@ -184,21 +201,21 @@ namespace SplatVfx.Editor [BurstCompile] private struct SplatDataNative { - NativeArray position; - NativeArray color; - NativeArray sh; - NativeArray opacity; - NativeArray scale; - NativeArray rotation; + public NativeArray position; + public NativeArray color; + public NativeArray sh; + public NativeArray opacity; + public NativeArray scale; + public NativeArray rotation; public SplatDataNative(int count) { - position = new NativeArray(count, Allocator.Temp); - color = new NativeArray(count, Allocator.Temp); - sh = new NativeArray(count, Allocator.Temp); - opacity = new NativeArray(count, Allocator.Temp); - scale = new NativeArray(count, Allocator.Temp); - rotation = new NativeArray(count, Allocator.Temp); + position = new NativeArray(count, Allocator.Persistent); + color = new NativeArray(count, Allocator.Persistent); + sh = new NativeArray(count * 45, Allocator.Persistent); + opacity = new NativeArray(count, Allocator.Persistent); + scale = new NativeArray(count, Allocator.Persistent); + rotation = new NativeArray(count, Allocator.Persistent); } public void Dispose() @@ -216,67 +233,135 @@ namespace SplatVfx.Editor { var splatData = ScriptableObject.CreateInstance(); - int elementsPerChunk = MAX_CHUNK_SIZE / headerData.BytesPerElement; + splatData.PositionArray = new Vector3[headerData.NumElements]; + splatData.ScaleArray = new Vector3[headerData.NumElements]; + splatData.RotationArray = new Quaternion[headerData.NumElements]; + splatData.ColorArray = new Color[headerData.NumElements]; + splatData.OpacityArray = new float[headerData.NumElements]; + splatData.SHArray = new float[headerData.NumElements * 45]; + + int elementsPerChunk = Mathf.Min( + MAX_CHUNK_SIZE / headerData.BytesPerElement, + (int)headerData.NumElements + ); int chunkSize = elementsPerChunk * headerData.BytesPerElement; + Debug.Log($"Elements per chunk: {elementsPerChunk}"); + Debug.Log($"Chunk size: {chunkSize}"); + using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - using var reader = new BinaryReader(fileStream); fileStream.Seek(endHeaderPosition, SeekOrigin.Begin); - var dataList = new List(); - var jobHandles = new List(); + int totalBytes = (int)fileStream.Length - endHeaderPosition; + Debug.Log($"Total bytes: {totalBytes}"); - for (int pos = endHeaderPosition; pos < fileStream.Length; pos += chunkSize) + var splatDataNative = new SplatDataNative(elementsPerChunk); + var chunkBuffer = new NativeArray(chunkSize, Allocator.Persistent); + var chunkBufferSpan = MemoryMarshal.AsBytes(chunkBuffer); + int numBytesRead; + int numElemsRead; + int totalElemsRead = 0; + + while ((numBytesRead = fileStream.Read(chunkBufferSpan)) > 0) { - int currChunkSize = (int)(pos + chunkSize < fileStream.Length ? chunkSize : fileStream.Length - pos); - byte[] chunkBytes = reader.ReadBytes(currChunkSize); - int numElemInChunk = currChunkSize / headerData.BytesPerElement; - - var splatDataNative = new SplatDataNative(numElemInChunk); - dataList.Add(splatDataNative); - - var splatChunkJob = new SplatChunkJob - { - chunkBytes = chunkBytes, + numElemsRead = numBytesRead / headerData.BytesPerElement; + var splatElementJob = new SplatElementJob + { + chunkBytes = chunkBuffer, splatDataNative = splatDataNative, headerData = headerData, - numElem = numElemInChunk + totalElemsRead = totalElemsRead, + numElemsRead = numElemsRead }; - var handle = splatChunkJob.Schedule(); - jobHandles.Add(handle); + splatElementJob.Schedule().Complete(); + + for (int i = 0; i < numElemsRead; i++) + { + splatData.PositionArray[totalElemsRead + i] = splatDataNative.position[i]; + splatData.ColorArray[totalElemsRead + i].r = splatDataNative.color[i].x; + splatData.ColorArray[totalElemsRead + i].g = splatDataNative.color[i].y; + splatData.ColorArray[totalElemsRead + i].b = splatDataNative.color[i].z; + splatData.OpacityArray[totalElemsRead + i] = splatDataNative.opacity[i]; + splatData.ScaleArray[totalElemsRead + i] = splatDataNative.scale[i]; + splatData.RotationArray[totalElemsRead + i] = new quaternion(splatDataNative.rotation[i]); + } + + // NativeArray.Copy(splatDataNative.position.Reinterpret(), 0, splatData.PositionArray, totalElemsRead, numElemsRead); + + totalElemsRead += numElemsRead; + Debug.Log($"Current Elements Read: {totalElemsRead}"); } - // Copy data here + Debug.Log($"First position Vector3: {splatData.PositionArray[0]}"); + headerData.Dispose(); + splatDataNative.Dispose(); + chunkBuffer.Dispose(); + splatData.ReleaseGpuResources(); + return splatData; } [BurstCompile] - private struct SplatChunkJob : IJob + private struct SplatElementJob : IJob { - public byte[] chunkBytes; + public NativeArray chunkBytes; public SplatDataNative splatDataNative; - public HeaderData headerData; - public int numElem; + [ReadOnly] public HeaderData headerData; + [ReadOnly] public int totalElemsRead; + [ReadOnly] public int numElemsRead; public void Execute() { - throw new NotImplementedException(); - } - } + for (int i = 0; i < numElemsRead; i++) + { + int elemIx = i; + int start = elemIx * headerData.BytesPerElement; - [BurstCompile] - private struct SplatElementJob : IJobParallelFor - { - public byte[] chunkBytes; - public SplatDataNative splatDataNative; - public HeaderData headerData; + splatDataNative.position[elemIx] = new() + { + x = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.x]), + y = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.y]), + z = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.z]) + }; - public void Execute(int ix) - { - throw new NotImplementedException(); + float3 color = new() + { + x = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.f_dc_0]), + y = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.f_dc_1]), + z = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.f_dc_2]) + }; + color = 0.5f + color * SH_C0; + splatDataNative.color[elemIx] = color; + + for (int j = 0; j < 45; j++) + { + splatDataNative.sh[elemIx + j] = chunkBytes[start + headerData.ElementByteOffsets[(int)DataEnum.f_rest_0 + j]]; + } + + float opacity = chunkBytes[start + headerData.ElementByteOffsets[(int)DataEnum.opacity]]; + opacity = 1 / (1 + math.exp(-opacity)); + splatDataNative.opacity[elemIx] = opacity; + + float3 scale = new() + { + x = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.scale_0]), + y = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.scale_1]), + z = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.scale_2]) + }; + scale = math.exp(scale); + splatDataNative.scale[elemIx] = scale; + + splatDataNative.rotation[elemIx] = new() + { + w = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.rot_0]), + x = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.rot_1]), + y = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.rot_2]), + z = chunkBytes.ReinterpretLoad(start + headerData.ElementByteOffsets[(int)DataEnum.rot_3]) + }; + } } } diff --git a/Assets/vfx-graph-gs/SplatVFX.vfx b/Assets/vfx-graph-gs/SplatVFX.vfx index 7990cef..8566f6f 100644 --- a/Assets/vfx-graph-gs/SplatVFX.vfx +++ b/Assets/vfx-graph-gs/SplatVFX.vfx @@ -209,7 +209,7 @@ MonoBehaviour: x: -692 y: -492 width: 1846 - height: 2501 + height: 2502 --- !u!114 &114350483966674976 MonoBehaviour: m_ObjectHideFlags: 1 @@ -13741,7 +13741,7 @@ MonoBehaviour: m_Parent: {fileID: 114350483966674976} m_Children: - {fileID: 8926484042661619332} - m_UIPosition: {x: 705, y: 700} + m_UIPosition: {x: 705, y: 741} m_UICollapsed: 0 m_UISuperCollapsed: 0 m_InputSlots: [] @@ -16773,7 +16773,7 @@ MonoBehaviour: m_Type: m_SerializableType: System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - m_SerializableObject: True + m_SerializableObject: False m_Space: -1 m_Property: name: _vfx_enabled