protected override void InitializeCore()
        {
            base.InitializeCore();

            _skyViewLutTexture = Texture.New2D(Context.GraphicsDevice, SkyViewLutSettings.Width, SkyViewLutSettings.Height, SkyViewLutSettings.Format, TextureFlags.RenderTarget | TextureFlags.ShaderResource);
            _atmosphereCameraScatteringVolumeTexture = Texture.New3D(Context.GraphicsDevice, AtmosphereCameraScatteringVolumeSettings.Size, AtmosphereCameraScatteringVolumeSettings.Size, AtmosphereCameraScatteringVolumeSettings.Slices, AtmosphereCameraScatteringVolumeSettings.Format, TextureFlags.RenderTarget | TextureFlags.ShaderResource);
            _multiScatteringTexture        = Texture.New2D(Context.GraphicsDevice, MultiScatteringTextureSettings.Size, MultiScatteringTextureSettings.Size, MultiScatteringTextureSettings.Format, TextureFlags.UnorderedAccess | TextureFlags.ShaderResource);
            TransmittanceLutTexture        = Texture.New2D(Context.GraphicsDevice, TransmittanceLutSettings.Width, TransmittanceLutSettings.Height, TransmittanceLutSettings.Format, TextureFlags.UnorderedAccess | TextureFlags.RenderTarget | TextureFlags.ShaderResource);
            _atmosphereCubeMapRenderTarget = Texture.New2D(Context.GraphicsDevice, 64, 64, PixelFormat.R16G16B16A16_Float, TextureFlags.RenderTarget | TextureFlags.ShaderResource);
            _atmosphereCubeMap             = Texture.NewCube(Context.GraphicsDevice, 64, PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource);
            _atmosphereCubeMapSpecular     = Texture.NewCube(Context.GraphicsDevice, 64, MipMapCount.Auto, PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);

            _transmittanceLutEffect = new ImageEffectShader("AtmosphereRenderTransmittanceLutEffect");
            _skyViewLutEffect       = new ImageEffectShader("AtmosphereRenderSkyViewLutEffect");
            _renderMultipleScatteringTextureEffect = new ComputeEffectShader(Context)
            {
                ShaderSourceName = "AtmosphereMultipleScatteringTextureEffect"
            };

            _renderAtmosphereScatteringVolumeEffect = new DynamicEffectInstance("AtmosphereRenderScatteringCameraVolumeEffect");
            _renderAtmosphereScatteringVolumeEffect.Initialize(Context.Services);

            _renderAtmosphereScatteringVolumePipelineState = new MutablePipelineState(Context.GraphicsDevice);
            _renderAtmosphereScatteringVolumePipelineState.State.SetDefaults();
            _renderAtmosphereScatteringVolumePipelineState.State.PrimitiveType = PrimitiveType.TriangleList;

            _atmosphereLogicalGroupKey = CreateDrawLogicalGroup("Atmosphere");

            _spriteBatch = new SpriteBatch(Context.GraphicsDevice);
        }
        public override Task DisposeAsync()
        {
            EnsureNotDestroyed(nameof(EditorGameCameraPreviewService));

            // Unregister events
            var selectionService = Services.Get <IEditorGameEntitySelectionService>();

            if (selectionService != null)
            {
                selectionService.SelectionUpdated -= UpdateModifiedEntitiesList;
            }

            // Remove renderers
            var gameTopLevel   = game.SceneSystem.GraphicsCompositor.Game as EditorTopLevelCompositor;
            var editorTopLevel = game.EditorSceneSystem.GraphicsCompositor.Game as EditorTopLevelCompositor;

            if (gameTopLevel != null && editorTopLevel != null)
            {
                gameTopLevel.PostGizmoCompositors.Remove(generateIncrustRenderer);
                editorTopLevel.PostGizmoCompositors.Remove(renderIncrustRenderer);
            }

            defaultFont?.Dispose();
            defaultFont = null;

            spriteEffect?.Dispose();
            spriteEffect = null;

            borderPipelineState = null;
            borderVertexBuffer?.Dispose();
            borderVertexBuffer = null;

            return(base.DisposeAsync());
        }
