예제 #1
0
        private void RenderTexturedBackground(IDrawDevice device)
        {
            if (!cachedTexturedBackground.IsAvailable)
            {
                return;
            }

            float timeMult = Time.TimeMult;

            backgroundX     += timeMult * 1.2f;
            backgroundY     += timeMult * -0.2f + timeMult * MathF.Sin(backgroundPhase) * 0.6f;
            backgroundPhase += timeMult * 0.001f;

            Vector3 renderPos = new Vector3(0, 0, 600);

            // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets
            renderPos.X = MathF.Round(renderPos.X);
            renderPos.Y = MathF.Round(renderPos.Y);
            if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2)
            {
                renderPos.X += 0.5f;
            }
            if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2)
            {
                // AMD Bugfix?
                renderPos.Y -= 0.004f;
            }

            // Reserve the required space for vertex data in our locally cached buffer
            int neededVertices = 4;

            if (cachedVertices == null || cachedVertices.Length < neededVertices)
            {
                cachedVertices = new VertexC1P3T2[neededVertices];
            }

            // Render it as world-space fullscreen quad
            cachedVertices[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z);
            cachedVertices[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z);
            cachedVertices[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);
            cachedVertices[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);

            cachedVertices[0].TexCoord = new Vector2(0.0f, 0.0f);
            cachedVertices[1].TexCoord = new Vector2(1f, 0.0f);
            cachedVertices[2].TexCoord = new Vector2(1f, 1f);
            cachedVertices[3].TexCoord = new Vector2(0.0f, 1f);

            cachedVertices[0].Color = cachedVertices[1].Color = cachedVertices[2].Color = cachedVertices[3].Color = ColorRgba.White;

            // Setup custom pixel shader
            BatchInfo material = device.RentMaterial();

            material.Technique   = texturedBackgroundShader;
            material.MainTexture = cachedTexturedBackground;
            material.SetValue("horizonColor", horizonColor);
            material.SetValue("shift", new Vector2(backgroundX, backgroundY));
            material.SetValue("parallaxStarsEnabled", 0f);

            device.AddVertices(material, VertexMode.Quads, cachedVertices, 0, 4);
        }
예제 #2
0
        private void ProcessBloomStep(RenderStep step, DrawDevice drawDevice)
        {
            ContentRef <RenderTarget> outputTarget = drawDevice.Target;
            Vector2 imageSize    = drawDevice.TargetSize;
            Rect    viewportRect = drawDevice.ViewportRect;

            this.SetupTargets((Point2)drawDevice.TargetSize);

            // Extract bright spots from the rendered image
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = this.techFilterBrightness;
                material.MainTexture = step.Input.MainTexture;
                material.SetValue("minBrightness", this.minBrightness);
                material.SetValue("bloomStrength", this.bloomStrength);
                this.Blit(drawDevice, material, this.targetPingPongA[0]);
            }

            // Downsample to lowest target
            for (int i = 1; i < this.targetPingPongA.Length; i++)
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = this.techDownsample;
                material.MainTexture = this.targetPingPongA[i - 1].Targets[0];
                this.Blit(drawDevice, material, this.targetPingPongA[i]);
            }

            // Blur all targets, separating horizontal and vertical blur
            for (int i = 0; i < this.targetPingPongA.Length; i++)
            {
                BatchInfo material;
                material           = drawDevice.RentMaterial();
                material.Technique = this.techBlur;

                material.MainTexture = this.targetPingPongA[i].Targets[0];
                material.SetValue("blurDirection", new Vector2(1.0f, 0.0f));
                this.Blit(drawDevice, material, this.targetPingPongB[i]);

                material           = drawDevice.RentMaterial();
                material.Technique = this.techBlur;

                material.MainTexture = this.targetPingPongB[i].Targets[0];
                material.SetValue("blurDirection", new Vector2(0.0f, 1.0f));
                this.Blit(drawDevice, material, this.targetPingPongA[i]);
            }

            // Combine all targets into the final image using the draw device's original target
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = this.techCombineFinal;
                material.MainTexture = step.Input.MainTexture;
                material.SetTexture("blurFullTex", this.targetPingPongA[0].Targets[0]);
                material.SetTexture("blurHalfTex", this.targetPingPongA[1].Targets[0]);
                material.SetTexture("blurQuarterTex", this.targetPingPongA[2].Targets[0]);
                material.SetTexture("blurEighthTex", this.targetPingPongA[3].Targets[0]);
                this.Blit(drawDevice, material, outputTarget.Res, imageSize, viewportRect);
            }
        }
