Przejdź do głównej zawartości

Custom Scene Renderer with SpriteBatch

Ta treść nie jest jeszcze dostępna w Twoim języku.

To use custom shaders and effects with sprites we need to create a Custom Scene Renderer. This renderer is an example how the whole process works.

We need to create a struct that will represent the data that we want to pass to the shader. This struct will by shader’s StructuredBuffer.

ShaderParameters.cs
public struct ShaderParameters
{
public float Progress;
}

An example of a very basic shader that just changes the textures color to green.

ExampleShader.sdsl
shader ExampleShader : SpriteBase
{
struct ShaderParameters
{
float Progress;
};
StructuredBuffer<ShaderParameters> Data;
stage override float4 Shading()
{
float progress = Data[streams.InstanceID].Progress;
return float4(0.0f, progress, 0.0f, 1.0f);
}
};
ExampleShader.sdsl.cs
public static partial class ExampleShaderKeys
{
public static readonly ObjectParameterKey<Buffer> Data = ParameterKeys.NewObject<Buffer>();
}

We need a Custom Scene Renderer to inject our drawing logic into Stride’s render pipeline.

CustomSceneRenderer.cs
public class CustomSceneRenderer : SceneRendererBase
{
// Sprite batch is used to draw everything in a single draw call.
// You need only a single instance of this per SceneRenderer.
private SpriteBatch _spriteBatch;
// The depth texture is a buffer for managing the drawing order.
// Thanks to this objects behind other objects are not rendered.
// You should create a blank texture in an external class and pass it to the Renderer.
private Texture _depthTexture;
// The actual texture for GPU to draw on.
// You should create a blank texture in an external class and pass it to the Renderer.
private Texture _renderTexture;
// Your shader instance.
// You need only a single instance of this per SceneRenderer.
private EffectInstance _effectInstance;
// A buffer used to pass the data from the CPU to the GPU.
private Buffer<ShaderParameters> _effectBuffer;
// The params that will be passed to the shader.
// These params should be managed by some external script, like "ButtonAnimationScript".
// You will need an instance of the params per the element you want to draw.
private ShaderParameters _shaderParams;
protected override void InitializeCore()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_effectInstance = new EffectInstance(EffectSystem.LoadEffect("ExampleShader").WaitForResult());
_effectBuffer = Buffer.Structured.New<ShaderParameters>(GraphicsDevice, 1); // 1 - number of the elements in the buffer.
_effectInstance.Parameters.Set(ExampleShaderKeys.Data, _effectBuffer);
// -------------------------
// THIS SHOULD BE PASSED FROM THE EXTERNAL CLASS
// -------------------------
_depthTexture = Texture.New2D(GraphicsDevice, 64, 64, false, PixelFormat.D16_UNorm, TextureFlags.DepthStencil);
_renderTexture = Texture.New2D(GraphicsDevice, 64, 64, false, PixelFormat.R8G8B8A8_UNorm, TextureFlags.ShaderResource | TextureFlags.RenderTarget);
_shaderParams = new ShaderParameters { Progress = 0.5f };
}
protected override void DrawCore(RenderContext context, RenderDrawContext drawContext)
{
// A list of commands that will be send to the GPU.
var commandList = drawContext.CommandList;
// We don't want to draw our element on the scene directly, so we need to store a reference to the original RenderTarget;
var oldRenderTexture = commandList.RenderTarget;
// The same as above, we don't want to use a depth map from the scene.
var oldDepthTexture = commandList.DepthStencilBuffer;
// Pass the params to the shader.
// In real implementation you will have a list of params instead of a single instance.
_effectBuffer.SetData(commandList, shaderParams)
// Change the render target to our texture.
commandList.SetRenderTargetAndViewport(_depthTexture, _renderTexture);
// Clear the textures so we have a blank canvas to draw on.
commandList.Clear(_depthTexture, DepthStencilClearOptions.DepthBuffer);
commandList.Clear(_renderTexture, Color4.Black);
// Instructs the sprite buffer that we want to draw the sprites with a single draw call (Deferred).
_spriteBatch.Begin(drawContext.GraphicsContext, SpriteSortMode.Deferred, _effectInstance);
// Draw your elements like text or other textures.
//_spriteBatch.Draw()
// Instructs the sprite buffer to send the commands to the GPU.
_spriteBatch.End();
// Sets the original textures so our scene is renderer properly.
commandList.SetRenderTargetAndViewport(oldDepthTexture, oldRenderTexture);
// -------------------------
// HOW TO DRAW THE TEXTURE ON THE SCREEN
// This code shows how to draw the rendered texture from the code above to the game screen.
// -------------------------
_spriteBatch.Begin(drawContext.GraphicsContext, SpriteSortMode.Deffered, _effectInstance);
_spriteBatch.Draw(_renderTexture, new Vector2(0, 0)); // (0, 0) -> top-left corner of the screen.
_spriteBatch.End();
}
}