예제 #3
0
        //private Matrix viewprojection = new Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);


        protected override void InitializeCore()
        {
            base.InitializeCore();
            myCustomShader = new DynamicEffectInstance("test_shader1");
            myCustomShader.Initialize(Context.Services);
            pipelineState = new MutablePipelineState(Context.GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements            = VertexDeclaration.CreateInputElements();
            pipelineState.State.PrimitiveType            = PrimitiveType.TriangleStrip;
            pipelineState.State.BlendState               = BlendStates.Default;
            pipelineState.State.RasterizerState.CullMode = CullMode.None;
        }
        protected override Task <bool> Initialize(EditorServiceGame editorGame)
        {
            game = (EntityHierarchyEditorGame)editorGame;

            // create the default font
            var fontItem = OfflineRasterizedSpriteFontFactory.Create();

            fontItem.FontType.Size = 8;
            defaultFont            = OfflineRasterizedFontCompiler.Compile(game.Services.GetService <IFontFactory>(), fontItem, game.GraphicsDevice.ColorSpace == ColorSpace.Linear);

            incrustBatch = new SpriteBatch(game.GraphicsDevice);

            // SpriteEffect will be used to draw camera incrust border
            spriteEffect = new EffectInstance(new Graphics.Effect(game.GraphicsDevice, SpriteEffect.Bytecode));
            spriteEffect.Parameters.Set(TexturingKeys.Texture0, game.GraphicsDevice.GetSharedWhiteTexture());
            spriteEffect.UpdateEffect(game.GraphicsDevice);

            borderPipelineState = new MutablePipelineState(game.GraphicsDevice);
            borderPipelineState.State.RootSignature   = spriteEffect.RootSignature;
            borderPipelineState.State.EffectBytecode  = spriteEffect.Effect.Bytecode;
            borderPipelineState.State.InputElements   = VertexPositionTexture.Layout.CreateInputElements();
            borderPipelineState.State.PrimitiveType   = PrimitiveType.LineStrip;
            borderPipelineState.State.RasterizerState = RasterizerStates.CullNone;

            unsafe
            {
                var borderVertices = new[]
                {
                    new VertexPositionTexture(new Vector3(0.0f, 0.0f, 0.0f), Vector2.Zero),
                    new VertexPositionTexture(new Vector3(0.0f, 1.0f, 0.0f), Vector2.Zero),
                    new VertexPositionTexture(new Vector3(1.0f, 1.0f, 0.0f), Vector2.Zero),
                    new VertexPositionTexture(new Vector3(1.0f, 0.0f, 0.0f), Vector2.Zero),
                    new VertexPositionTexture(new Vector3(0.0f, 0.0f, 0.0f), Vector2.Zero),
                    new VertexPositionTexture(new Vector3(0.0f, 1.0f, 0.0f), Vector2.Zero), // extra vertex so that left-top corner is not missing (likely due to rasterization rule)
                };

                fixed(VertexPositionTexture *borderVerticesPtr = borderVertices)
                borderVertexBuffer = Graphics.Buffer.Vertex.New(game.GraphicsDevice, new DataPointer(borderVerticesPtr, VertexPositionTexture.Size * borderVertices.Length));
            }

            var editorTopLevel = game.EditorSceneSystem.GraphicsCompositor.Game as EditorTopLevelCompositor;

            if (editorTopLevel != null)
            {
                // Display it as incrust
                editorTopLevel.PostGizmoCompositors.Add(renderIncrustRenderer = new RenderIncrustRenderer(this));
            }

            Services.Get <IEditorGameEntitySelectionService>().SelectionUpdated += UpdateModifiedEntitiesList;

            return(Task.FromResult(true));
        }
예제 #5
0
        protected override void InitializeCore()
        {
            base.InitializeCore();

            // Light accumulation shader
            lightShaftsEffectShader = ToLoadAndUnload(new ImageEffectShader("LightShaftsEffect"));

            // Additive blending shader
            applyLightEffectShader            = ToLoadAndUnload(new ImageEffectShader("AdditiveLightEffect"));
            applyLightEffectShader.BlendState = new BlendStateDescription(Blend.One, Blend.One);

            minmaxVolumeEffectShader = new DynamicEffectInstance("VolumeMinMaxShader");
            minmaxVolumeEffectShader.Initialize(Context.Services);

            blur = ToLoadAndUnload(new GaussianBlur());

            // Need the shadow map renderer in order to render light shafts
            var meshRenderFeature = Context.RenderSystem.RenderFeatures.OfType <MeshRenderFeature>().FirstOrDefault();

            if (meshRenderFeature == null)
            {
                throw new InvalidOperationException("Missing mesh render feature");
            }

            var forwardLightingFeature = meshRenderFeature.RenderFeatures.OfType <ForwardLightingRenderFeature>().FirstOrDefault();

            if (forwardLightingFeature == null)
            {
                throw new InvalidOperationException("Missing forward lighting render feature");
            }

            shadowMapRenderer = forwardLightingFeature.ShadowMapRenderer;

            for (int i = 0; i < 2; ++i)
            {
                var minmaxPipelineState = new MutablePipelineState(Context.GraphicsDevice);
                minmaxPipelineState.State.SetDefaults();

                minmaxPipelineState.State.BlendState.RenderTarget0.BlendEnable           = true;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorSourceBlend      = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorDestinationBlend = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorBlendFunction    = i == 0 ? BlendFunction.Min : BlendFunction.Max;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorWriteChannels    = i == 0 ? ColorWriteChannels.Red : ColorWriteChannels.Green;

                minmaxPipelineState.State.DepthStencilState.DepthBufferEnable      = false;
                minmaxPipelineState.State.DepthStencilState.DepthBufferWriteEnable = false;
                minmaxPipelineState.State.RasterizerState.DepthClipEnable          = true;
                minmaxPipelineState.State.RasterizerState.CullMode = i == 0 ? CullMode.Back : CullMode.Front;

                minmaxPipelineStates[i] = minmaxPipelineState;
            }
        }
예제 #6
0
        public TileMeshRenderer(GraphicsDevice graphicsDevice)
        {
            if (graphicsDevice == null)
            {
                throw new ArgumentNullException(nameof(graphicsDevice));
            }

            GraphicsDevice  = graphicsDevice;
            MutablePipeline = new MutablePipelineState(graphicsDevice);
            DefaultEffect   = new EffectInstance(new Effect(GraphicsDevice, TileMeshRendererShader.Bytecode)
            {
                Name = "BatchDefaultEffect"
            });
        }
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            pipelineState = new MutablePipelineState(GraphicsDevice);

            var vertices = new Vertex[4];
            vertices[0] = new Vertex { Position = new Vector3(-1, -1, 0.5f), TexCoords = new Vector2(0, 0) };
            vertices[1] = new Vertex { Position = new Vector3(-1, 1, 0.5f), TexCoords = new Vector2(3, 0) };
            vertices[2] = new Vertex { Position = new Vector3(1, 1, 0.5f), TexCoords = new Vector2(3, 3) };
            vertices[3] = new Vertex { Position = new Vector3(1, -1, 0.5f), TexCoords = new Vector2(0, 3) };

            var indices = new short[] { 0, 1, 2, 0, 2, 3 };

            var vertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertices, GraphicsResourceUsage.Default);
            var indexBuffer = Buffer.Index.New(GraphicsDevice, indices, GraphicsResourceUsage.Default);
            var meshDraw = new MeshDraw
            {
                DrawCount = 4,
                PrimitiveType = PrimitiveType.TriangleList,
                VertexBuffers = new[]
                {
                    new VertexBufferBinding(vertexBuffer,
                        new VertexDeclaration(VertexElement.Position<Vector3>(),
                            VertexElement.TextureCoordinate<Vector2>()),
                        4)
                },
                IndexBuffer = new IndexBufferBinding(indexBuffer, false, indices.Length),
            };

            mesh = new Mesh
            {
                Draw = meshDraw,
            };

            simpleEffect = new EffectInstance(new Effect(GraphicsDevice, SpriteEffect.Bytecode));
            simpleEffect.Parameters.Set(TexturingKeys.Texture0, UVTexture);
            simpleEffect.UpdateEffect(GraphicsDevice);

            // TODO GRAPHICS REFACTOR
            //vao = VertexArrayObject.New(GraphicsDevice, mesh.Draw.IndexBuffer, mesh.Draw.VertexBuffers);

            myDraws = new DrawOptions[3];
            myDraws[0] = new DrawOptions { Sampler = GraphicsDevice.SamplerStates.LinearClamp, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(-0.5f, 0.5f, 0f)) };
            myDraws[1] = new DrawOptions { Sampler = GraphicsDevice.SamplerStates.LinearWrap, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, 0.5f, 0f)) };
            myDraws[2] = new DrawOptions { Sampler = SamplerState.New(GraphicsDevice, new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Mirror)), Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, -0.5f, 0f)) };
            //var borderDescription = new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Border) { BorderColor = Color.Purple };
            //var border = SamplerState.New(GraphicsDevice, borderDescription);
            //myDraws[3] = new DrawOptions { Sampler = border, Transform = Matrix.Multiply(Matrix.Scale(0.3f), Matrix.Translation(-0.5f, -0.5f, 0f)) };
        }
        protected override void InitializeCore()
        {
            // Initalize the shader
            myCustomShader = new DynamicEffectInstance("MyCustomShader");
            myCustomShader.Initialize(Context.Services);

            // Create the pipeline state and set properties that won't change
            pipelineState = new MutablePipelineState(Context.GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements            = MyRenderObject.VertexDeclaration.CreateInputElements();
            pipelineState.State.PrimitiveType            = MyRenderObject.PrimitiveType;
            pipelineState.State.BlendState               = BlendStates.Default;
            pipelineState.State.RasterizerState.CullMode = CullMode.None;
        }
예제 #9
0
        public ComputeEffectShader(RenderContext context)
            : base(context, null)
        {
            pipelineState = new MutablePipelineState(context.GraphicsDevice);

            // Setup the effect compiler
            EffectInstance = new DynamicEffectInstance("ComputeEffectShader", Parameters);
            EffectInstance.Initialize(context.Services);

            ThreadNumbers     = new Int3(1);
            ThreadGroupCounts = new Int3(1);

            SetDefaultParameters();
        }
        protected override void InitializeCore()
        {
            base.InitializeCore();

            // initialize shader
            shader = new DynamicEffectInstance("SinglePassWireframeShader");
            shader.Initialize(Context.Services);

            // create the pipeline state and set properties that won't change
            pipelineState = new MutablePipelineState(Context.GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements            = VertexPositionNormalTexture.Layout.CreateInputElements();
            pipelineState.State.BlendState               = BlendStates.AlphaBlend;
            pipelineState.State.RasterizerState.CullMode = CullMode.None;
        }
예제 #11
0
        /// <inheritdoc/>
        protected override void InitializeCore()
        {
            base.InitializeCore();

            pipelineState = new MutablePipelineState(Context.GraphicsDevice);

            if (EffectName == null)
            {
                throw new ArgumentNullException("No EffectName specified");
            }

            // Setup the effect compiler
            EffectInstance.Initialize(Context.Services);

            SetDefaultParameters();
        }
예제 #12
0
        //public static

        public static MutablePipelineState ReApplyGeometryStreamOutShader(this MutablePipelineState mutablePipelineState, GraphicsDevice graphicsDevice, EffectInstance geometryEffectInstance, string semanticName)
        {
            var bytecode   = geometryEffectInstance.Effect.Bytecode;
            var reflection = bytecode.Reflection;

            var geometryBytecode = bytecode.Stages.First(s => s.Stage == ShaderStage.Geometry);

            // Calculate the strides
            var soStrides = new List <int>();

            foreach (var streamOutputElement in reflection.ShaderStreamOutputDeclarations)
            {
                for (int i = soStrides.Count; i < (streamOutputElement.Stream + 1); i++)
                {
                    soStrides.Add(0);
                }
                soStrides[streamOutputElement.Stream] += streamOutputElement.ComponentCount * sizeof(float);
            }


            SharpDX.Direct3D11.StreamOutputElement[] soElements;

            var soElems = new List <SharpDX.Direct3D11.StreamOutputElement>();

            foreach (var streamOutputElement in reflection.ShaderStreamOutputDeclarations)
            {
                var soElem = new SharpDX.Direct3D11.StreamOutputElement()
                {
                    Stream         = streamOutputElement.Stream,
                    SemanticIndex  = streamOutputElement.SemanticIndex,
                    SemanticName   = streamOutputElement.SemanticName,
                    StartComponent = streamOutputElement.StartComponent,
                    ComponentCount = streamOutputElement.ComponentCount,
                    OutputSlot     = streamOutputElement.OutputSlot
                };
                soElems.Add(soElem);
            }


            var nativeDevice = graphicsDevice.GetNativeDevice();
            //var oldGeomShader = (SharpDX.Direct3D11.GeometryShader)geometryShaderFi.GetValue(mutablePipelineState.CurrentState);
            //oldGeomShader.Dispose(); needed?
            var geometryShader = new SharpDX.Direct3D11.GeometryShader(nativeDevice, geometryBytecode, soElems.ToArray(), reflection.StreamOutputStrides, reflection.StreamOutputRasterizedStream);

            geometryShaderFi.SetValue(mutablePipelineState.CurrentState, geometryShader);
            return(mutablePipelineState);
        }
        public ComputeEffectShader(RenderContext context)
            : base(context, null)
        {
            pipelineState = new MutablePipelineState(context.GraphicsDevice);

            // Setup the effect compiler
            EffectInstance = new DynamicEffectInstance("ComputeEffectShader", Parameters);
            EffectInstance.Initialize(context.Services);

            // We give ComputeEffectShader a higher priority, since they are usually executed serially and blocking
            EffectInstance.EffectCompilerParameters.TaskPriority = -1;

            ThreadNumbers     = new Int3(1);
            ThreadGroupCounts = new Int3(1);

            SetDefaultParameters();
        }
예제 #14
0
        private void Init()
        {
            _tribuf = Buffer.New(GraphicsDevice, new[]
            {
                new Vector4(-0.5f, 0.5f, 0, 1),
                new Vector4(0.5f, 0.5f, 0, 1),
                new Vector4(0, -0.5f, 0, 1)
            }, BufferFlags.VertexBuffer, GraphicsResourceUsage.Immutable);

            simpleEffect = new EffectInstance(new Effect(GraphicsDevice, SpriteEffect.Bytecode));
            simpleEffect.Parameters.Set(SpriteBaseKeys.MatrixTransform, Matrix.Identity);
            simpleEffect.UpdateEffect(GraphicsDevice);

            pipelineState = new MutablePipelineState(GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements = VertexDeclaration.CreateInputElements();
            pipelineState.State.PrimitiveType = PrimitiveType;
        }
예제 #15
0
        /// <summary>
        /// Initializes a new instance of the <see cref="XenkoRenderer"/> class.
        /// </summary>
        /// <param name="graphicsDeviceManager">The graphics device manager.</param>
        public XenkoRenderer(GraphicsDeviceManager graphicsDeviceManager, EffectSystem effectSystem)
            : base()
        {
            manager           = graphicsDeviceManager;
            this.effectSystem = effectSystem;
            spriteBatch       = new SpriteBatch(manager.GraphicsDevice);
            clipRectanges     = new Stack <Rectangle>();
            activeEffects     = new Stack <EffectInstance>();

            scissorRasterizerStateDescription = RasterizerStates.CullNone;
            scissorRasterizerStateDescription.ScissorTestEnable = true; // enables the scissor test

            geometryRasterizerStateDescription = RasterizerStates.CullNone;
            //geometryRasterizerStateDescription.FillMode = FillMode.Wireframe;
            geometryPipelineState = new MutablePipelineState(manager.GraphicsDevice);
            geometryPipelineState.State.DepthStencilState = DepthStencilStates.None;

            clipArray = new Rectangle[1];
        }
예제 #16
0
        /// <inheritdoc/>
        protected override void InitializeCore()
        {
            base.InitializeCore();

            pipelineState = new MutablePipelineState(Context.GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements = PrimitiveQuad.VertexDeclaration.CreateInputElements();
            pipelineState.State.PrimitiveType = PrimitiveQuad.PrimitiveType;

            if (EffectName == null)
            {
                throw new ArgumentNullException("No EffectName specified");
            }

            // Setup the effect compiler
            EffectInstance.Initialize(Context.Services);

            SetDefaultParameters();
        }
예제 #17
0
        /// <summary>
        /// Initializes a new instance of the <see cref="GeometricPrimitive{T}"/> class.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device.</param>
        /// <param name="geometryMesh">The geometry mesh.</param>
        /// <exception cref="System.InvalidOperationException">Cannot generate more than 65535 indices on feature level HW <= 9.3</exception>
        public GeometricPrimitive(GraphicsDevice graphicsDevice, GeometricMeshData <T> geometryMesh)
        {
            GraphicsDevice = graphicsDevice;
            PipelineState  = new MutablePipelineState(graphicsDevice);

            var vertices = geometryMesh.Vertices;
            var indices  = geometryMesh.Indices;

            if (geometryMesh.IsLeftHanded)
            {
                ReverseWinding(vertices, indices);
            }

            if (indices.Length < 0xFFFF)
            {
                var indicesShort = new ushort[indices.Length];
                for (int i = 0; i < indicesShort.Length; i++)
                {
                    indicesShort[i] = (ushort)indices[i];
                }
                IndexBuffer = Buffer.Index.New(graphicsDevice, indicesShort).RecreateWith(indicesShort).DisposeBy(this);
            }
            else
            {
                if (graphicsDevice.Features.CurrentProfile <= GraphicsProfile.Level_9_3)
                {
                    throw new InvalidOperationException("Cannot generate more than 65535 indices on feature level HW <= 9.3");
                }

                IndexBuffer   = Buffer.Index.New(graphicsDevice, indices).RecreateWith(indices).DisposeBy(this);
                IsIndex32Bits = true;
            }

            // For now it will keep buffers for recreation.
            // TODO: A better alternative would be to store recreation parameters so that we can reuse procedural code.
            VertexBuffer        = Buffer.Vertex.New(graphicsDevice, vertices).RecreateWith(vertices).DisposeBy(this);
            VertexBufferBinding = new VertexBufferBinding(VertexBuffer, new T().GetLayout(), vertices.Length);

            PipelineState.State.SetDefaults();
            PipelineState.State.InputElements = VertexBufferBinding.Declaration.CreateInputElements();
            PipelineState.State.PrimitiveType = PrimitiveQuad.PrimitiveType;
        }
예제 #18
0
        /// <inheritdoc/>
        protected override void InitializeCore()
        {
            base.InitializeCore();

            pipelineState = new MutablePipelineState(Context.GraphicsDevice);
            pipelineState.State.SetDefaults();
            pipelineState.State.InputElements = PrimitiveQuad.VertexDeclaration.CreateInputElements();
            pipelineState.State.PrimitiveType = PrimitiveQuad.PrimitiveType;

            if (EffectName == null)
            {
                throw new ArgumentNullException("No EffectName specified");
            }

            // Setup the effect compiler
            EffectInstance.Initialize(Context.Services);

            // We give ImageEffectShader a higher priority, since they are usually executed serially and blocking
            EffectInstance.EffectCompilerParameters.TaskPriority = -1;

            SetDefaultParameters();
        }
예제 #19
0
        /// <inheritdoc/>
        protected override void InitializeCore()
        {
            base.InitializeCore();

            MutablePipeline = new MutablePipelineState(Context.GraphicsDevice);

            // Create RenderEffectKey
            RenderEffectKey = RenderData.CreateStaticObjectKey <RenderEffect>(null, EffectPermutationSlotCount);

            // TODO: Assign weights so that PerDraw is always last? (we usually most custom user ones to be between PerView and PerDraw)
            perFrameDescriptorSetSlot = GetOrCreateEffectDescriptorSetSlot("PerFrame");
            perViewDescriptorSetSlot  = GetOrCreateEffectDescriptorSetSlot("PerView");
            perDrawDescriptorSetSlot  = GetOrCreateEffectDescriptorSetSlot("PerDraw");

            RenderSystem.RenderStages.CollectionChanged += RenderStages_CollectionChanged;

            // Create effect slots
            Array.Resize(ref effectSlots, RenderSystem.RenderStages.Count);
            for (int index = 0; index < RenderSystem.RenderStages.Count; index++)
            {
                var renderStage = RenderSystem.RenderStages[index];
                effectSlots[index] = CreateEffectPermutationSlot(renderStage.EffectSlotName);
            }
        }
예제 #20
0
 public PrepareThreadContext(RenderContext renderContext)
 {
     MutablePipelineState = new MutablePipelineState(renderContext.GraphicsDevice);
     Context = renderContext.GetThreadContext();
 }
예제 #21
0
        void ILowLevelAPIRender.Draw(RenderContext renderContext, RenderDrawContext drawContext, RenderView renderView, RenderViewStage renderViewStage, CommandList commandList)
        {
            if (!enabledPin.Value)
            {
                return;
            }

            try
            {
                var pipelineState = this.pipelineState ?? (this.pipelineState = new MutablePipelineState(renderContext.GraphicsDevice));

                // TODO1: PerFrame could be done in Update if we'd have access to frame time
                // TODO2: This code can be optimized by using parameter accessors and not parameter keys
                parameters.SetPerFrameParameters(perFrameParams, drawContext.RenderContext);
                parameters.SetPerViewParameters(perViewParams, renderView);

                if (worldPin != null)
                {
                    var world = worldPin.ShaderValue;
                    parameters.SetPerDrawParameters(perDrawParams, renderView, ref world);
                }

                // Set permutation parameters before updating the effect (needed by compiler)
                parameters.Set(ComputeEffectShaderKeys.ThreadNumbers, threadNumbersPin.Value);

                // Give user chance to override
                parameterSetterPin?.Value.Invoke(parameters, renderView, drawContext);

                if (instance.UpdateEffect(renderContext.GraphicsDevice) || pipelineStateDirty)
                {
                    threadGroupCountAccessor = parameters.GetAccessor(ComputeShaderBaseKeys.ThreadGroupCountGlobal);
                    foreach (var p in Inputs.OfType <ParameterPin>())
                    {
                        p.Update(parameters);
                    }
                    pipelineState.State.SetDefaults();
                    pipelineState.State.RootSignature  = instance.RootSignature;
                    pipelineState.State.EffectBytecode = instance.Effect.Bytecode;
                    pipelineState.Update();
                    pipelineStateDirty = false;
                }

                // Apply pipeline state
                commandList.SetPipelineState(pipelineState.CurrentState);

                // Set thread group count as provided by input pin
                var threadGroupCount = dispatchCountPin.Value;
                parameters.Set(ComputeShaderBaseKeys.ThreadGroupCountGlobal, threadGroupCount);

                // TODO: This can be optimized by uploading only parameters from the PerDispatch groups - look in Xenkos RootEffectRenderFeature
                var iterationCount = Math.Max(iterationCountPin.Value, 1);
                for (int i = 0; i < iterationCount; i++)
                {
                    // Give user chance to override
                    iterationParameterSetterPin.Value?.Invoke(parameters, renderView, drawContext, i);

                    // The thread group count can be set for each dispatch
                    threadGroupCount = parameters.Get(threadGroupCountAccessor);

                    // Upload the parameters
                    instance.Apply(drawContext.GraphicsContext);

                    // Draw a full screen quad
                    commandList.Dispatch(threadGroupCount.X, threadGroupCount.Y, threadGroupCount.Z);
                }
            }
            catch (Exception e)
            {
                var re = new RuntimeException(e.InnermostException(), this);
                RuntimeGraph.ReportException(re);
            }
        }
예제 #22
0
        /// <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("XenkoBakeLightProbeEffect");
                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);
                }
            }
        }