예제 #3
0
        public void RenderTexturedBackground(IDrawDevice device, ref TileMapLayer layer, int cacheIndex, float x, float y)
        {
            if (!cachedTexturedBackground.IsAvailable || cachedTexturedBackgroundAnimated)
            {
                RecreateTexturedBackground(ref layer);
            }

            // Fit the input material rect to the output size according to rendering step config
            Vector3 renderPos = new Vector3(device.ViewerPos.X - device.TargetSize.X / 2, device.ViewerPos.Y - device.TargetSize.Y / 2, layer.Depth);

            // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets
            renderPos.X = MathF.Round(renderPos.X);
            renderPos.Y = MathF.Round(renderPos.Y);
            if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2)
            {
                renderPos.X += 0.5f;
            }
            if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2)
            {
                // AMD Bugfix?
                renderPos.Y -= 0.004f;
            }

            // Reserve the required space for vertex data in our locally cached buffer
            int neededVertices = 4;

            if (cachedVertices == null || cachedVertices.Length < neededVertices)
            {
                cachedVertices = new VertexC1P3T2[neededVertices];
            }

            // Render it as world-space fullscreen quad
            cachedVertices[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z);
            cachedVertices[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z);
            cachedVertices[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);
            cachedVertices[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);

            cachedVertices[0].TexCoord = new Vector2(0f, 0f);
            cachedVertices[1].TexCoord = new Vector2(1f, 0f);
            cachedVertices[2].TexCoord = new Vector2(1f, 1f);
            cachedVertices[3].TexCoord = new Vector2(0f, 1f);

            cachedVertices[0].Color = cachedVertices[1].Color = cachedVertices[2].Color = cachedVertices[3].Color = ColorRgba.White;

            // Setup custom pixel shader
            BatchInfo material = device.RentMaterial();

            material.Technique   = texturedBackgroundShader;
            material.MainTexture = cachedTexturedBackground;
            material.SetValue("horizonColor", layer.BackgroundColor);
            material.SetValue("shift", new Vector2(x, y));
            material.SetValue("parallaxStarsEnabled", layer.ParallaxStarsEnabled ? 1f : 0f);

            device.AddVertices(material, VertexMode.Quads, cachedVertices, 0, 4);
        }
예제 #4
0
        public override void Draw(IDrawDevice device)
        {
            if (animHidden)
            {
                return;
            }

            Texture       mainTex = RetrieveMainTex();
            DrawTechnique tech    = RetrieveDrawTechnique();

            Rect uvRect, uvRectNext;
            bool smoothShaderInput = (tech != null && tech.PreferredVertexFormat == VertexC1P3T4A1.Declaration);

            GetAnimData(mainTex, smoothShaderInput, out uvRect, out uvRectNext);

            if (!smoothShaderInput)
            {
                PrepareVertices(ref vertices, device, this.colorTint, uvRect);
                if (customMat != null)
                {
                    device.AddVertices(customMat, VertexMode.Quads, vertices, 0, 4);
                }
                else
                {
                    if (flipMode == 0)
                    {
                        device.AddVertices(sharedMat, VertexMode.Quads, vertices, 0, 4);
                    }
                    else
                    {
                        BatchInfo material = device.RentMaterial(sharedMat.Res.Info);
                        material.SetValue("normalMultiplier", new Vector2((flipMode & FlipMode.Horizontal) == 0 ? 1 : -1f, (flipMode & FlipMode.Vertical) == 0 ? 1 : -1f));
                        device.AddVertices(material, VertexMode.Quads, vertices, 0, 4);
                    }
                }
            }
            else
            {
                PrepareVerticesSmooth(ref verticesSmooth, device, curAnimFrameFade, this.colorTint, uvRect, uvRectNext);
                if (customMat != null)
                {
                    device.AddVertices(customMat, VertexMode.Quads, verticesSmooth, 0, 4);
                }
                else
                {
                    if (flipMode == 0)
                    {
                        device.AddVertices(sharedMat, VertexMode.Quads, verticesSmooth, 0, 4);
                    }
                    else
                    {
                        BatchInfo material = device.RentMaterial(sharedMat.Res.Info);
                        material.SetValue("normalMultiplier", new Vector2((flipMode & FlipMode.Horizontal) == 0 ? 1 : -1f, (flipMode & FlipMode.Vertical) == 0 ? 1 : -1f));
                        device.AddVertices(material, VertexMode.Quads, verticesSmooth, 0, 4);
                    }
                }
            }
        }
예제 #5
0
        private void ProcessResizeStep(DrawDevice drawDevice)
        {
            BatchInfo material = drawDevice.RentMaterial();

            material.Technique   = resizeShader;
            material.MainTexture = finalTexture;
            material.SetValue("mainTexSize", new Vector2(finalTexture.ContentWidth, finalTexture.ContentHeight));
            this.Blit(drawDevice, material, drawDevice.ViewportRect);
        }
예제 #6
0
        void ICmpUpdatable.OnUpdate()
        {
            var renderer = GameObj.GetComponent <SpriteRenderer>();

            _batch = _batch ?? new BatchInfo(renderer.SharedMaterial.Res);
            renderer.CustomMaterial = _batch;
            var offset = Speed * (float)Time.GameTimer.TotalSeconds;

            offset.X %= 1;
            offset.Y %= 1;
            _batch.SetValue("texOffset", offset);
        }
예제 #7
0
        public Texture RequestBlurredInGame()
        {
#if PLATFORM_ANDROID || PLATFORM_WASM
            // Blur is disabled in Android and WebAssembly version
            return(finalTexture);
#else
            DrawDevice device = new DrawDevice();
            device.Projection = ProjectionMode.Screen;

            // One temporary material is used for all operations
            BatchInfo tempMaterial = device.RentMaterial();

            // Downsample to half size
            tempMaterial.Technique   = downsampleShader;
            tempMaterial.MainTexture = finalTexture;
            tempMaterial.SetValue("pixelOffset", new Vector2(1f / finalTexture.ContentWidth, 1f / finalTexture.ContentHeight));
            this.Blit(device, tempMaterial, targetPingPongA[0]);

            // Downsample to quarter size
            tempMaterial.MainTexture = targetPingPongA[0].Targets[0];
            tempMaterial.SetValue("pixelOffset", new Vector2(1f / tempMaterial.MainTexture.Res.ContentWidth, 1f / tempMaterial.MainTexture.Res.ContentHeight));
            this.Blit(device, tempMaterial, targetPingPongA[1]);

            // Blur last target, separating horizontal and vertical blur
            tempMaterial.Technique   = blurShader;
            tempMaterial.MainTexture = targetPingPongA[1].Targets[0];
            tempMaterial.SetValue("blurDirection", new Vector2(1f, 0f));
            tempMaterial.SetValue("pixelOffset", new Vector2(1f / tempMaterial.MainTexture.Res.ContentWidth, 1f / tempMaterial.MainTexture.Res.ContentHeight));

            this.Blit(device, tempMaterial, targetPingPongB[1]);

            tempMaterial.MainTexture = targetPingPongB[1].Targets[0];
            tempMaterial.SetValue("blurDirection", new Vector2(0f, 1f));
            tempMaterial.SetValue("pixelOffset", new Vector2(1f / tempMaterial.MainTexture.Res.ContentWidth, 1f / tempMaterial.MainTexture.Res.ContentHeight));

            this.Blit(device, tempMaterial, targetPingPongA[1]);

            return(targetPingPongA[1].Targets[0].Res);
#endif
        }
예제 #8
0
        private void ProcessCombineSceneStep(DrawDevice drawDevice)
        {
            // ToDo: Split lighting to RGB channels
            // ToDo: Implement dynamic lighting/shadows (https://github.com/mattdesl/lwjgl-basics/wiki/2D-Pixel-Perfect-Shadows)

            Vector2 viewSize   = drawDevice.TargetSize;
            Vector2 viewOffset = new Vector2(
                drawDevice.RefCoord.X - viewSize.X / 2,
                drawDevice.RefCoord.Y - viewSize.Y / 2
                );

            float ambientLight   = levelHandler.AmbientLightCurrent;
            float viewWaterLevel = (levelHandler.WaterLevel - viewOffset.Y);

            // Blit ambient light color
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique = DrawTechnique.Solid;
                material.MainColor = new ColorRgba(ambientLight, 0, 0);
                this.Blit(drawDevice, material, lightingTarget);
            }

            // Render lights (target was set in previous step)
            drawDevice.PrepareForDrawcalls();

            foreach (GameObject actor in levelHandler.ActiveObjects)
            {
                LightEmitter light = actor.GetComponent <LightEmitter>();
                if (light != null)
                {
                    // World-space to screen-space position transformation
                    Vector3 pos = actor.Transform.Pos;
                    pos.X -= viewOffset.X;
                    pos.Y -= viewOffset.Y;

                    float left   = pos.X - light.RadiusFar;
                    float top    = pos.Y - light.RadiusFar;
                    float right  = pos.X + light.RadiusFar;
                    float bottom = pos.Y + light.RadiusFar;

                    if (left < viewSize.X &&
                        top < viewSize.Y &&
                        right > 0 &&
                        bottom > 0)
                    {
                        lightBuffer[0].Pos.X = left;
                        lightBuffer[0].Pos.Y = top;

                        lightBuffer[1].Pos.X = left;
                        lightBuffer[1].Pos.Y = bottom;

                        lightBuffer[2].Pos.X = right;
                        lightBuffer[2].Pos.Y = bottom;

                        lightBuffer[3].Pos.X = right;
                        lightBuffer[3].Pos.Y = top;

                        // Use TexCoord X & Y for screen-space Light position
                        lightBuffer[0].TexCoord.X = lightBuffer[1].TexCoord.X = lightBuffer[2].TexCoord.X = lightBuffer[3].TexCoord.X = pos.X;
                        lightBuffer[0].TexCoord.Y = lightBuffer[1].TexCoord.Y = lightBuffer[2].TexCoord.Y = lightBuffer[3].TexCoord.Y = pos.Y;
                        // Use TexCoord Z & W for Light radius
                        lightBuffer[0].TexCoord.Z = lightBuffer[1].TexCoord.Z = lightBuffer[2].TexCoord.Z = lightBuffer[3].TexCoord.Z = light.RadiusNear;
                        lightBuffer[0].TexCoord.W = lightBuffer[1].TexCoord.W = lightBuffer[2].TexCoord.W = lightBuffer[3].TexCoord.W = light.RadiusFar;

                        // Use Red channel for Light intensity
                        lightBuffer[0].Color.R = lightBuffer[1].Color.R = lightBuffer[2].Color.R = lightBuffer[3].Color.R = (byte)(light.Intensity * 255);
                        // Use Green channel for Light brightness
                        lightBuffer[0].Color.G = lightBuffer[1].Color.G = lightBuffer[2].Color.G = lightBuffer[3].Color.G = (byte)(light.Brightness * 255);

                        switch (light.Type)
                        {
                        default:
                        case LightType.Solid:
                            drawDevice.AddVertices(lightingMaterial, VertexMode.Quads, lightBuffer);
                            break;

                        case LightType.WithNoise:
                            drawDevice.AddVertices(lightingNoiseMaterial, VertexMode.Quads, lightBuffer);
                            break;
                        }
                    }
                }
            }

            drawDevice.Render();

            // Resize Blur targets
            SetupTargets((Point2)drawDevice.TargetSize);

            // Blit it into screen
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = DrawTechnique.Solid;
                material.MainTexture = mainTexture;
                this.Blit(drawDevice, material, targetPingPongA[0]);
            }

            // Downsample to lowest target
            for (int i = 1; i < targetPingPongA.Length; i++)
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = downsampleShader;
                material.MainTexture = targetPingPongA[i - 1].Targets[0];
                material.SetValue("pixelOffset", new Vector2(1f / material.MainTexture.Res.ContentWidth, 1f / material.MainTexture.Res.ContentHeight));

                this.Blit(drawDevice, material, targetPingPongA[i]);
            }

            // Blur all targets, separating horizontal and vertical blur
            for (int i = 0; i < targetPingPongA.Length; i++)
            {
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique   = blurShader;
                material.MainTexture = targetPingPongA[i].Targets[0];
                material.SetValue("blurDirection", new Vector2(1f, 0f));
                material.SetValue("pixelOffset", new Vector2(1f / material.MainTexture.Res.ContentWidth, 1f / material.MainTexture.Res.ContentHeight));

                this.Blit(drawDevice, material, targetPingPongB[i]);

                material.MainTexture = targetPingPongB[i].Targets[0];
                material.SetValue("blurDirection", new Vector2(0f, 1f));
                material.SetValue("pixelOffset", new Vector2(1f / material.MainTexture.Res.ContentWidth, 1f / material.MainTexture.Res.ContentHeight));

                this.Blit(drawDevice, material, targetPingPongA[i]);
            }

            // Blit it into screen
            if (viewWaterLevel < viewSize.Y)
            {
                // Render lighting with water
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique = combineSceneWaterShader;
                material.SetTexture("mainTex", mainTexture);
                material.SetTexture("lightTex", lightingTexture);
                material.SetTexture("displacementTex", noiseTexture); // Underwater displacement

                material.SetTexture("blurHalfTex", targetPingPongA[1].Targets[0]);
                material.SetTexture("blurQuarterTex", targetPingPongA[2].Targets[0]);

                material.SetValue("ambientLight", ambientLight);
                material.SetValue("darknessColor", levelHandler.DarknessColor);

                material.SetValue("waterLevel", viewWaterLevel / viewSize.Y);

                this.Blit(drawDevice, material, finalTarget);
            }
            else
            {
                // Render lighting without water
                BatchInfo material = drawDevice.RentMaterial();
                material.Technique = combineSceneShader;
                material.SetTexture("mainTex", mainTexture);
                material.SetTexture("lightTex", lightingTexture);

                material.SetTexture("blurHalfTex", targetPingPongA[1].Targets[0]);
                material.SetTexture("blurQuarterTex", targetPingPongA[2].Targets[0]);

                material.SetValue("ambientLight", ambientLight);
                material.SetValue("darknessColor", levelHandler.DarknessColor);

                this.Blit(drawDevice, material, finalTarget);
            }
        }
