Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,46 @@
using System;
namespace UnityEngine.Rendering
{
/// <summary>
/// Common global resources for mip pyramid generator (color, depth).
/// </summary>
[Serializable]
[SupportedOnRenderPipeline]
[Categorization.CategoryInfo(Name = "R: Mip Generator Resources", Order = 1000), HideInInspector]
internal sealed class MipGenRenderPipelineRuntimeResources : IRenderPipelineResources
{
/// <summary>
/// Version of the SSR Resources
/// </summary>
public int version => 0;
bool IRenderPipelineGraphicsSettings.isAvailableInPlayerBuild => true;
[SerializeField, ResourcePath("Runtime/MipGen/Shaders/ColorPyramidPS.shader")]
private Shader m_ColorPyramidPS;
public Shader colorPyramidPS
{
get => m_ColorPyramidPS;
set => this.SetValueAndNotify(ref m_ColorPyramidPS, value);
}
[SerializeField, ResourcePath("Runtime/MipGen/Shaders/ColorPyramid.compute")]
public ComputeShader m_ColorPyramidCS;
public ComputeShader colorPyramidCS
{
get => m_ColorPyramidCS;
set => this.SetValueAndNotify(ref m_ColorPyramidCS, value);
}
[SerializeField, ResourcePath("Runtime/MipGen/Shaders/DepthPyramid.compute")]
private ComputeShader m_DepthPyramidCS;
public ComputeShader depthPyramidCS
{
get => m_DepthPyramidCS;
set => this.SetValueAndNotify(ref m_DepthPyramidCS, value);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26bfb1c84b583444c85ebb814cb4eaf9
@@ -0,0 +1,511 @@
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;
namespace UnityEngine.Rendering
{
internal struct PackedMipChainInfo
{
public Vector2Int textureSize;
public int mipLevelCount; // mips contain min (closest) depth
public int mipLevelCountCheckerboard;
public Vector2Int[] mipLevelSizes;
public Vector2Int[] mipLevelOffsets; // mips contain min (closest) depth
public Vector2Int[] mipLevelOffsetsCheckerboard;
private Vector2 cachedTextureScale;
private Vector2Int cachedHardwareTextureSize;
private int cachedCheckerboardMipCount;
public void Allocate()
{
mipLevelOffsets = new Vector2Int[15];
mipLevelOffsetsCheckerboard = new Vector2Int[15];
mipLevelSizes = new Vector2Int[15];
}
enum PackDirection
{
Right,
Down,
}
static Vector2Int NextMipBegin(Vector2Int prevMipBegin, Vector2Int prevMipSize, PackDirection dir)
{
Vector2Int mipBegin = prevMipBegin;
if (dir == PackDirection.Right)
mipBegin.x += prevMipSize.x;
else
mipBegin.y += prevMipSize.y;
return mipBegin;
}
// 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, int checkerboardMipCount)
{
// only support up to 2 mips of checkerboard data being created
checkerboardMipCount = Mathf.Clamp(checkerboardMipCount, 0, 2);
bool isHardwareDrsOn = DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled();
Vector2Int 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);
// No work needed.
if (cachedHardwareTextureSize == hardwareTextureSize && cachedTextureScale == textureScale && cachedCheckerboardMipCount == checkerboardMipCount)
return;
cachedHardwareTextureSize = hardwareTextureSize;
cachedTextureScale = textureScale;
cachedCheckerboardMipCount = checkerboardMipCount;
mipLevelSizes[0] = hardwareTextureSize;
mipLevelOffsets[0] = Vector2Int.zero;
mipLevelOffsetsCheckerboard[0] = mipLevelOffsets[0];
int mipLevel = 0;
Vector2Int mipSize = hardwareTextureSize;
bool hasCheckerboard = (checkerboardMipCount != 0);
do
{
mipLevel++;
// Round up.
mipSize.x = Mathf.Max(1, (mipSize.x + 1) >> 1);
mipSize.y = Mathf.Max(1, (mipSize.y + 1) >> 1);
mipLevelSizes[mipLevel] = mipSize;
Vector2Int prevMipSize = mipLevelSizes[mipLevel - 1];
Vector2Int prevMipBegin = mipLevelOffsets[mipLevel - 1];
Vector2Int prevMipBeginCheckerboard = mipLevelOffsetsCheckerboard[mipLevel - 1];
Vector2Int mipBegin = prevMipBegin;
Vector2Int mipBeginCheckerboard = prevMipBeginCheckerboard;
if (mipLevel == 1)
{
// first mip always below full resolution
mipBegin = NextMipBegin(prevMipBegin, prevMipSize, PackDirection.Down);
// pack checkerboard next to it if present
if (hasCheckerboard)
mipBeginCheckerboard = NextMipBegin(mipBegin, mipSize, PackDirection.Right);
else
mipBeginCheckerboard = mipBegin;
}
else
{
// alternate directions, mip 2 starts with down if checkerboard, right if not
bool isOdd = ((mipLevel & 1) != 0);
PackDirection dir = (isOdd ^ hasCheckerboard) ? PackDirection.Down : PackDirection.Right;
mipBegin = NextMipBegin(prevMipBegin, prevMipSize, dir);
mipBeginCheckerboard = NextMipBegin(prevMipBeginCheckerboard, prevMipSize, dir);
}
mipLevelOffsets[mipLevel] = mipBegin;
mipLevelOffsetsCheckerboard[mipLevel] = mipBeginCheckerboard;
hardwareTextureSize.x = Mathf.Max(hardwareTextureSize.x, mipBegin.x + mipSize.x);
hardwareTextureSize.y = Mathf.Max(hardwareTextureSize.y, mipBegin.y + mipSize.y);
hardwareTextureSize.x = Mathf.Max(hardwareTextureSize.x, mipBeginCheckerboard.x + mipSize.x);
hardwareTextureSize.y = Mathf.Max(hardwareTextureSize.y, mipBeginCheckerboard.y + mipSize.y);
}
while ((mipSize.x > 1) || (mipSize.y > 1));
textureSize = new Vector2Int(
(int)Mathf.Ceil((float)hardwareTextureSize.x * textureScale.x), (int)Mathf.Ceil((float)hardwareTextureSize.y * textureScale.y));
mipLevelCount = mipLevel + 1;
mipLevelCountCheckerboard = hasCheckerboard ? (1 + checkerboardMipCount) : 0;
}
}
internal class MipGenerator
{
ComputeShader m_ColorPyramidCS = null;
ComputeShader m_DepthPyramidCS = null;
Shader m_ColorPyramidPS;
Material m_ColorPyramidPSMat;
MaterialPropertyBlock m_PropertyBlockBlur;
int m_DepthDownsampleKernel = -1;
int m_ColorDownsampleKernel = -1;
int m_ColorGaussianKernel = -1;
bool m_PreferCompute;
bool m_SupportCompute;
LocalKeyword m_DisableTexture2DArrayColorKeyword;
LocalKeyword m_DisableTexture2DArrayColorPSKeyword;
LocalKeyword m_DisableTexture2DArrayDepthKeyword;
LocalKeyword m_EnableCheckerboardKeyword;
public static readonly int _DepthMipChain = Shader.PropertyToID("_DepthMipChain");
public static readonly int _DepthPyramidConstants = Shader.PropertyToID("DepthPyramidConstants");
public static readonly int _Size = Shader.PropertyToID("_Size");
public static readonly int _Source = Shader.PropertyToID("_Source");
public static readonly int _Destination = Shader.PropertyToID("_Destination");
public static readonly int _SourceMip = Shader.PropertyToID("_SourceMip");
public static readonly int _SrcScaleBias = Shader.PropertyToID("_SrcScaleBias");
public static readonly int _SrcUvLimits = Shader.PropertyToID("_SrcUvLimits");
public static readonly string k_EnableCheckerboard = "ENABLE_CHECKERBOARD";
public const int k_MinimumResolutionGaussian = 8;
public MipGenerator(bool preferCompute = true)
{
if (!GraphicsSettings.TryGetRenderPipelineSettings<MipGenRenderPipelineRuntimeResources>(out var runtimeShaders))
{
Debug.LogErrorFormat(
$"Couldn't find the required resources for the {nameof(MipGenerator)} .");
}
m_SupportCompute = SystemInfo.supportsComputeShaders
&& runtimeShaders.colorPyramidCS.HasKernel("ColorDownsample")
&& runtimeShaders.colorPyramidCS.HasKernel("ColorGaussian")
&& runtimeShaders.depthPyramidCS.HasKernel("DepthDownsample");
if (m_SupportCompute)
{
m_ColorPyramidCS = runtimeShaders.colorPyramidCS;
m_DepthPyramidCS = runtimeShaders.depthPyramidCS;
m_ColorDownsampleKernel = m_ColorPyramidCS.FindKernel("ColorDownsample");
m_ColorGaussianKernel = m_ColorPyramidCS.FindKernel("ColorGaussian");
m_DisableTexture2DArrayColorKeyword = new LocalKeyword(m_ColorPyramidCS, ShaderKeywordStrings.DisableTexture2DXArray);
m_DepthDownsampleKernel = m_DepthPyramidCS.FindKernel("DepthDownsample");
m_DisableTexture2DArrayDepthKeyword = new LocalKeyword(m_DepthPyramidCS, ShaderKeywordStrings.DisableTexture2DXArray);
m_EnableCheckerboardKeyword = new LocalKeyword(m_DepthPyramidCS, k_EnableCheckerboard);
}
m_ColorPyramidPS = runtimeShaders.colorPyramidPS;
m_DisableTexture2DArrayColorPSKeyword = new LocalKeyword(m_ColorPyramidPS, ShaderKeywordStrings.DisableTexture2DXArray);
m_ColorPyramidPSMat = CoreUtils.CreateEngineMaterial(m_ColorPyramidPS);
m_PropertyBlockBlur = new MaterialPropertyBlock();
m_PreferCompute = preferCompute;
}
public void Release()
{
CoreUtils.Destroy(m_ColorPyramidPSMat);
}
class DepthPyramidPassData
{
public PackedMipChainInfo info;
public ComputeShader cs;
public int kernel;
public TextureHandle depthTexture;
public LocalKeyword disableTexture2DArrayKeyword;
public LocalKeyword enableCheckerboardKeyword;
}
// Generates an in-place depth pyramid Compute only
// TODO: Mip-mapping depth is problematic for precision at lower mips, generate a packed atlas instead
public void RenderMinDepthPyramid(RenderGraph renderGraph, TextureHandle depthTexture, ref PackedMipChainInfo depthBufferMipChainInfo)
{
if (!m_SupportCompute)
{
Debug.LogError("MipGenerator: Can't render depth pyramid, platform doesn't support compute shaders.");
return;
}
using (var builder = renderGraph.AddComputePass<DepthPyramidPassData>("Generate Depth Buffer MIP Chain", out var passData))
{
passData.info = depthBufferMipChainInfo;
passData.cs = m_DepthPyramidCS;
passData.kernel = m_DepthDownsampleKernel;
passData.depthTexture = depthTexture;
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayDepthKeyword;
passData.enableCheckerboardKeyword = m_EnableCheckerboardKeyword;
// Enable global state modification so we can set keywords.
builder.AllowGlobalStateModification(true);
builder.UseTexture(passData.depthTexture, AccessFlags.ReadWrite);
builder.SetRenderFunc<DepthPyramidPassData>(static (data, context) =>
{
var cmd = context.cmd;
var sourceIsArray = (((RenderTexture)data.depthTexture).dimension == TextureDimension.Tex2DArray);
cmd.SetKeyword(data.cs, data.disableTexture2DArrayKeyword, !sourceIsArray);
// Note: Gather() doesn't take a LOD parameter and we cannot bind an SRV of a MIP level,
// and we don't support Min samplers either. So we are forced to perform 4x loads.
for (int dstIndex0 = 1; dstIndex0 < data.info.mipLevelCount;)
{
int minCount = Mathf.Min(data.info.mipLevelCount - dstIndex0, 4);
int cbCount = 0;
if (dstIndex0 < data.info.mipLevelCountCheckerboard)
{
cbCount = data.info.mipLevelCountCheckerboard - dstIndex0;
Debug.Assert(dstIndex0 == 1, "expected to make checkerboard mips on the first pass");
Debug.Assert(cbCount <= minCount, "expected fewer checkerboard mips than min mips");
Debug.Assert(cbCount <= 2, "expected 2 or fewer checkerboard mips for now");
}
Vector2Int srcOffset = data.info.mipLevelOffsets[dstIndex0 - 1];
Vector2Int srcSize = data.info.mipLevelSizes[dstIndex0 - 1];
int dstIndex1 = Mathf.Min(dstIndex0 + 1, data.info.mipLevelCount - 1);
int dstIndex2 = Mathf.Min(dstIndex0 + 2, data.info.mipLevelCount - 1);
int dstIndex3 = Mathf.Min(dstIndex0 + 3, data.info.mipLevelCount - 1);
DepthPyramidConstants cb = new DepthPyramidConstants
{
_MinDstCount = (uint)minCount,
_CbDstCount = (uint)cbCount,
_SrcOffset = srcOffset,
_SrcLimit = srcSize - Vector2Int.one,
_DstSize0 = data.info.mipLevelSizes[dstIndex0],
_DstSize1 = data.info.mipLevelSizes[dstIndex1],
_DstSize2 = data.info.mipLevelSizes[dstIndex2],
_DstSize3 = data.info.mipLevelSizes[dstIndex3],
_MinDstOffset0 = data.info.mipLevelOffsets[dstIndex0],
_MinDstOffset1 = data.info.mipLevelOffsets[dstIndex1],
_MinDstOffset2 = data.info.mipLevelOffsets[dstIndex2],
_MinDstOffset3 = data.info.mipLevelOffsets[dstIndex3],
_CbDstOffset0 = data.info.mipLevelOffsetsCheckerboard[dstIndex0],
_CbDstOffset1 = data.info.mipLevelOffsetsCheckerboard[dstIndex1],
};
ConstantBuffer.Push(cmd, in cb, data.cs, _DepthPyramidConstants);
cmd.SetComputeTextureParam(data.cs, data.kernel, _DepthMipChain, data.depthTexture);
cmd.SetKeyword(data.cs, data.enableCheckerboardKeyword, cbCount != 0);
Vector2Int dstSize = data.info.mipLevelSizes[dstIndex0];
cmd.DispatchCompute(data.cs, data.kernel, CoreUtils.DivRoundUp(dstSize.x, 8), CoreUtils.DivRoundUp(dstSize.y, 8), ((RenderTexture)data.depthTexture).volumeDepth);
dstIndex0 += minCount;
}
});
}
}
private class PassDataMipChainRaster
{
public MaterialPropertyBlock propertyBlock;
public Material material;
public TextureHandle source;
public int dstMipWidth, dstMipHeight;
public int srcMipLevel;
public float scaleX, scaleY;
public float blurSourceTextureWidth, blurSourceTextureHeight;
public LocalKeyword disableTexture2DArrayKeyword;
public bool sourceIsArray;
}
private class PassDataMipChainCompute
{
public int numTheadGroupX, numTheadGroupY, numTheadGroupZ;
public int srcMipWidth, srcMipHeight;
public int dstMipWidth, dstMipHeight;
public int srcMipLevel;
public TextureHandle tempDownsamplePyramid, destination;
public ComputeShader cs;
public LocalKeyword disableTexture2DArrayKeyword;
public int downsampleKernel, gaussianKernel;
public bool sourceIsArray;
}
// Generates the gaussian pyramid of source into destination
// We can't do it in place as the color pyramid has to be read while writing to the color
// buffer in some cases (e.g. refraction, distortion)
// Returns the number of mips
public int RenderColorGaussianPyramid(RenderGraph renderGraph, Vector2Int size, TextureHandle source, TextureHandle destination)
{
TextureDesc descSrc = renderGraph.GetTextureDesc(source);
TextureDesc descDst = renderGraph.GetTextureDesc(destination);
// Select between Tex2D and Tex2DArray versions of the kernels
bool sourceIsArray = (descSrc.dimension == TextureDimension.Tex2DArray);
// Sanity check
if (sourceIsArray)
{
Debug.Assert(descSrc.dimension == descDst.dimension, "MipGenerator source texture does not match dimension of destination!");
}
int srcMipLevel = 0;
int srcMipWidth = size.x;
int srcMipHeight = size.y;
bool isHardwareDrsOn = DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled();
var hardwareTextureSize = new Vector2Int(descSrc.width, descSrc.height);
if (isHardwareDrsOn)
hardwareTextureSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(hardwareTextureSize);
renderGraph.AddCopyPass(source, destination, "Copy mip 0");
var finalTargetSize = new Vector2Int(descDst.width, descDst.height);
if (descDst.useDynamicScale && isHardwareDrsOn)
finalTargetSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(finalTargetSize);
bool usePixelShader = !m_SupportCompute || !m_PreferCompute;
// Note: smaller mips are excluded as we don't need them and the gaussian compute works
// on 8x8 blocks
while (srcMipWidth >= k_MinimumResolutionGaussian || srcMipHeight >= k_MinimumResolutionGaussian)
{
int dstMipWidth = Mathf.Max(1, srcMipWidth >> 1);
int dstMipHeight = Mathf.Max(1, srcMipHeight >> 1);
var desc = new TextureDesc(dstMipWidth, dstMipHeight);
desc.name = "Temporary Downsampled Pyramid";
desc.scale = Vector2.one * 0.5f;
desc.dimension = descSrc.dimension;
desc.colorFormat = descDst.colorFormat;
desc.enableRandomWrite = !usePixelShader;
desc.useMipMap = false;
desc.slices = sourceIsArray ? TextureXR.slices : 1;
var tempDownsamplePyramid = renderGraph.CreateTexture(desc);
// Scale for downsample
float downsampleScaleX = ((float)srcMipWidth / finalTargetSize.x);
float downsampleScaleY = ((float)srcMipHeight / finalTargetSize.y);
if (usePixelShader)
{
// Downsample.
renderGraph.AddBlitPass(destination, tempDownsamplePyramid, new(downsampleScaleX, downsampleScaleY), Vector2.zero, sourceMip: srcMipLevel, passName: "Color Pyramid - Downsample");
// In this mip generation process, source viewport can be smaller than the source render target itself because of the RTHandle system
// We are not using the scale provided by the RTHandle system for two reasons:
// - Source might be a planar probe which will not be scaled by the system (since it's actually the final target of probe rendering at the exact size)
// - When computing mip size, depending on even/odd sizes, the scale computed for mip 0 might miss a texel at the border.
// This can result in a shift in the mip map downscale that depends on the render target size rather than the actual viewport
// (Two rendering at the same viewport size but with different RTHandle reference size would yield different results which can break automated testing)
// So in the end we compute a specific scale for downscale and blur passes at each mip level.
// Scales for Blur
// Same size as m_TempColorTargets which is the source for vertical blur
desc.name = "Temp Gaussian Pyramid Target";
desc.slices = sourceIsArray ? TextureXR.slices : 1;
desc.dimension = descSrc.dimension;
desc.filterMode = FilterMode.Bilinear;
desc.colorFormat = descDst.colorFormat;
desc.enableRandomWrite = false;
desc.useMipMap = false;
var tempColorTarget = renderGraph.CreateTexture(desc);
var hardwareBlurSourceTextureSize = new Vector2Int(dstMipWidth, dstMipHeight);
if (isHardwareDrsOn)
hardwareBlurSourceTextureSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(hardwareBlurSourceTextureSize);
float blurSourceTextureWidth = (float)hardwareBlurSourceTextureSize.x;
float blurSourceTextureHeight = (float)hardwareBlurSourceTextureSize.y;
float scaleX = ((float)dstMipWidth / blurSourceTextureWidth);
float scaleY = ((float)dstMipHeight / blurSourceTextureHeight);
// Blur horizontal.
using (var builder = renderGraph.AddRasterRenderPass<PassDataMipChainRaster>("Color Pyramid - Horizontal Blur", out var passData))
{
passData.propertyBlock = m_PropertyBlockBlur;
passData.source = tempDownsamplePyramid;
passData.material = m_ColorPyramidPSMat;
passData.dstMipWidth = dstMipWidth;
passData.dstMipHeight = dstMipHeight;
passData.scaleX = scaleX;
passData.scaleY = scaleY;
passData.blurSourceTextureWidth = blurSourceTextureWidth;
passData.blurSourceTextureHeight = blurSourceTextureHeight;
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorPSKeyword;
passData.sourceIsArray = sourceIsArray;
builder.UseTexture(passData.source, AccessFlags.Read);
builder.SetRenderAttachment(tempColorTarget, 0);
builder.SetRenderFunc<PassDataMipChainRaster>(static (data, context) => MipChainRasterBlurExecutePass(data, context, false));
}
// Blur vertical.
using (var builder = renderGraph.AddRasterRenderPass<PassDataMipChainRaster>("Color Pyramid - Vertical Blur", out var passData))
{
passData.propertyBlock = m_PropertyBlockBlur;
passData.source = tempColorTarget;
passData.material = m_ColorPyramidPSMat;
passData.dstMipWidth = dstMipWidth;
passData.dstMipHeight = dstMipHeight;
passData.scaleX = scaleX;
passData.scaleY = scaleY;
passData.blurSourceTextureWidth = blurSourceTextureWidth;
passData.blurSourceTextureHeight = blurSourceTextureHeight;
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorPSKeyword;
passData.sourceIsArray = sourceIsArray;
builder.UseTexture(passData.source, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write, srcMipLevel + 1, -1);
builder.SetRenderFunc<PassDataMipChainRaster>(static (data, context) => MipChainRasterBlurExecutePass(data, context, true));
}
}
else
{
using (var builder = renderGraph.AddComputePass<PassDataMipChainCompute>("Color Pyramid - MIP Chain (Compute)", out var passData))
{
passData.numTheadGroupX = (dstMipWidth + 7) / 8;
passData.numTheadGroupY = (dstMipHeight + 7) / 8;
passData.numTheadGroupZ = TextureXR.slices;
passData.srcMipWidth = srcMipWidth;
passData.srcMipHeight = srcMipHeight;
passData.dstMipWidth = dstMipWidth;
passData.dstMipHeight = dstMipHeight;
passData.srcMipLevel = srcMipLevel;
passData.tempDownsamplePyramid = tempDownsamplePyramid;
passData.destination = destination;
passData.cs = m_ColorPyramidCS;
passData.disableTexture2DArrayKeyword = m_DisableTexture2DArrayColorKeyword;
passData.downsampleKernel = m_ColorDownsampleKernel;
passData.gaussianKernel = m_ColorGaussianKernel;
passData.sourceIsArray = sourceIsArray;
// Enable global state modification so we can set keywords.
builder.AllowGlobalStateModification(true);
builder.UseTexture(passData.tempDownsamplePyramid, AccessFlags.ReadWrite);
builder.UseTexture(destination, AccessFlags.ReadWrite);
builder.SetRenderFunc(static (PassDataMipChainCompute data, ComputeGraphContext context) =>
{
context.cmd.SetKeyword(data.cs, data.disableTexture2DArrayKeyword, !data.sourceIsArray);
// Downsample.
context.cmd.SetComputeVectorParam(data.cs, _Size, new Vector4(data.srcMipWidth, data.srcMipHeight, 0f, 0f));
context.cmd.SetComputeTextureParam(data.cs, data.downsampleKernel, _Source, data.destination, data.srcMipLevel);
context.cmd.SetComputeTextureParam(data.cs, data.downsampleKernel, _Destination, data.tempDownsamplePyramid);
context.cmd.DispatchCompute(data.cs, data.downsampleKernel, data.numTheadGroupX, data.numTheadGroupY, data.numTheadGroupZ);
// Single pass blur
context.cmd.SetComputeVectorParam(data.cs, _Size, new Vector4(data.dstMipWidth, data.dstMipHeight, 0f, 0f));
context.cmd.SetComputeTextureParam(data.cs, data.gaussianKernel, _Source, data.tempDownsamplePyramid);
context.cmd.SetComputeTextureParam(data.cs, data.gaussianKernel, _Destination, data.destination, data.srcMipLevel + 1);
context.cmd.DispatchCompute(data.cs, data.gaussianKernel, data.numTheadGroupX, data.numTheadGroupY, data.numTheadGroupZ);
});
}
}
srcMipLevel++;
srcMipWidth = dstMipWidth;
srcMipHeight = dstMipHeight;
finalTargetSize.x >>= 1;
finalTargetSize.y >>= 1;
}
return srcMipLevel + 1;
}
static void MipChainRasterBlurExecutePass(PassDataMipChainRaster data, RasterGraphContext context, bool isVertical)
{
data.propertyBlock.SetTexture(_Source, data.source);
data.propertyBlock.SetVector(_SrcScaleBias, new Vector4(data.scaleX, data.scaleY, 0f, 0f));
data.propertyBlock.SetVector(_SrcUvLimits, new Vector4((data.dstMipWidth - 0.5f) / data.blurSourceTextureWidth, (data.dstMipHeight - 0.5f) / data.blurSourceTextureHeight, !isVertical ? 1.0f / data.blurSourceTextureWidth : 0f, isVertical ? 1.0f / data.blurSourceTextureHeight : 0));
data.propertyBlock.SetFloat(_SourceMip, 0);
data.material.SetKeyword(data.disableTexture2DArrayKeyword, !data.sourceIsArray);
context.cmd.SetViewport(new Rect(0, 0, data.dstMipWidth, data.dstMipHeight));
context.cmd.DrawProcedural(Matrix4x4.identity, data.material, 0, MeshTopology.Triangles, 3, 1, data.propertyBlock);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68deae0b402bcaa43a6f4fb81a956eaa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c88990944d5cd446b5b7b917b2c85a9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,188 @@
//
// This is a modified version of the BlurCS compute shader from Microsoft's MiniEngine
// library. The copyright notice from the original version is included below.
//
// The original source code of MiniEngine is available on GitHub.
// https://github.com/Microsoft/DirectX-Graphics-Samples
//
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
// Developed by Minigraph
//
// Author: Bob Brown
//
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch switch2 glcore gles3 webgpu
#pragma kernel ColorGaussian KERNEL_SIZE=8 MAIN_GAUSSIAN=ColorGaussian
#pragma kernel ColorDownsample KERNEL_SIZE=8 MAIN_DOWNSAMPLE=ColorDownsample
#pragma multi_compile_local _ DISABLE_TEXTURE2D_X_ARRAY
//#define NO_ALPHA
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureXR.hlsl"
RW_TEXTURE2D_X(unorm float4, _Source);
RW_TEXTURE2D_X(unorm float4, _Destination);
CBUFFER_START(cb)
float4 _Size; // x: src width, y: src height, zw: unused
CBUFFER_END
// 16x16 pixels with an 8x8 center that we will be blurring writing out. Each uint is two color
// channels packed together.
// The reason for separating channels is to reduce bank conflicts in the local data memory
// controller. A large stride will cause more threads to collide on the same memory bank.
groupshared uint gs_cacheR[128];
groupshared uint gs_cacheG[128];
groupshared uint gs_cacheB[128];
#if !defined(NO_ALPHA)
groupshared uint gs_cacheA[128];
#endif
float4 BlurPixels(float4 a, float4 b, float4 c, float4 d, float4 e, float4 f, float4 g, float4 h, float4 i)
{
return 0.27343750 * (e )
+ 0.21875000 * (d + f)
+ 0.10937500 * (c + g)
+ 0.03125000 * (b + h)
+ 0.00390625 * (a + i);
}
void Store2Pixels(uint index, float4 pixel1, float4 pixel2)
{
gs_cacheR[index] = f32tof16(pixel1.r) | f32tof16(pixel2.r) << 16;
gs_cacheG[index] = f32tof16(pixel1.g) | f32tof16(pixel2.g) << 16;
gs_cacheB[index] = f32tof16(pixel1.b) | f32tof16(pixel2.b) << 16;
#if !defined(NO_ALPHA)
gs_cacheA[index] = f32tof16(pixel1.a) | f32tof16(pixel2.a) << 16;
#endif
}
void Load2Pixels(uint index, out float4 pixel1, out float4 pixel2)
{
uint rr = gs_cacheR[index];
uint gg = gs_cacheG[index];
uint bb = gs_cacheB[index];
#if !defined(NO_ALPHA)
uint aa = gs_cacheA[index];
#else
uint aa = 0;
#endif
pixel1 = float4(f16tof32(rr ), f16tof32(gg ), f16tof32(bb ), f16tof32(aa ));
pixel2 = float4(f16tof32(rr >> 16), f16tof32(gg >> 16), f16tof32(bb >> 16), f16tof32(aa >> 16));
}
void Store1Pixel(uint index, float4 pixel)
{
gs_cacheR[index] = asuint(pixel.r);
gs_cacheG[index] = asuint(pixel.g);
gs_cacheB[index] = asuint(pixel.b);
#if !defined(NO_ALPHA)
gs_cacheA[index] = asuint(pixel.a);
#endif
}
void Load1Pixel(uint index, out float4 pixel)
{
#if defined(NO_ALPHA)
uint a = 0;
#else
uint a = gs_cacheA[index];
#endif
pixel = asfloat(uint4(gs_cacheR[index], gs_cacheG[index], gs_cacheB[index], a));
}
// Blur two pixels horizontally. This reduces LDS reads and pixel unpacking.
void BlurHorizontally(uint outIndex, uint leftMostIndex)
{
float4 s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
Load2Pixels(leftMostIndex + 0, s0, s1);
Load2Pixels(leftMostIndex + 1, s2, s3);
Load2Pixels(leftMostIndex + 2, s4, s5);
Load2Pixels(leftMostIndex + 3, s6, s7);
Load2Pixels(leftMostIndex + 4, s8, s9);
Store1Pixel(outIndex , BlurPixels(s0, s1, s2, s3, s4, s5, s6, s7, s8));
Store1Pixel(outIndex + 1, BlurPixels(s1, s2, s3, s4, s5, s6, s7, s8, s9));
}
void BlurVertically(uint2 pixelCoord, uint topMostIndex)
{
float4 s0, s1, s2, s3, s4, s5, s6, s7, s8;
Load1Pixel(topMostIndex , s0);
Load1Pixel(topMostIndex + 8, s1);
Load1Pixel(topMostIndex + 16, s2);
Load1Pixel(topMostIndex + 24, s3);
Load1Pixel(topMostIndex + 32, s4);
Load1Pixel(topMostIndex + 40, s5);
Load1Pixel(topMostIndex + 48, s6);
Load1Pixel(topMostIndex + 56, s7);
Load1Pixel(topMostIndex + 64, s8);
float4 blurred = BlurPixels(s0, s1, s2, s3, s4, s5, s6, s7, s8);
// Write to the final target
_Destination[COORD_TEXTURE2D_X(pixelCoord)] = blurred;
}
[numthreads(KERNEL_SIZE, KERNEL_SIZE, 1)]
void MAIN_GAUSSIAN(uint2 groupId : SV_GroupID, uint2 groupThreadId : SV_GroupThreadID, uint3 dispatchThreadId : SV_DispatchThreadID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
// Upper-left pixel coordinate of quad that this thread will read
int2 threadUL = (groupThreadId << 1) + (groupId << 3) - 4;
uint2 uthreadUL = uint2(max(0, threadUL));
uint2 size = uint2(_Size.xy) - 1u;
float4 p00 = _Source[COORD_TEXTURE2D_X(min(uthreadUL + uint2(0u, 0u), size))];
float4 p10 = _Source[COORD_TEXTURE2D_X(min(uthreadUL + uint2(1u, 0u), size))];
float4 p11 = _Source[COORD_TEXTURE2D_X(min(uthreadUL + uint2(1u, 1u), size))];
float4 p01 = _Source[COORD_TEXTURE2D_X(min(uthreadUL + uint2(0u, 1u), size))];
// Store the 4 downsampled pixels in LDS
uint destIdx = groupThreadId.x + (groupThreadId.y << 4u);
Store2Pixels(destIdx , p00, p10);
Store2Pixels(destIdx + 8u, p01, p11);
GroupMemoryBarrierWithGroupSync();
// Horizontally blur the pixels in LDS
uint row = groupThreadId.y << 4u;
BlurHorizontally(row + (groupThreadId.x << 1u), row + groupThreadId.x + (groupThreadId.x & 4u));
GroupMemoryBarrierWithGroupSync();
// Vertically blur the pixels in LDS and write the result to memory
BlurVertically(dispatchThreadId.xy, (groupThreadId.y << 3u) + groupThreadId.x);
}
[numthreads(KERNEL_SIZE, KERNEL_SIZE, 1)]
void MAIN_DOWNSAMPLE(uint3 dispatchThreadId : SV_DispatchThreadID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
uint2 offset = dispatchThreadId.xy * 2u;
uint2 size = uint2(_Size.xy) - 1u;
uint2 c00 = min(offset + uint2(0u, 0u), size);
uint2 c10 = min(offset + uint2(1u, 0u), size);
uint2 c11 = min(offset + uint2(1u, 1u), size);
uint2 c01 = min(offset + uint2(0u, 1u), size);
float4 p00 = _Source[COORD_TEXTURE2D_X(c00)];
float4 p10 = _Source[COORD_TEXTURE2D_X(c10)];
float4 p11 = _Source[COORD_TEXTURE2D_X(c11)];
float4 p01 = _Source[COORD_TEXTURE2D_X(c01)];
_Destination[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = (p00 + p01 + p11 + p10) * 0.25;
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 28969cfddd9ef3f4e94b971690146a4f
timeCreated: 1503754250
licenseType: Pro
ComputeShaderImporter:
currentAPIMask: 131076
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
#pragma multi_compile_local _ DISABLE_TEXTURE2D_X_ARRAY
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
TEXTURE2D_X_HALF(_Source);
uniform float4 _SrcScaleBias;
uniform float4 _SrcUvLimits; // {xy: max uv, zw: offset of blur for 1 texel }
uniform half _SourceMip;
struct Attributes
{
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID) * _SrcScaleBias.xy + _SrcScaleBias.zw;
return output;
}
half4 Frag(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Gaussian weights for 9 texel kernel from center textel to furthest texel. Keep in sync with ColorPyramid.compute
const half gaussWeights[] = { 0.27343750, 0.21875000, 0.10937500, 0.03125000, 0.00390625 };
float2 offset = _SrcUvLimits.zw;
float2 offset1 = offset * (1.0 + (gaussWeights[2] / (gaussWeights[1] + gaussWeights[2])));
float2 offset2 = offset * (3.0 + (gaussWeights[4] / (gaussWeights[3] + gaussWeights[4])));
float2 uv_m2 = input.texcoord.xy - offset2;
float2 uv_m1 = input.texcoord.xy - offset1;
float2 uv_p0 = input.texcoord.xy;
float2 uv_p1 = min(_SrcUvLimits.xy, input.texcoord.xy + offset1);
float2 uv_p2 = min(_SrcUvLimits.xy, input.texcoord.xy + offset2);
return
+ SAMPLE_TEXTURE2D_X_LOD(_Source, sampler_LinearClamp, uv_m2, _SourceMip) * (gaussWeights[3] + gaussWeights[4])
+ SAMPLE_TEXTURE2D_X_LOD(_Source, sampler_LinearClamp, uv_m1, _SourceMip) * (gaussWeights[1] + gaussWeights[2])
+ SAMPLE_TEXTURE2D_X_LOD(_Source, sampler_LinearClamp, uv_p0, _SourceMip) * gaussWeights[0]
+ SAMPLE_TEXTURE2D_X_LOD(_Source, sampler_LinearClamp, uv_p1, _SourceMip) * (gaussWeights[1] + gaussWeights[2])
+ SAMPLE_TEXTURE2D_X_LOD(_Source, sampler_LinearClamp, uv_p2, _SourceMip) * (gaussWeights[3] + gaussWeights[4]);
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 95152aa728f8eec4d9d5c1988ddd90e6
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
Shader "Hidden/ColorPyramidPS"
{
SubShader
{
Tags{ "RenderPipeline" = "UniversalPipeline" }
Pass
{
ZWrite Off ZTest Always Blend Off Cull Off
ColorMask RGBA
HLSLPROGRAM
#pragma editor_sync_compilation
#pragma target 2.0
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch switch2 glcore gles3 webgpu
#pragma vertex Vert
#pragma fragment Frag
#include_with_pragmas "ColorPyramidPS.hlsl"
ENDHLSL
}
}
Fallback Off
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 63b92baba208e314c8fdebe52ca0cc34
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,130 @@
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch switch2 glcore gles3 webgpu
#pragma multi_compile_local _ ENABLE_CHECKERBOARD
#pragma multi_compile_local _ DISABLE_TEXTURE2D_X_ARRAY
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureXR.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Runtime/MipGen/Shaders/DepthPyramidConstants.cs.hlsl"
#pragma kernel DepthDownsample
RW_TEXTURE2D_X(float, _DepthMipChain);
#if UNITY_REVERSED_Z
#define MIN_DEPTH(A, B) max((A), (B))
#define MIN3_DEPTH(A, B, C) Max3((A), (B), (C))
#define MAX_DEPTH(A, B) min((A), (B))
#define MAX3_DEPTH(A, B, C) Min3((A), (B), (C))
#else
#define MIN_DEPTH(A, B) min((A), (B))
#define MIN3_DEPTH(A, B, C) Min3((A), (B), (C))
#define MAX_DEPTH(A, B) max((A), (B))
#define MAX3_DEPTH(A, B, C) Max3((A), (B), (C))
#endif
uint2 CoordInTileByIndex(uint i)
{
// decode i = [yxxyyx] (we want each pair of bits to have an x and a y)
return uint2(
(i & 1) | ((i >> 2) & 6),
((i >> 1) & 3) | ((i >> 3) & 4));
}
groupshared float s_minDepth[32];
#ifdef ENABLE_CHECKERBOARD
groupshared float s_maxDepth[32];
#endif
void SubgroupMergeDepths(uint threadID, uint bitIndex, inout float minDepth, inout float maxDepth)
{
uint highIndex = threadID >> (bitIndex + 1);
uint lowIndex = threadID & ((1 << (bitIndex + 1)) - 1);
if (lowIndex == (1 << bitIndex))
{
s_minDepth[highIndex] = minDepth;
#ifdef ENABLE_CHECKERBOARD
s_maxDepth[highIndex] = maxDepth;
#endif
}
GroupMemoryBarrierWithGroupSync();
if (lowIndex == 0)
{
minDepth = MIN_DEPTH(minDepth, s_minDepth[highIndex]);
#ifdef ENABLE_CHECKERBOARD
maxDepth = MAX_DEPTH(maxDepth, s_maxDepth[highIndex]);
#endif
}
GroupMemoryBarrierWithGroupSync();
}
float CheckerboardDepth(uint2 coord, float minDepth, float maxDepth)
{
return ((coord.x ^ coord.y) & 1) ? minDepth : maxDepth;
}
// Downsample a depth texture by taking the min value of sampled pixels
[numthreads(64, 1, 1)]
void DepthDownsample(uint threadID : SV_GroupThreadID, uint3 groupID : SV_GroupID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(groupID.z);
// assign threads to pixels in a swizzle-like pattern
int2 dstCoord0 = (groupID.xy << 3) | CoordInTileByIndex(threadID);
int2 readOffsetUL = dstCoord0 << 1;
float p00 = _DepthMipChain[COORD_TEXTURE2D_X(_SrcOffset + min(readOffsetUL + int2(0, 0), _SrcLimit))];
float p10 = _DepthMipChain[COORD_TEXTURE2D_X(_SrcOffset + min(readOffsetUL + int2(1, 0), _SrcLimit))];
float p01 = _DepthMipChain[COORD_TEXTURE2D_X(_SrcOffset + min(readOffsetUL + int2(0, 1), _SrcLimit))];
float p11 = _DepthMipChain[COORD_TEXTURE2D_X(_SrcOffset + min(readOffsetUL + int2(1, 1), _SrcLimit))];
float minDepth = MIN3_DEPTH(p00, p10, MIN_DEPTH(p01, p11));
float maxDepth = MAX3_DEPTH(p00, p10, MAX_DEPTH(p01, p11));
// write dst0
if (all(dstCoord0 < _DstSize0))
{
_DepthMipChain[COORD_TEXTURE2D_X(_MinDstOffset0 + dstCoord0)] = minDepth;
#ifdef ENABLE_CHECKERBOARD
if (_CbDstCount >= 1)
_DepthMipChain[COORD_TEXTURE2D_X(_CbDstOffset0 + dstCoord0)] = CheckerboardDepth(dstCoord0, minDepth, maxDepth);
#endif
}
// merge to thread 0 in subgroup size 4
SubgroupMergeDepths(threadID, 0, minDepth, maxDepth);
SubgroupMergeDepths(threadID, 1, minDepth, maxDepth);
if (_MinDstCount >= 2 && (threadID & 0x3) == 0)
{
int2 dstCoord1 = dstCoord0 >> 1;
if (all(dstCoord1 < _DstSize1))
{
_DepthMipChain[COORD_TEXTURE2D_X(_MinDstOffset1 + dstCoord1)] = minDepth;
#ifdef ENABLE_CHECKERBOARD
if (_CbDstCount >= 2)
_DepthMipChain[COORD_TEXTURE2D_X(_CbDstOffset1 + dstCoord1)] = CheckerboardDepth(dstCoord1, minDepth, maxDepth);
#endif
}
}
// merge to thread 0 in subgroup size 16
SubgroupMergeDepths(threadID, 2, minDepth, maxDepth);
SubgroupMergeDepths(threadID, 3, minDepth, maxDepth);
if (_MinDstCount >= 3 && (threadID & 0xf) == 0)
{
int2 dstCoord2 = dstCoord0 >> 2;
if (all(dstCoord2 < _DstSize2))
_DepthMipChain[COORD_TEXTURE2D_X(_MinDstOffset2 + dstCoord2)] = minDepth;
}
// merge to thread 0
SubgroupMergeDepths(threadID, 4, minDepth, maxDepth);
SubgroupMergeDepths(threadID, 5, minDepth, maxDepth);
if (_MinDstCount >= 4 && (threadID & 0x3f) == 0)
{
int2 dstCoord3 = dstCoord0 >> 3;
if (all(dstCoord3 < _DstSize3))
_DepthMipChain[COORD_TEXTURE2D_X(_MinDstOffset3 + dstCoord3)] = minDepth;
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 90a204702cd44e54cac7ed4fc6e5d86b
timeCreated: 1506516404
licenseType: Pro
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
namespace UnityEngine.Rendering
{
[GenerateHLSL(needAccessors = false, generateCBuffer = true)]
internal unsafe struct DepthPyramidConstants
{
public uint _MinDstCount;
public uint _CbDstCount;
public uint _DepthPyramidPad0;
public uint _DepthPyramidPad1;
public Vector2Int _SrcOffset;
public Vector2Int _SrcLimit;
public Vector2Int _DstSize0;
public Vector2Int _DstSize1;
public Vector2Int _DstSize2;
public Vector2Int _DstSize3;
public Vector2Int _MinDstOffset0;
public Vector2Int _MinDstOffset1;
public Vector2Int _MinDstOffset2;
public Vector2Int _MinDstOffset3;
public Vector2Int _CbDstOffset0;
public Vector2Int _CbDstOffset1;
}
}
@@ -0,0 +1,29 @@
//
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
//
#ifndef DEPTHPYRAMIDCONSTANTS_CS_HLSL
#define DEPTHPYRAMIDCONSTANTS_CS_HLSL
// Generated from UnityEngine.Rendering.DepthPyramidConstants
// PackingRules = Exact
CBUFFER_START(DepthPyramidConstants)
uint _MinDstCount;
uint _CbDstCount;
uint _DepthPyramidPad0;
uint _DepthPyramidPad1;
int2 _SrcOffset;
int2 _SrcLimit;
int2 _DstSize0;
int2 _DstSize1;
int2 _DstSize2;
int2 _DstSize3;
int2 _MinDstOffset0;
int2 _MinDstOffset1;
int2 _MinDstOffset2;
int2 _MinDstOffset3;
int2 _CbDstOffset0;
int2 _CbDstOffset1;
CBUFFER_END
#endif
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dda90eae3301de545856ee63771154fd
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f6fd62f76461a0429f9b8049a8f339c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: