public void CollectVoxelizationPasses(ProcessedVoxelVolume data, VoxelStorageContext storageContext) { Matrix BaseVoxelMatrix = storageContext.Matrix; BaseVoxelMatrix.Invert(); BaseVoxelMatrix = BaseVoxelMatrix * Matrix.Scaling(2f, 2f, 2f); if (UpdatesPerFrame != UpdateMethods.AllClipmapsMultipleRenders) { /* * Having trouble with shadow culling when this is enabled - currently performed in vertex shader instead * if (UpdateMethod == UpdateMethods.OneClipPerFrame) * { * BaseVoxelMatrix = Matrix.Scaling(PerMapOffsetScale[ClipMapCurrent].W) * Matrix.Translation(PerMapOffsetScale[ClipMapCurrent].XYZ()); * BaseVoxelMatrix = BaseVoxelMatrix * Matrix.Translation(-0.5f,-0.5f,-0.5f); * BaseVoxelMatrix = BaseVoxelMatrix * Matrix.Scaling(2f, 2f, 2f); * } */ VoxelStorerClipmap Storer = new VoxelStorerClipmap { storageUints = storageUints, FragmentsBuffer = FragmentsBuffer, ClipMapCount = ClipMapCount, ClipMapCurrent = ClipMapCurrent, ClipMapResolution = ClipMapResolution, PerMapOffsetScale = PerMapOffsetScale, UpdatesPerFrame = UpdatesPerFrame }; foreach (var attr in data.Attributes) { attr.Attribute.CollectVoxelizationPasses(data.passList, Storer, BaseVoxelMatrix, ClipMapResolution, attr.Stage, attr.Output); } } if (UpdatesPerFrame == UpdateMethods.AllClipmapsMultipleRenders) { for (int i = 0; i < ClipMapCount; i++) { VoxelStorerClipmap Storer = new VoxelStorerClipmap { storageUints = storageUints, FragmentsBuffer = FragmentsBuffer, ClipMapCount = ClipMapCount, ClipMapCurrent = i, ClipMapResolution = ClipMapResolution, PerMapOffsetScale = PerMapOffsetScale, UpdatesPerFrame = UpdateMethods.SingleClipmap }; foreach (var attr in data.Attributes) { attr.Attribute.CollectVoxelizationPasses(data.passList, Storer, BaseVoxelMatrix, ClipMapResolution, attr.Stage, attr.Output); } } } }
public ShaderSource GetVoxelizationShader(VoxelizationPass pass, ProcessedVoxelVolume data) { bool singleClip = UpdatesOneClipPerFrame(); ShaderSource VoxelizationMethodSource = pass.method.GetVoxelizationShader(); ShaderMixinSource cachedMixin = new ShaderMixinSource(); cachedMixin.Mixins.Add(storage); cachedMixin.AddComposition("method", VoxelizationMethodSource); if (singleClip) { cachedMixin.AddMacro("singleClip", true); } string IndirectStoreMacro = ""; for (int i = 0; i < pass.AttributesIndirect.Count; i++) { string iStr = i.ToString(); IndirectStoreMacro += $"AttributesIndirect[{iStr}].IndirectWrite(fragmentsBuffer, writeindex + {pass.AttributesIndirect[i].BufferOffset});\n"; } cachedMixin.AddMacro("IndirectStoreMacro", IndirectStoreMacro); foreach (var attr in pass.AttributesTemp) { cachedMixin.AddCompositionToArray("AttributesTemp", attr.GetVoxelizationShader()); } foreach (var attr in pass.AttributesDirect) { cachedMixin.AddCompositionToArray("AttributesDirect", attr.GetVoxelizationShader()); } foreach (var attr in pass.AttributesIndirect) { cachedMixin.AddCompositionToArray("AttributesIndirect", attr.GetVoxelizationShader()); } return(cachedMixin); }
public void PostProcess(VoxelStorageContext context, RenderDrawContext drawContext, ProcessedVoxelVolume data) { }
public void PostProcess(VoxelStorageContext storageContext, RenderDrawContext drawContext, ProcessedVoxelVolume data) { if (Math.Max(Math.Max(ClipMapResolution.X, ClipMapResolution.Y), ClipMapResolution.Z) < 32) { return; } if (FragmentsBuffer == null) { return; } var context = drawContext.RenderContext; if (ClearBuffer == null) { ClearBuffer = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "ClearBuffer" }; BufferToTexture = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "BufferToTextureEffect" }; BufferToTextureColumns = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "BufferToTextureColumnsEffect" }; } bool VoxelsAreIndependent = true; List <VoxelAttribute> IndirectVoxels = new List <VoxelAttribute>(); List <VoxelAttribute> TempVoxels = new List <VoxelAttribute>(); ShaderSourceCollection Indirect = new ShaderSourceCollection(); ShaderSourceCollection Temp = new ShaderSourceCollection(); //Assign sample indices and check whether voxels can be calculated independently int sampleIndex = 0; foreach (var attr in data.Attributes) { attr.Attribute.LocalSamplerID = sampleIndex; VoxelsAreIndependent &= !attr.Attribute.RequiresColumns(); sampleIndex++; } //Populate ShaderSourceCollections and temp lists foreach (var attr in data.Attributes) { if (attr.Stage != VoxelizationStage.Post) { continue; } if (attr.Output) { Indirect.Add(attr.Attribute.GetVoxelizationShader()); IndirectVoxels.Add(attr.Attribute); } else { Temp.Add(attr.Attribute.GetVoxelizationShader()); TempVoxels.Add(attr.Attribute); } } var BufferWriter = VoxelsAreIndependent ? BufferToTexture : BufferToTextureColumns; for (int i = 0; i < IndirectVoxels.Count; i++) { var attr = IndirectVoxels[i]; attr.UpdateVoxelizationLayout($"AttributesIndirect[{i}]"); } for (int i = 0; i < TempVoxels.Count; i++) { var attr = TempVoxels[i]; attr.UpdateVoxelizationLayout($"AttributesTemp[{i}]"); } foreach (var attr in data.Attributes) { attr.Attribute.ApplyVoxelizationParameters(BufferWriter.Parameters); } int processYSize = VoxelsAreIndependent ? (int)ClipMapResolution.Y : 1; processYSize *= (UpdatesPerFrame == UpdateMethods.SingleClipmap) ? 1 : ClipMapCount; BufferWriter.ThreadGroupCounts = VoxelsAreIndependent ? new Int3(32, 32, 32) : new Int3(32, 1, 32); BufferWriter.ThreadNumbers = new Int3((int)ClipMapResolution.X / BufferWriter.ThreadGroupCounts.X, processYSize / BufferWriter.ThreadGroupCounts.Y, (int)ClipMapResolution.Z / BufferWriter.ThreadGroupCounts.Z); BufferWriter.Parameters.Set(BufferToTextureKeys.VoxelFragments, FragmentsBuffer); BufferWriter.Parameters.Set(BufferToTextureKeys.clipMapResolution, ClipMapResolution); BufferWriter.Parameters.Set(BufferToTextureKeys.storageUints, storageUints); BufferWriter.Parameters.Set(BufferToTextureKeys.clipOffset, (uint)(UpdatesPerFrame == UpdateMethods.SingleClipmap ? ClipMapCurrent : 0)); //Modifiers are stored within attributes, yet need to be able to query their results. //Ideally a stage stream could resolve this, however due to the lack of pointers, there would be a cyclic dependency of AttributesList->Attribute->Modifier->AttributesList->... //So instead the results will be stored within a second array that only contains float4s. Unfortunately the only way to iterate through the AttributesList is by foreach, which //makes it difficult to access the results array (AttributeLocalSamples) by index. So instead it's just all done through this macro... string IndirectReadAndStoreMacro = ""; string IndirectStoreMacro = ""; for (int i = 0; i < Temp.Count; i++) { string iStr = i.ToString(); string sampleIndexStr = TempVoxels[i].LocalSamplerID.ToString(); IndirectReadAndStoreMacro += $"AttributesTemp[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {TempVoxels[i].BufferOffset}, uint2({TempVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" + $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n\n"; IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n"; } for (int i = 0; i < Indirect.Count; i++) { string iStr = i.ToString(); string sampleIndexStr = IndirectVoxels[i].LocalSamplerID.ToString(); IndirectReadAndStoreMacro += $"AttributesIndirect[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {IndirectVoxels[i].BufferOffset}, uint2({IndirectVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" + $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n\n"; IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n"; } BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesIndirect, Indirect); BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesTemp, Temp); BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectReadAndStoreMacro, IndirectReadAndStoreMacro); BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectStoreMacro, IndirectStoreMacro); ((RendererBase)BufferWriter).Draw(drawContext); ClearBuffer.Parameters.Set(ClearBufferKeys.buffer, FragmentsBuffer); if (UpdatesPerFrame != UpdateMethods.SingleClipmap) { //Clear all ClearBuffer.ThreadNumbers = new Int3(1024, 1, 1); ClearBuffer.ThreadGroupCounts = new Int3(FragmentsBuffer.ElementCount / 1024, 1, 1); ClearBuffer.Parameters.Set(ClearBufferKeys.offset, 0); } else { //Clear next clipmap buffer ClearBuffer.ThreadNumbers = new Int3(1024, 1, 1); ClearBuffer.ThreadGroupCounts = new Int3((int)(ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints) / 1024, 1, 1); ClearBuffer.Parameters.Set(ClearBufferKeys.offset, (int)(((ClipMapCurrent + 1) % ClipMapCount) * ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints)); } ((RendererBase)ClearBuffer).Draw(drawContext); }
public virtual void Collect(RenderContext Context, Shadows.IShadowMapRenderer ShadowMapRenderer) { renderVoxelVolumes = Context.VisibilityGroup.Tags.Get(CurrentRenderVoxelVolumes); renderVoxelVolumeData = Context.VisibilityGroup.Tags.Get(CurrentProcessedVoxelVolumes); if (renderVoxelVolumes == null || renderVoxelVolumes.Count == 0) { return; } if (Context.RenderSystem.GraphicsDevice.Features.CurrentProfile < GraphicsProfile.Level_11_0) { throw new ArgumentOutOfRangeException("Graphics Profile Level 11 or higher required for Voxelization."); } //Setup per volume passes and texture allocations foreach (var pair in renderVoxelVolumes) { var dataVolume = pair.Value; var bounds = dataVolume.VolumeSize; ProcessedVoxelVolume processedVolume; if (!renderVoxelVolumeData.TryGetValue(pair.Key, out processedVolume)) { processedVolume = new ProcessedVoxelVolume(); renderVoxelVolumeData.Add(pair.Key, processedVolume); } //Setup matrix Vector3 matScale = dataVolume.VolumeSize; Vector3 matTrans = dataVolume.VolumeTranslation; Matrix corMatrix = Matrix.Scaling(matScale) * Matrix.Translation(matTrans); VoxelStorageContext storageContext = new VoxelStorageContext { device = Context.GraphicsDevice, Extents = bounds, VoxelSize = dataVolume.AproxVoxelSize, Matrix = corMatrix }; if (dataVolume.VoxelGridSnapping) { matTrans /= storageContext.RealVoxelSize(); matTrans.X = (float)Math.Floor(matTrans.X); matTrans.Y = (float)Math.Floor(matTrans.Y); matTrans.Z = (float)Math.Floor(matTrans.Z); matTrans *= storageContext.RealVoxelSize(); corMatrix = Matrix.Scaling(matScale) * Matrix.Translation(matTrans); storageContext.Matrix = corMatrix; } storageContext.Translation = matTrans; storageContext.VoxelSpaceTranslation = matTrans / storageContext.RealVoxelSize(); //Update storage dataVolume.Storage.UpdateFromContext(storageContext); //Transfer voxelization info processedVolume.VisualizeVoxels = dataVolume.VisualizeVoxels; processedVolume.Storage = dataVolume.Storage; processedVolume.StorageContext = storageContext; processedVolume.VoxelizationMethod = dataVolume.VoxelizationMethod; processedVolume.VoxelVisualization = dataVolume.VoxelVisualization; processedVolume.VisualizationAttribute = dataVolume.VisualizationAttribute; processedVolume.Voxelize = dataVolume.Voxelize; processedVolume.OutputAttributes = dataVolume.Attributes; processedVolume.Attributes.Clear(); processedVolume.passList.Clear(); processedVolume.groupedPasses.Clear(); processedVolume.passList.defaultVoxelizationMethod = dataVolume.VoxelizationMethod; //Create final list of attributes (including temporary ones) foreach (var attr in dataVolume.Attributes) { attr.CollectAttributes(processedVolume.Attributes, VoxelizationStage.Initial, true); } //Allocate textures and space in the temporary buffer foreach (var attr in processedVolume.Attributes) { attr.Attribute.PrepareLocalStorage(storageContext, dataVolume.Storage); if (attr.Output) { attr.Attribute.PrepareOutputStorage(storageContext, dataVolume.Storage); } else { attr.Attribute.ClearOutputStorage(); } } dataVolume.Storage.UpdateTempStorage(storageContext); //Create list of voxelization passes that need to be done dataVolume.Storage.CollectVoxelizationPasses(processedVolume, storageContext); //Group voxelization passes where the RenderStage can be shared //TODO: Group identical attributes for (int i = 0; i < processedVolume.passList.passes.Count; i++) { bool added = false; var passA = processedVolume.passList.passes[i]; for (int group = 0; group < processedVolume.groupedPasses.Count; group++) { var passB = processedVolume.groupedPasses[group][0]; if ( passB.storer.CanShareRenderStage(passA.storer) && passB.method.CanShareRenderStage(passA.method) && passB.AttributesDirect.SequenceEqual(passA.AttributesDirect) && passB.AttributesIndirect.SequenceEqual(passA.AttributesIndirect) && passB.AttributesTemp.SequenceEqual(passA.AttributesTemp) ) { processedVolume.groupedPasses[group].Add(passA); added = true; break; } } if (!added) { List <VoxelizationPass> newGroup = new List <VoxelizationPass> { passA }; processedVolume.groupedPasses.Add(newGroup); } } if (VoxelStages.Count < processedVolume.groupedPasses.Count) { throw new ArgumentOutOfRangeException(processedVolume.groupedPasses.Count.ToString() + " Render Stages required for voxelization, only " + VoxelStages.Count.ToString() + " provided."); } //Finish preparing the passes, collecting views and setting up shader sources and shadows for (int group = 0; group < processedVolume.groupedPasses.Count; group++) { foreach (var pass in processedVolume.groupedPasses[group]) { pass.renderStage = VoxelStages[group]; pass.source = pass.storer.GetVoxelizationShader(pass, processedVolume); pass.view.RenderStages.Add(pass.renderStage); Context.RenderSystem.Views.Add(pass.view); Context.VisibilityGroup.TryCollect(pass.view); if (pass.requireShadows) { ShadowMapRenderer?.RenderViewsWithShadows.Add(pass.view); } } } } }