public void Build(CommandList commandList, LightSpot lightSpot) { int[] indices; if (lightSpot.ProjectiveTexture != null) // If no projection texture has been supplied, we render the regular cone: { indices = BuildRectangleIndexBuffer(); vertexBuffer = Buffer.Vertex.New(graphicsDevice, new VertexPositionNormalTexture[9], GraphicsResourceUsage.Dynamic); RebuildRectangleVertexBuffer(commandList, lightSpot); } else // If a projection texture has been supplied, we render a rectangular frustum instead: { indices = BuildConeIndexBuffer(); vertexBuffer = Buffer.Vertex.New(graphicsDevice, new VertexPositionNormalTexture[4 * Tesselation + 1], GraphicsResourceUsage.Dynamic); RebuildConeVertexBuffer(commandList, lightSpot); } MeshDraw = new MeshDraw { PrimitiveType = PrimitiveType.LineList, DrawCount = indices.Length, IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices), true, indices.Length), VertexBuffers = new[] { new VertexBufferBinding(vertexBuffer, VertexPositionNormalTexture.Layout, vertexBuffer.ElementCount) }, }; }
public void Build(CommandList commandList, CameraParameters parameters) { var indices = new [] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; vertexBuffer = Buffer.Vertex.New(graphicsDevice, new VertexPositionNormalTexture[8], GraphicsResourceUsage.Dynamic); RebuildVertexBuffer(commandList, parameters); MeshDraw = new MeshDraw { PrimitiveType = PrimitiveType.LineList, DrawCount = indices.Length, IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices), true, indices.Length), VertexBuffers = new[] { new VertexBufferBinding(vertexBuffer, VertexPositionNormalTexture.Layout, vertexBuffer.ElementCount) }, }; }
private void InitEntity(Color color, GraphicsDevice graphicsDevice) { vertexBuffer = Buffer.Vertex.New(this.graphicsDevice, vertexArray, GraphicsResourceUsage.Dynamic); indexBuffer = Buffer.Index.New(this.graphicsDevice, indexArray, GraphicsResourceUsage.Dynamic); var model = new Model { Meshes = { new Mesh { Draw = new MeshDraw { PrimitiveType = PrimitiveType.LineList, VertexBuffers = new[]{ new VertexBufferBinding(vertexBuffer, VertexPositionColorTexture.Layout, vertexArray.Length * VertexPositionColorTexture.Layout.CalculateSize()) }, IndexBuffer = new IndexBufferBinding(indexBuffer, true, indexArray.Length * sizeof(int)), DrawCount = vertexArray.Length } } }, Materials = { Materials.CreateDebugMaterial(color, true, graphicsDevice) } }; Entity = new Entity { Name = color.ToString(), Components = { new ModelComponent(model) } }; }
public void ReleaseBuffers(RenderDrawContext renderDrawContext) { // Release the temporary vertex buffer if (VertexBuffer != null) { renderDrawContext.GraphicsContext.Allocator.ReleaseReference(VertexBuffer); VertexBuffer = null; } }
public static bool DisposeBufferBySpecs(Stride.Graphics.Buffer buf, int count) { if (buf == null || buf.ElementCount != count) { if (buf != null) { buf.Dispose(); } return(true); } return(false); }
public void UpdateTempStorage(VoxelStorageContext context) { storageUints = (tempStorageCounter + 31) / 32; tempStorageCounter = 0; var resolution = ClipMapResolution; int fragments = (int)(resolution.X * resolution.Y * resolution.Z) * ClipMapCount; if (VoxelUtils.DisposeBufferBySpecs(FragmentsBuffer, storageUints * fragments) && storageUints * fragments > 0) { FragmentsBuffer = Stride.Graphics.Buffer.Typed.New(context.device, storageUints * fragments, PixelFormat.R32_UInt, true); } }
public void Build() { var indices = new int[2 * Tesselation * 3]; var vertices = new VertexPositionNormalTexture[(Tesselation + 1) * 3]; int indexCount = 0; int vertexCount = 0; // the two rings for (int j = 0; j < 3; j++) { var rotation = Matrix.Identity; if (j == 1) { rotation = Matrix.RotationX(MathF.PI / 2); } else if (j == 2) { rotation = Matrix.RotationY(MathF.PI / 2); } for (int i = 0; i <= Tesselation; i++) { var longitude = (float)(i * 2.0 * Math.PI / Tesselation); var dx = MathF.Cos(longitude); var dy = MathF.Sin(longitude); var normal = new Vector3(dx, dy, 0); Vector3.TransformNormal(ref normal, ref rotation, out normal); if (i < Tesselation) { indices[indexCount++] = vertexCount; indices[indexCount++] = vertexCount + 1; } vertices[vertexCount++] = new VertexPositionNormalTexture(normal, normal, new Vector2(0)); } } vertexBuffer = Buffer.Vertex.New(graphicsDevice, vertices); MeshDraw = new MeshDraw { PrimitiveType = PrimitiveType.LineList, DrawCount = indices.Length, IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices), true, indices.Length), VertexBuffers = new[] { new VertexBufferBinding(vertexBuffer, VertexPositionNormalTexture.Layout, vertexBuffer.ElementCount) }, }; }
/// <inheritdoc/> protected override void InitializeCore() { base.InitializeCore(); RenderFeatures.CollectionChanged += RenderFeatures_CollectionChanged; foreach (var renderFeature in RenderFeatures) { renderFeature.AttachRootRenderFeature(this); renderFeature.Initialize(Context); } // Create an empty buffer to compensate for missing vertex streams emptyBuffer = Buffer.Vertex.New(Context.GraphicsDevice, new Vector4[1]); }
protected override void Destroy() { foreach (var renderFeature in RenderFeatures) { renderFeature.Dispose(); } RenderFeatures.CollectionChanged -= RenderFeatures_CollectionChanged; descriptorSets.Dispose(); emptyBuffer?.Dispose(); emptyBuffer = null; base.Destroy(); }
private static byte[] GetDataSafe(this Buffer buffer, CommandList commandList = null) { var data = buffer.GetSerializationData(); if (data != null) { return(data.Content); } if (commandList == null) { throw new InvalidOperationException("Could not find underlying CPU buffer data and no command list was given to extract them from GPU"); } return(buffer.GetData <byte>(commandList)); }
public override void Unload() { // Dispose GPU resources lightClusters?.Dispose(); lightClusters = null; lightIndicesBuffer?.Dispose(); lightIndicesBuffer = null; pointLightsBuffer?.Dispose(); pointLightsBuffer = null; spotLightsBuffer?.Dispose(); spotLightsBuffer = null; base.Unload(); }
public void Build() { var indices = new int[12 * 2]; var vertices = new VertexPositionNormalTexture[8]; vertices[0] = new VertexPositionNormalTexture(new Vector3(-1, 1, -1), Vector3.UnitY, Vector2.Zero); vertices[1] = new VertexPositionNormalTexture(new Vector3(-1, 1, 1), Vector3.UnitY, Vector2.Zero); vertices[2] = new VertexPositionNormalTexture(new Vector3(1, 1, 1), Vector3.UnitY, Vector2.Zero); vertices[3] = new VertexPositionNormalTexture(new Vector3(1, 1, -1), Vector3.UnitY, Vector2.Zero); int indexOffset = 0; // Top sides for (int i = 0; i < 4; i++) { indices[indexOffset++] = i; indices[indexOffset++] = (i + 1) % 4; } // Duplicate vertices and indices to bottom part for (int i = 0; i < 4; i++) { vertices[i + 4] = vertices[i]; vertices[i + 4].Position.Y = -vertices[i + 4].Position.Y; indices[indexOffset++] = indices[i * 2] + 4; indices[indexOffset++] = indices[i * 2 + 1] + 4; } // Sides for (int i = 0; i < 4; i++) { indices[indexOffset++] = i; indices[indexOffset++] = i + 4; } vertexBuffer = Buffer.Vertex.New(graphicsDevice, vertices); MeshDraw = new MeshDraw { PrimitiveType = PrimitiveType.LineList, DrawCount = indices.Length, IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices), true, indices.Length), VertexBuffers = new[] { new VertexBufferBinding(vertexBuffer, VertexPositionNormalTexture.Layout, vertexBuffer.ElementCount) }, }; }
// TODO: make this more easy and clear, improve instancing component to support this better protected override void ManageInstancingData() { var transformUsage = (ModelTransformUsage)(((int)Game.UpdateTime.Total.TotalSeconds) % 3); instancingUserBuffer.ModelTransformUsage = ModelTransformUsage.PostMultiply; instancingUserBuffer.InstanceCount = instanceWorldTransformations.Length; // Make sure inverse matrices are big enough if (worldInverseTransformations.Length != instanceWorldTransformations.Length) { worldInverseTransformations = new Matrix[instanceWorldTransformations.Length]; } // Invert matrices and update bounding box var ibb = BoundingBox.Empty; for (int i = 0; i < instanceWorldTransformations.Length; i++) { Matrix.Invert(ref instanceWorldTransformations[i], out worldInverseTransformations[i]); var pos = instanceWorldTransformations[i].TranslationVector; BoundingBox.Merge(ref ibb, ref pos, out ibb); } instancingUserBuffer.BoundingBox = ibb; // Manage buffers if (InstanceWorldBuffer == null || InstanceWorldBuffer.ElementCount < instancingUserBuffer.InstanceCount) { InstanceWorldBuffer?.Dispose(); InstanceWorldInverseBuffer?.Dispose(); InstanceWorldBuffer = CreateMatrixBuffer(GraphicsDevice, instancingUserBuffer.InstanceCount); instancingUserBuffer.InstanceWorldBuffer = InstanceWorldBuffer; InstanceWorldInverseBuffer = CreateMatrixBuffer(GraphicsDevice, instancingUserBuffer.InstanceCount); instancingUserBuffer.InstanceWorldInverseBuffer = InstanceWorldInverseBuffer; } instancingUserBuffer.InstanceWorldBuffer.SetData(Game.GraphicsContext.CommandList, instanceWorldTransformations); instancingUserBuffer.InstanceWorldInverseBuffer.SetData(Game.GraphicsContext.CommandList, worldInverseTransformations); }
public unsafe void AllocateBuffers(RenderDrawContext renderDrawContext, int vertexBufferSize, int requiredIndexCount) { // Build the shared vertex buffer - every frame if (vertexBufferSize > 0) { { vertexBufferSize--; vertexBufferSize |= vertexBufferSize >> 1; vertexBufferSize |= vertexBufferSize >> 2; vertexBufferSize |= vertexBufferSize >> 3; vertexBufferSize |= vertexBufferSize >> 8; vertexBufferSize |= vertexBufferSize >> 16; vertexBufferSize++; } VertexBufferSize = vertexBufferSize; VertexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer( new BufferDescription(VertexBufferSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic)); } // Build the shared index buffer - only when necessary var requiredIndexBufferSize = requiredIndexCount * IndexStride; if (requiredIndexBufferSize > IndexBufferSize) { if (IndexBuffer != null) { renderDrawContext.GraphicsContext.Allocator.ReleaseReference(IndexBuffer); IndexBuffer = null; } // We start allocating from 64K (allowing 32K indices to be written at once - this is most probably going to be sufficient in all cases) IndexBufferSize = requiredIndexBufferSize; if (IndexBufferSize < 64 * 1024) { IndexBufferSize = 64 * 1024; } { IndexBufferSize--; IndexBufferSize |= IndexBufferSize >> 1; IndexBufferSize |= IndexBufferSize >> 2; IndexBufferSize |= IndexBufferSize >> 3; IndexBufferSize |= IndexBufferSize >> 8; IndexBufferSize |= IndexBufferSize >> 16; IndexBufferSize++; } IndexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer( new BufferDescription(IndexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic)); var indexCount = IndexBufferSize / IndexStride; indexCount = ((indexCount / 6) * 6); { var commandList = renderDrawContext.CommandList; var mappedIndices = commandList.MapSubresource(IndexBuffer, 0, MapMode.WriteNoOverwrite, false, 0, IndexBufferSize); var indexPointer = mappedIndices.DataBox.DataPointer; int indexStructSize = sizeof(short); int verticesPerQuad = 4; var k = 0; for (var i = 0; i < indexCount; k += verticesPerQuad) { *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 0); *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 1); *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 2); *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 0); *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 2); *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 3); } commandList.UnmapSubresource(mappedIndices); } } }
private unsafe Mesh ConvertToMesh(GraphicsDevice graphicsDevice, PrimitiveType primitiveType, LightProbeRuntimeData lightProbeRuntimeData) { // Generate data for vertex buffer var vertices = new VertexPositionNormalColor[lightProbeRuntimeData.LightProbes.Length]; for (var i = 0; i < lightProbeRuntimeData.LightProbes.Length; i++) { vertices[i] = new VertexPositionNormalColor(lightProbeRuntimeData.Vertices[i], Vector3.Zero, Color.White); } // Generate data for index buffer var indices = new int[lightProbeRuntimeData.Faces.Count * 6]; for (var i = 0; i < lightProbeRuntimeData.Faces.Count; ++i) { var currentFace = lightProbeRuntimeData.Faces[i]; // Skip infinite edges to not clutter display // Maybe we could reenable it when we have better infinite nodes if (currentFace.Vertices[0] >= lightProbeRuntimeData.UserVertexCount || currentFace.Vertices[1] >= lightProbeRuntimeData.UserVertexCount || currentFace.Vertices[2] >= lightProbeRuntimeData.UserVertexCount) { continue; } indices[i * 6 + 0] = currentFace.Vertices[0]; indices[i * 6 + 1] = currentFace.Vertices[1]; indices[i * 6 + 2] = currentFace.Vertices[1]; indices[i * 6 + 3] = currentFace.Vertices[2]; indices[i * 6 + 4] = currentFace.Vertices[2]; indices[i * 6 + 5] = currentFace.Vertices[0]; } var boundingBox = BoundingBox.Empty; for (int i = 0; i < vertices.Length; i++) { BoundingBox.Merge(ref boundingBox, ref vertices[i].Position, out boundingBox); } // Compute bounding sphere BoundingSphere boundingSphere; fixed(void *verticesPtr = vertices) BoundingSphere.FromPoints((IntPtr)verticesPtr, 0, vertices.Length, VertexPositionNormalTexture.Size, out boundingSphere); var layout = vertices[0].GetLayout(); var meshDraw = new MeshDraw { IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices).RecreateWith(indices), true, indices.Length), VertexBuffers = new[] { new VertexBufferBinding(Buffer.New(graphicsDevice, vertices, BufferFlags.VertexBuffer).RecreateWith(vertices), layout, vertices.Length) }, DrawCount = indices.Length, PrimitiveType = primitiveType, }; wireframeResources.Add(meshDraw.VertexBuffers[0].Buffer); wireframeResources.Add(meshDraw.IndexBuffer.Buffer); return(new Mesh { Draw = meshDraw, BoundingBox = boundingBox, BoundingSphere = boundingSphere }); }
private static Buffer <Matrix> CreateMatrixBuffer(GraphicsDevice graphicsDevice, int elementCount) { return(Buffer.New <Matrix>(graphicsDevice, elementCount, BufferFlags.ShaderResource | BufferFlags.StructuredBuffer, GraphicsResourceUsage.Dynamic)); }
/// <summary> /// Bake lightprobes into buffers compatible with <see cref="LightProbeRenderer"/> /// </summary> /// <param name="drawContext">The drawing context</param> private unsafe void BakeLightProbes(RenderContext context, RenderDrawContext drawContext) { Texture ibl = null; Buffer tetrahedronProbeIndices = null; Buffer tetrahedronMatrices = null; Buffer lightprobesCoefficients = null; var renderView = context.RenderView; var lightProbesData = context.VisibilityGroup.Tags.Get(LightProbeRenderer.CurrentLightProbes); if (lightProbesData == null || lightProbesData.Tetrahedra.Count == 0) { // No lightprobes, we still set GPU resources (otherwise rendering might fetch invalid data) goto SetGPUResources; } // First time initialization if (bakeLightProbes == null) { bakeLightProbes = new DynamicEffectInstance("StrideBakeLightProbeEffect"); bakeLightProbes.Initialize(Services); bakeLightProbesPipeline = new MutablePipelineState(GraphicsDevice); bakeLightProbesPipeline.State.InputElements = LightProbeVertex.Layout.CreateInputElements(); bakeLightProbesPipeline.State.PrimitiveType = PrimitiveType.TriangleList; } // Render IBL tetrahedra ID so that we can assign them per pixel //ibl = PushScopedResource(Context.Allocator.GetTemporaryTexture2D(drawContext.CommandList.DepthStencilBuffer.Width, drawContext.CommandList.DepthStencilBuffer.Height, PixelFormat.R16_UInt)); ibl = PushScopedResource(Context.Allocator.GetTemporaryTexture2D(TextureDescription.New2D(drawContext.CommandList.DepthStencilBuffer.Width, drawContext.CommandList.DepthStencilBuffer.Height, 1, PixelFormat.R16_UInt, TextureFlags.ShaderResource | TextureFlags.RenderTarget, 1, GraphicsResourceUsage.Default, actualMultisampleCount))); using (drawContext.PushRenderTargetsAndRestore()) { drawContext.CommandList.Clear(ibl, Color4.Black); drawContext.CommandList.SetRenderTarget(drawContext.CommandList.DepthStencilBuffer, ibl); bakeLightProbes.UpdateEffect(GraphicsDevice); bakeLightProbesPipeline.State.RootSignature = bakeLightProbes.RootSignature; bakeLightProbesPipeline.State.EffectBytecode = bakeLightProbes.Effect.Bytecode; bakeLightProbesPipeline.State.RasterizerState.DepthClipEnable = false; bakeLightProbesPipeline.State.DepthStencilState = new DepthStencilStateDescription(true, false) { StencilEnable = true, FrontFace = new DepthStencilStencilOpDescription { StencilDepthBufferFail = StencilOperation.Keep, StencilFail = StencilOperation.Keep, StencilPass = StencilOperation.Increment, StencilFunction = CompareFunction.Equal, }, }; //bakeLightProbesPipeline.State.RasterizerState.DepthClipEnable = false; bakeLightProbesPipeline.State.Output.CaptureState(drawContext.CommandList); bakeLightProbesPipeline.Update(); drawContext.CommandList.SetPipelineState(bakeLightProbesPipeline.CurrentState); drawContext.CommandList.SetStencilReference(0); // Apply the effect bakeLightProbes.Parameters.Set(BakeLightProbeShaderKeys.MatrixTransform, ref renderView.ViewProjection); bakeLightProbes.Apply(drawContext.GraphicsContext); /*int tetrahedrawGridSize = 5; * Vector3 tetrahedraMin = new Vector3(-12.0f); * Vector3 tetrahedraMax = new Vector3(12.0f); * var lightprobePositions = new Vector3[tetrahedrawGridSize*tetrahedrawGridSize*tetrahedrawGridSize]; * * for (int i = 0; i < lightprobePositions.Length; ++i) * { * lightprobePositions[i] = new Vector3( * MathUtil.Lerp(tetrahedraMin.X, tetrahedraMax.X, (float)(i/(tetrahedrawGridSize*tetrahedrawGridSize))/(tetrahedrawGridSize - 1)), * MathUtil.Lerp(tetrahedraMin.Y, tetrahedraMax.Y, (float)((i/tetrahedrawGridSize)%tetrahedrawGridSize)/(tetrahedrawGridSize - 1)), * MathUtil.Lerp(tetrahedraMin.Z, tetrahedraMax.Z, (float)(i%tetrahedrawGridSize)/(tetrahedrawGridSize - 1))); * } * * var tetra = new BowyerWatsonTetrahedralization(); * var tetraResult = tetra.Compute(lightprobePositions);*/ Matrix.Invert(ref renderView.View, out var viewInverse); var eye = new Vector3(viewInverse.M41, viewInverse.M42, viewInverse.M43); var tetraResult = lightProbesData.Tetrahedra; var lightprobePositions = lightProbesData.Vertices; var lightprobeFaces = lightProbesData.Faces; // We build a graph of tetrahedron connectivity from back to front, then do a topological sort on top of it var tetraDepth = new TetrahedronSortKey[tetraResult.Count]; var faceDirection = new bool[lightprobeFaces.Count]; var incomingEdges = new byte[tetraResult.Count]; var processQueue = new Queue <int>(); int processedTetrahedra = 0; for (int i = 0; i < lightprobeFaces.Count; ++i) { var face = lightprobeFaces[i]; // Compute face orientations var vertex0 = lightprobePositions[face.Vertices[0]]; Vector3.Subtract(ref vertex0, ref eye, out vertex0); bool faceFrontFacing = Vector3.Dot(face.Normal, vertex0) >= 0.0f; faceDirection[i] = faceFrontFacing; // Only process edges that connect two tetrahedra (ignore boundaries for now) if (face.BackTetrahedron != -1) { // Build list of incoming edges (back to front) if (faceFrontFacing) { incomingEdges[face.FrontTetrahedron] |= (byte)(1 << face.FrontFace); } else { incomingEdges[face.BackTetrahedron] |= (byte)(1 << face.BackFace); } } } for (int i = 0; i < tetraResult.Count; ++i) { // Tetrahedron without any incoming edges means they should be drawn first (graph nodes with no incoming edges for our topological sort) if (incomingEdges[i] == 0) { processQueue.Enqueue(i); } } // Perform topological sort while (processQueue.Count > 0) { var tetrahedronIndex = processQueue.Dequeue(); tetraDepth[tetrahedronIndex] = new TetrahedronSortKey(tetrahedronIndex, processedTetrahedra++); var tetrahedron = tetraResult[tetrahedronIndex]; //var frontFacingFaces = frontFacing[tetrahedronIndex]; // Process each outgoing face (edges in the graph) for (int tetrahedronFace = 0; tetrahedronFace < 4; ++tetrahedronFace) { // Check if there is a neighbour if (tetrahedron.Neighbours[tetrahedronFace] == -1) { continue; } var faceIndex = tetrahedron.Faces[tetrahedronFace]; var realFaceIndex = faceIndex >= 0 ? faceIndex : ~faceIndex; // Only process faces going back to front (outgoing edges) if (faceDirection[realFaceIndex] == faceIndex >= 0) { continue; } var face = lightprobeFaces[realFaceIndex]; int tetrahedronNeighbourIndex; sbyte tetrahedronNeighbourFace; if (faceIndex >= 0) { tetrahedronNeighbourIndex = face.BackTetrahedron; tetrahedronNeighbourFace = face.BackFace; } else { tetrahedronNeighbourIndex = face.FrontTetrahedron; tetrahedronNeighbourFace = face.FrontFace; } var neighbourTraversedFaces = incomingEdges[tetrahedronNeighbourIndex]; var newNeighbourTraversedFaces = (byte)(neighbourTraversedFaces & ~(1 << tetrahedronNeighbourFace)); // Proceed only if something changed if (newNeighbourTraversedFaces != neighbourTraversedFaces) { incomingEdges[tetrahedronNeighbourIndex] = newNeighbourTraversedFaces; if (newNeighbourTraversedFaces == 0) // are all incoming edges already marked? If yes, go on { processQueue.Enqueue(tetrahedronNeighbourIndex); } } } } Array.Sort(tetraDepth); // Draw shape tetrahedronMatrices = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 3 * sizeof(Vector4), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32A32_Float)); tetrahedronProbeIndices = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 4 * sizeof(int), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32A32_UInt)); lightprobesCoefficients = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(lightProbesData.Coefficients.Length * sizeof(Color3), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32_Float)); var tetraInsideIndex = -1; fixed(Color3 *lightProbeCoefficients = lightProbesData.Coefficients) fixed(Vector4 * matrices = lightProbesData.Matrices) fixed(Int4 * probeIndices = lightProbesData.LightProbeIndices) { drawContext.CommandList.UpdateSubresource(lightprobesCoefficients, 0, new DataBox((IntPtr)lightProbeCoefficients, 0, 0)); drawContext.CommandList.UpdateSubresource(tetrahedronProbeIndices, 0, new DataBox((IntPtr)probeIndices, 0, 0)); drawContext.CommandList.UpdateSubresource(tetrahedronMatrices, 0, new DataBox((IntPtr)matrices, 0, 0)); // Find which probe we are currently in // TODO: Optimize (use previous coherency info?) for (int i = 0; i < tetraResult.Count; ++i) { // Get tetrahedra matrix var tetrahedraMatrix = Matrix.Identity; tetrahedraMatrix.Column1 = matrices[i * 3 + 0]; tetrahedraMatrix.Column2 = matrices[i * 3 + 1]; tetrahedraMatrix.Column3 = matrices[i * 3 + 2]; // Extract and zero-out position of 3rd vertex (we get the 3x3 matrix) var vertex3 = tetrahedraMatrix.TranslationVector; tetrahedraMatrix.TranslationVector = Vector3.Zero; Vector3 tetraFactors = Vector3.TransformCoordinate(eye - vertex3, tetrahedraMatrix); var tetraFactorW = 1.0f - tetraFactors.X - tetraFactors.Y - tetraFactors.Z; if (tetraFactors.X >= 0.0f && tetraFactors.X <= 1.0f && tetraFactors.Y >= 0.0f && tetraFactors.Y <= 1.0f && tetraFactors.Z >= 0.0f && tetraFactors.Z <= 1.0f && tetraFactorW >= 0.0f && tetraFactorW <= 1.0f) { tetraInsideIndex = i; break; } } } // Fill vertex/index buffers var vertexBuffer = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription((tetraResult.Count * 4 + 3) * LightProbeVertex.Size, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic))); var indexBuffer = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 12 * sizeof(uint), BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic))); var mappedVertexBuffer = drawContext.CommandList.MapSubresource(vertexBuffer, 0, MapMode.WriteDiscard); var vertices = (LightProbeVertex *)mappedVertexBuffer.DataBox.DataPointer; // Upload sorted tetrahedron indices for (int i = 0; i < tetraResult.Count; ++i) { var sortedIndex = tetraDepth[i].Index; var tetrahedra = tetraResult[sortedIndex]; for (int j = 0; j < 4; ++j) { vertices[i * 4 + j] = new LightProbeVertex(lightprobePositions[tetrahedra.Vertices[j]], (uint)sortedIndex); } } // Full screen pass if (tetraInsideIndex != -1) { vertices[tetraResult.Count * 4 + 0] = new LightProbeVertex(new Vector3(-1, 1, 0), (uint)tetraInsideIndex); vertices[tetraResult.Count * 4 + 1] = new LightProbeVertex(new Vector3(3, 1, 0), (uint)tetraInsideIndex); vertices[tetraResult.Count * 4 + 2] = new LightProbeVertex(new Vector3(-1, -3, 0), (uint)tetraInsideIndex); } drawContext.CommandList.UnmapSubresource(mappedVertexBuffer); var mappedIndexBuffer = drawContext.CommandList.MapSubresource(indexBuffer, 0, MapMode.WriteDiscard); var indices = (int *)mappedIndexBuffer.DataBox.DataPointer; for (int i = 0; i < tetraResult.Count; ++i) { indices[i * 12 + 0] = i * 4 + 0; indices[i * 12 + 1] = i * 4 + 2; indices[i * 12 + 2] = i * 4 + 1; indices[i * 12 + 3] = i * 4 + 1; indices[i * 12 + 4] = i * 4 + 2; indices[i * 12 + 5] = i * 4 + 3; indices[i * 12 + 6] = i * 4 + 3; indices[i * 12 + 7] = i * 4 + 2; indices[i * 12 + 8] = i * 4 + 0; indices[i * 12 + 9] = i * 4 + 3; indices[i * 12 + 10] = i * 4 + 0; indices[i * 12 + 11] = i * 4 + 1; } drawContext.CommandList.UnmapSubresource(mappedIndexBuffer); drawContext.CommandList.SetVertexBuffer(0, vertexBuffer, 0, LightProbeVertex.Size); drawContext.CommandList.SetIndexBuffer(indexBuffer, 0, true); // Draw until current tetrahedra drawContext.CommandList.DrawIndexed(tetraResult.Count * 12); // For now, drawing them one by one (easier to debug) //for (int i = 0; i < tetraResult.Count; ++i) //{ // context.CommandList.DrawIndexed(12, i * 12); //} // Draw current tetrahedron we are in as full screen (fill stencil holes) if (tetraInsideIndex != -1) { bakeLightProbesPipeline.State.DepthStencilState.DepthBufferEnable = false; bakeLightProbesPipeline.Update(); drawContext.CommandList.SetPipelineState(bakeLightProbesPipeline.CurrentState); // Apply the effect bakeLightProbes.Parameters.Set(BakeLightProbeShaderKeys.MatrixTransform, Matrix.Identity); bakeLightProbes.Apply(drawContext.GraphicsContext); drawContext.CommandList.Draw(3, tetraResult.Count * 4); } // TODO: Draw the tetrahedron we are in full screen // context.CommandList.Draw... } // Set LightProbes resources SetGPUResources: foreach (var renderFeature in context.RenderSystem.RenderFeatures) { if (!(renderFeature is RootEffectRenderFeature)) { continue; } var logicalKey = ((RootEffectRenderFeature)renderFeature).CreateViewLogicalGroup("LightProbes"); var viewFeature = renderView.Features[renderFeature.Index]; foreach (var viewLayout in viewFeature.Layouts) { var resourceGroup = viewLayout.Entries[renderView.Index].Resources; var logicalGroup = viewLayout.GetLogicalGroup(logicalKey); if (logicalGroup.Hash == ObjectId.Empty) { continue; } resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart, ibl); resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 1, tetrahedronProbeIndices); resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 2, tetrahedronMatrices); resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 3, lightprobesCoefficients); } } }
public static Mesh ToStrideMesh(GraphicsDevice graphicsDevice, SimpleMesh g3Mesh, Vector3 offset, float scaling = 1f) { if (g3Mesh is null || g3Mesh.VertexCount == 0) { return(null); } var vertexDeclaration = GetVertexDeclaration(g3Mesh); var vertices = new byte[g3Mesh.VertexCount * vertexDeclaration.VertexStride]; var boundingBox = BoundingBox.Empty; BoundingSphere boundingSphere; unsafe { fixed(byte *ptr = vertices) { byte *current = ptr; for (int i = 0; i < g3Mesh.VertexCount; i++) { var vi = g3Mesh.GetVertexAll(i); var p = (new Vector3((float)vi.v.x, (float)vi.v.y, (float)vi.v.z) + offset) * scaling; BoundingBox.Merge(ref boundingBox, ref p, out boundingBox); Unsafe.Write(current, p); current += sizeof(Vector3); if (vi.bHaveN) { Unsafe.Write(current, vi.n); current += sizeof(Vector3); } if (vi.bHaveUV) { Unsafe.Write(current, vi.uv); current += sizeof(Vector2); } if (vi.bHaveC) { Unsafe.Write(current, new Color(vi.c.x, vi.c.y, vi.c.z)); current += sizeof(Color); } } BoundingSphere.FromPoints((IntPtr)ptr, 0, g3Mesh.VertexCount, vertexDeclaration.VertexStride, out boundingSphere); } } var vertexBuffer = Buffer.New(graphicsDevice, vertices, vertexDeclaration.VertexStride, BufferFlags.VertexBuffer); var indexBuffer = Buffer.Index.New(graphicsDevice, g3Mesh.Triangles.Reverse().ToArray()); return(new Mesh() { Draw = new MeshDraw() { VertexBuffers = new VertexBufferBinding[] { new VertexBufferBinding(vertexBuffer, vertexDeclaration, g3Mesh.VertexCount) }, IndexBuffer = new IndexBufferBinding(indexBuffer, is32Bit: true, g3Mesh.Triangles.Length), DrawCount = g3Mesh.Triangles.Length, PrimitiveType = PrimitiveType.TriangleList }, BoundingBox = boundingBox, BoundingSphere = boundingSphere }); }
/// <inheritdoc/> public void Generate(IServiceRegistry services, Model model) { if (model is null) { throw new ArgumentNullException(nameof(model)); } var needsTempDevice = false; var graphicsDevice = services?.GetSafeServiceAs <IGraphicsDeviceService>().GraphicsDevice; if (graphicsDevice is null) { graphicsDevice = GraphicsDevice.New(); needsTempDevice = true; } var data = CreatePrimitiveMeshData(); if (data.Vertices.Length == 0) { throw new InvalidOperationException("Invalid GeometricPrimitive [{0}]. Expecting non-zero Vertices array."); } // Translate if necessary if (LocalOffset != Vector3.Zero) { for (var index = 0; index < data.Vertices.Length; index++) { data.Vertices[index].Position += LocalOffset; } } // Scale if necessary if (Scale != Vector3.One) { var inverseMatrix = Matrix.Scaling(Scale); inverseMatrix.Invert(); for (var index = 0; index < data.Vertices.Length; index++) { data.Vertices[index].Position *= Scale; // TODO: Shouldn't be TransformNormal? Vector3.TransformCoordinate(ref data.Vertices[index].Normal, ref inverseMatrix, out data.Vertices[index].Normal); } } var boundingBox = BoundingBox.Empty; for (int i = 0; i < data.Vertices.Length; i++) { BoundingBox.Merge(ref boundingBox, ref data.Vertices[i].Position, out boundingBox); } BoundingSphere boundingSphere; unsafe { fixed(void *verticesPtr = data.Vertices) BoundingSphere.FromPoints((IntPtr)verticesPtr, 0, data.Vertices.Length, VertexPositionNormalTexture.Size, out boundingSphere); } var originalLayout = data.Vertices[0].GetLayout(); // Generate Tangent/BiNormal vectors var resultWithTangentBiNormal = VertexHelper.GenerateTangentBinormal(originalLayout, data.Vertices, data.Indices); // Generate Multi texturing coords var maxTexCoords = MathUtil.Clamp(NumberOfTextureCoordinates, 1, 10) - 1; var result = VertexHelper.GenerateMultiTextureCoordinates(resultWithTangentBiNormal, vertexStride: 0, maxTexCoords); var meshDraw = new MeshDraw(); var layout = result.Layout; var vertexBuffer = result.VertexBuffer; var indices = data.Indices; if (indices.Length < 0xFFFF) { // 16-bit indices var indicesShort = new ushort[indices.Length]; for (int i = 0; i < indicesShort.Length; i++) { indicesShort[i] = (ushort)indices[i]; } var indexBuffer = Buffer.Index.New(graphicsDevice, indicesShort) .RecreateWith(indicesShort); meshDraw.IndexBuffer = new IndexBufferBinding(indexBuffer, is32Bit: false, indices.Length); if (needsTempDevice) { var indexData = BufferData.New(BufferFlags.IndexBuffer, indicesShort); meshDraw.IndexBuffer = new IndexBufferBinding(indexData.ToSerializableVersion(), is32Bit: false, indices.Length); } } else { // 32-bit indices var indexBuffer = Buffer.Index.New(graphicsDevice, indices) .RecreateWith(indices); meshDraw.IndexBuffer = new IndexBufferBinding(indexBuffer, is32Bit: true, indices.Length); if (needsTempDevice) { var indexData = BufferData.New(BufferFlags.IndexBuffer, indices); meshDraw.IndexBuffer = new IndexBufferBinding(indexData.ToSerializableVersion(), is32Bit: true, indices.Length); } } var geometryBuffer = Buffer.New(graphicsDevice, vertexBuffer, BufferFlags.VertexBuffer) .RecreateWith(vertexBuffer); meshDraw.VertexBuffers = new[] { new VertexBufferBinding(geometryBuffer, layout, data.Vertices.Length) }; if (needsTempDevice) { var vertexData = BufferData.New(BufferFlags.VertexBuffer, vertexBuffer); meshDraw.VertexBuffers = new[] { new VertexBufferBinding(vertexData.ToSerializableVersion(), layout, data.Vertices.Length) }; } meshDraw.DrawCount = indices.Length; meshDraw.PrimitiveType = PrimitiveType.TriangleList; var mesh = new Mesh { Draw = meshDraw, BoundingBox = boundingBox, BoundingSphere = boundingSphere }; model.BoundingBox = boundingBox; model.BoundingSphere = boundingSphere; model.Add(mesh); if (MaterialInstance?.Material is not null) { model.Materials.Add(MaterialInstance); } if (needsTempDevice) { graphicsDevice.Dispose(); } }
protected override void DrawCore(RenderDrawContext context) { var inputTexture = RadianceMap; if (inputTexture == null) { return; } const int FirstPassBlockSize = 4; const int FirstPassSumsCount = FirstPassBlockSize * FirstPassBlockSize; var faceCount = inputTexture.ViewDimension == TextureDimension.TextureCube ? 6 : 1; if (faceCount == 1) { throw new NotSupportedException("Only texture cube are currently supported as input of 'LambertianPrefilteringSH' effect."); } var inputSize = new Int2(inputTexture.Width, inputTexture.Height); // (Note: for cube maps width = height) var coefficientsCount = harmonicalOrder * harmonicalOrder; var sumsToPerfomRemaining = inputSize.X * inputSize.Y * faceCount / FirstPassSumsCount; var partialSumBuffer = NewScopedTypedBuffer(coefficientsCount * sumsToPerfomRemaining, PixelFormat.R32G32B32A32_Float, true); // Project the radiance on the SH basis and sum up the results along the 4x4 blocks firstPassEffect.ThreadNumbers = new Int3(FirstPassBlockSize, FirstPassBlockSize, 1); firstPassEffect.ThreadGroupCounts = new Int3(inputSize.X / FirstPassBlockSize, inputSize.Y / FirstPassBlockSize, faceCount); firstPassEffect.Parameters.Set(LambertianPrefilteringSHParameters.BlockSize, FirstPassBlockSize); firstPassEffect.Parameters.Set(SphericalHarmonicsParameters.HarmonicsOrder, harmonicalOrder); firstPassEffect.Parameters.Set(LambertianPrefilteringSHPass1Keys.RadianceMap, inputTexture); firstPassEffect.Parameters.Set(LambertianPrefilteringSHPass1Keys.OutputBuffer, partialSumBuffer); ((RendererBase)firstPassEffect).Draw(context); // Recursively applies the pass2 (sums the coefficients together) as long as needed. Swap input/output buffer at each iteration. var secondPassInputBuffer = partialSumBuffer; Buffer secondPassOutputBuffer = null; while (sumsToPerfomRemaining % 2 == 0) { // we are limited in the number of summing threads by the group-shared memory size. // determine the number of threads to use and update the number of sums remaining afterward. var sumsCount = 1; while (sumsCount < (1 << 10) && sumsToPerfomRemaining % 2 == 0) // shader can perform only an 2^x number of sums. { sumsCount <<= 1; sumsToPerfomRemaining >>= 1; } // determine the numbers of groups (limited to 65535 groups by dimensions) var groupCountX = 1; var groupCountY = sumsToPerfomRemaining; while (groupCountX >= short.MaxValue) { groupCountX <<= 1; groupCountY >>= 1; } // create the output buffer if not existing yet if (secondPassOutputBuffer == null) { secondPassOutputBuffer = NewScopedTypedBuffer(coefficientsCount * sumsToPerfomRemaining, PixelFormat.R32G32B32A32_Float, true); } // draw pass 2 secondPassEffect.ThreadNumbers = new Int3(sumsCount, 1, 1); secondPassEffect.ThreadGroupCounts = new Int3(groupCountX, groupCountY, coefficientsCount); secondPassEffect.Parameters.Set(LambertianPrefilteringSHParameters.BlockSize, sumsCount); secondPassEffect.Parameters.Set(SphericalHarmonicsParameters.HarmonicsOrder, harmonicalOrder); secondPassEffect.Parameters.Set(LambertianPrefilteringSHPass2Keys.InputBuffer, secondPassInputBuffer); secondPassEffect.Parameters.Set(LambertianPrefilteringSHPass2Keys.OutputBuffer, secondPassOutputBuffer); ((RendererBase)secondPassEffect).Draw(context); // swap second pass input/output buffers. var swapTemp = secondPassOutputBuffer; secondPassOutputBuffer = secondPassInputBuffer; secondPassInputBuffer = swapTemp; } // create and initialize result SH prefilteredLambertianSH = new SphericalHarmonics(HarmonicOrder); // Get the data out of the final buffer var sizeResult = coefficientsCount * sumsToPerfomRemaining * PixelFormat.R32G32B32A32_Float.SizeInBytes(); var stagedBuffer = NewScopedBuffer(new BufferDescription(sizeResult, BufferFlags.None, GraphicsResourceUsage.Staging)); context.CommandList.CopyRegion(secondPassInputBuffer, 0, new ResourceRegion(0, 0, 0, sizeResult, 1, 1), stagedBuffer, 0); var finalsValues = stagedBuffer.GetData <Vector4>(context.CommandList); // performs last possible additions, normalize the result and store it in the SH for (var c = 0; c < coefficientsCount; c++) { var coeff = Vector4.Zero; for (var f = 0; f < sumsToPerfomRemaining; ++f) { coeff += finalsValues[coefficientsCount * f + c]; } prefilteredLambertianSH.Coefficients[c] = 4 * MathUtil.Pi / coeff.W * new Color3(coeff.X, coeff.Y, coeff.Z); } }