예제 #9
0
        public void RenderTexturedBackground(IDrawDevice device, ref TileMapLayer layer, int cacheIndex, float x, float y)
        {
            if (cachedTexturedBackground == null || cachedTexturedBackgroundAnimated)
            {
                RecreateTexturedBackground(ref layer);
            }

            // Fit the input material rect to the output size according to rendering step config
            Vector3 renderPos = new Vector3(device.RefCoord.X - device.TargetSize.X / 2, device.RefCoord.Y - device.TargetSize.Y / 2, layer.Depth);
            float   scale     = 1.0f;

            device.PreprocessCoords(ref renderPos, ref scale);

            // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets
            renderPos.X = MathF.Round(renderPos.X);
            renderPos.Y = MathF.Round(renderPos.Y);
            if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2)
            {
                renderPos.X += 0.5f;
            }
            if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2)
            {
                //renderPos.Y += 0.5f;
                // AMD Bugfix?
                renderPos.Y -= 0.001f;
            }

            // Reserve the required space for vertex data in our locally cached buffer
            VertexC1P3T2[] vertexData;

            int neededVertices = 4;

            if (cachedVertices[cacheIndex] == null || cachedVertices[cacheIndex].Length < neededVertices)
            {
                cachedVertices[cacheIndex] = vertexData = new VertexC1P3T2[neededVertices];
            }
            else
            {
                vertexData = cachedVertices[cacheIndex];
            }

            // Render it as world-space fullscreen quad
            vertexData[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z);
            vertexData[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z);
            vertexData[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);
            vertexData[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);

            vertexData[0].TexCoord = new Vector2(0.0f, 0.0f);
            vertexData[1].TexCoord = new Vector2(1f, 0.0f);
            vertexData[2].TexCoord = new Vector2(1f, 1f);
            vertexData[3].TexCoord = new Vector2(0.0f, 1f);

            vertexData[0].Color = vertexData[1].Color = vertexData[2].Color = vertexData[3].Color = ColorRgba.White;

            // Setup custom pixel shader
            BatchInfo material = new BatchInfo(texturedBackgroundShader, cachedTexturedBackground);

            material.SetValue("horizonColor", layer.BackgroundColor);
            material.SetValue("shift", new Vector2(x, y));

            device.AddVertices(material, VertexMode.Quads, vertexData);
        }