예제 #23
0
        protected override async Task LoadContent()
        {
            await base.LoadContent();

            pipelineState = new MutablePipelineState(GraphicsDevice);

            var vertices = new Vertex[4];

            vertices[0] = new Vertex {
                Position = new Vector3(-1, -1, 0.5f), TexCoords = new Vector2(0, 0)
            };
            vertices[1] = new Vertex {
                Position = new Vector3(-1, 1, 0.5f), TexCoords = new Vector2(3, 0)
            };
            vertices[2] = new Vertex {
                Position = new Vector3(1, 1, 0.5f), TexCoords = new Vector2(3, 3)
            };
            vertices[3] = new Vertex {
                Position = new Vector3(1, -1, 0.5f), TexCoords = new Vector2(0, 3)
            };

            var indices = new short[] { 0, 1, 2, 0, 2, 3 };

            var vertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertices, GraphicsResourceUsage.Default);
            var indexBuffer  = Buffer.Index.New(GraphicsDevice, indices, GraphicsResourceUsage.Default);
            var meshDraw     = new MeshDraw
            {
                DrawCount     = 4,
                PrimitiveType = PrimitiveType.TriangleList,
                VertexBuffers = new[]
                {
                    new VertexBufferBinding(vertexBuffer,
                                            new VertexDeclaration(VertexElement.Position <Vector3>(),
                                                                  VertexElement.TextureCoordinate <Vector2>()),
                                            4)
                },
                IndexBuffer = new IndexBufferBinding(indexBuffer, false, indices.Length),
            };

            mesh = new Mesh
            {
                Draw = meshDraw,
            };

            simpleEffect = new EffectInstance(new Effect(GraphicsDevice, SpriteEffect.Bytecode));
            simpleEffect.Parameters.Set(TexturingKeys.Texture0, UVTexture);
            simpleEffect.UpdateEffect(GraphicsDevice);

            // TODO GRAPHICS REFACTOR
            //vao = VertexArrayObject.New(GraphicsDevice, mesh.Draw.IndexBuffer, mesh.Draw.VertexBuffers);

            myDraws    = new DrawOptions[3];
            myDraws[0] = new DrawOptions {
                Sampler = GraphicsDevice.SamplerStates.LinearClamp, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(-0.5f, 0.5f, 0f))
            };
            myDraws[1] = new DrawOptions {
                Sampler = GraphicsDevice.SamplerStates.LinearWrap, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, 0.5f, 0f))
            };
            myDraws[2] = new DrawOptions {
                Sampler = SamplerState.New(GraphicsDevice, new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Mirror)), Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, -0.5f, 0f))
            };
            //var borderDescription = new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Border) { BorderColor = Color.Purple };
            //var border = SamplerState.New(GraphicsDevice, borderDescription);
            //myDraws[3] = new DrawOptions { Sampler = border, Transform = Matrix.Multiply(Matrix.Scale(0.3f), Matrix.Translation(-0.5f, -0.5f, 0f)) };
        }