예제 #10
0
        private void RenderTexturedBackground(IDrawDevice device)
        {
            if (cachedTexturedBackground == null)
            {
                return;
            }

            float timeMult = Time.TimeMult;

            backgroundX     += timeMult * 1.2f;
            backgroundY     += timeMult * -0.2f + timeMult * MathF.Sin(backgroundPhase) * 0.6f;
            backgroundPhase += timeMult * 0.001f;

            Vector3 renderPos = new Vector3(0, 0, (device.NearZ + device.FarZ) * 0.5f);

            // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets
            renderPos.X = MathF.Round(renderPos.X);
            renderPos.Y = MathF.Round(renderPos.Y);
            if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2)
            {
                renderPos.X += 0.5f;
            }
            if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2)
            {
                //renderPos.Y += 0.5f;
                // AMD Bugfix?
                renderPos.Y -= 0.001f;
            }

            // Reserve the required space for vertex data in our locally cached buffer
            VertexC1P3T2[] vertexData;

            int neededVertices = 4;

            if (cachedVertices == null || cachedVertices.Length < neededVertices)
            {
                cachedVertices = vertexData = new VertexC1P3T2[neededVertices];
            }
            else
            {
                vertexData = cachedVertices;
            }

            // Render it as world-space fullscreen quad
            vertexData[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z);
            vertexData[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z);
            vertexData[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);
            vertexData[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z);

            vertexData[0].TexCoord = new Vector2(0.0f, 0.0f);
            vertexData[1].TexCoord = new Vector2(1f, 0.0f);
            vertexData[2].TexCoord = new Vector2(1f, 1f);
            vertexData[3].TexCoord = new Vector2(0.0f, 1f);

            vertexData[0].Color = vertexData[1].Color = vertexData[2].Color = vertexData[3].Color = ColorRgba.White;

            // Setup custom pixel shader
            BatchInfo material = new BatchInfo(texturedBackgroundShader, cachedTexturedBackground);

            material.SetValue("horizonColor", horizonColor);
            material.SetValue("shift", new Vector2(backgroundX, backgroundY));

            device.AddVertices(material, VertexMode.Quads, vertexData);
        }