Shader Effect on Offscreen Render

Hi all,

I started to test Shader Effects on DesktopGL for Texture Generation. And I’m having some problem.

If I render just my “sprite” (actually a texture filled with CornflowerBlue with the size of my RenderTarget) and then render my target on my scene, everthing works fine:

But if I enable my effect, I get a red screen:

BUT if I render my target in the same spritebatch that my scene uses, I get my shader result all over the screen (bigger than the Render Target)

I’m not sure why this is happening. It only happens when I enable the shader effect. Here is my Shader:

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

sampler TextureSampler : register(s0);
float time;
float2 resolution;

float4 main(float4 pos : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float2 p = (pos.xy / resolution.xy) - 0.5;
    float sx = 0.2* (p.x + 0.5) * sin( 50.0 * p.x - 10.f * time);
    float dy = 1.f/ ( 50.f * abs(p.y  - sx) );
    dy += 1.f/ (60.f  * length(p - float2(p.x, 0.)));

    return float4( (p.x + 0.4) * dy, 0.9 * dy, dy, 1.0 );
}


technique Technique1
{
    pass Pass1
    {
        PixelShader = compile PS_SHADERMODEL main();
    }
}

Here is how I’m rendering my “sprite” to the render target:

    protected override void preDraw(GameTime gameTime) {
      var graphicsDevice = Context.GraphicsDeviceManager.GraphicsDevice;
      graphicsDevice.SetRenderTarget(ShaderRenderTarget);
      graphicsDevice.Clear(Color.CornflowerBlue);

      eff.Parameters["time"].SetValue((float)gameTime.TotalGameTime.TotalSeconds);
      secondarySpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
      //eff.CurrentTechnique.Passes[0].Apply(); // If I uncomment this, it gets the red screen
      dummySprite.draw(secondarySpriteBatch, gameTime);
      secondarySpriteBatch.End();

      graphicsDevice.SetRenderTarget(null);
    }

And here is how I’m rendering my Render Target:

    protected override void draw(SpriteBatch spriteBatch, GameTime gameTime) {
      background.draw(spriteBatch, gameTime);
      shadow.draw(spriteBatch, gameTime);
      changeLog.draw(spriteBatch, gameTime);
      panel.draw(spriteBatch, gameTime);
      startGameText.draw(spriteBatch, gameTime);
      exitGameText.draw(spriteBatch, gameTime);
      versionInfo.draw(spriteBatch, gameTime);
      buildInfo.draw(spriteBatch, gameTime);

      spriteBatch.Draw((Texture2D) ShaderRenderTarget, new Vector2(0, 0), Color.White); // My RenderTarget rendered on the corner
    }

Here is how I’m initializing the Target and related stuff:


      eff = Context.load<Effect>("Shaders\\TestEffect");
      ShaderRenderTarget = Shader.createRenderTarget(Context.GraphicsDeviceManager.GraphicsDevice, (int) sizeW.X /4 , (int) sizeW.Y/ 4);
      ShaderTexture = new Texture2D(Context.GraphicsDeviceManager.GraphicsDevice, ShaderRenderTarget.Width, ShaderRenderTarget.Height);
      secondarySpriteBatch = new SpriteBatch(Context.GraphicsDeviceManager.GraphicsDevice);
      dummySprite = new RectangleShape();
      dummySprite.initialize(ShaderRenderTarget.Width, ShaderRenderTarget.Height, Color.CornflowerBlue);
      eff.Parameters["resolution"].SetValue(sizeW);

Is this a bug or I’m doing something wrong?

Thanks!

EDIT: Here is a project I created with just the problem I’m having: https://github.com/racerxdl/ShaderTextureTest

in the spritebatch.begin() you can define what effect to use. Try it out

tryed with:

    protected override void preDraw(GameTime gameTime) {
      var graphicsDevice = Context.GraphicsDeviceManager.GraphicsDevice;
      graphicsDevice.SetRenderTarget(ShaderRenderTarget);
      graphicsDevice.Clear(Color.CornflowerBlue);

      eff.Parameters["time"].SetValue((float)gameTime.TotalGameTime.TotalSeconds);
      secondarySpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, eff);
      //eff.CurrentTechnique.Passes[0].Apply();
      dummySprite.draw(secondarySpriteBatch, gameTime);
      secondarySpriteBatch.End();

      graphicsDevice.SetRenderTarget(null);
    }

And I got the same red screen.

wierd, i draw tons of stuff off screen and it’s no problem.

Here is an example

                _graphicsDevice.SetRenderTargets(_bloomRenderTarget2DMip0Binding);

                _spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp, null, null, _bloomEffect);
                _spriteBatch.Draw(_accumulationBuffer, new Rectangle(0, 0, width, height), Color.White);
                _spriteBatch.End();

maybe in here:
spriteBatch.Draw((Texture2D) ShaderRenderTarget, new Vector2(0, 0), Color.White);

you can try to define the rectangle instead of the position (new Vector2(0,0).

Btw using Vector2.Zero mgiht be a better alternative instead of new, not sure.

Where do you begin and end your sprite batch? I mean spritebatch you are using to draw into backbuffer…

afterwards - i switch my SetRenderTarget to null and draw another rendertarget to the screen.

I do in the draw loop:

    protected override void draw(SpriteBatch spriteBatch, GameTime gameTime) {
      background.draw(spriteBatch, gameTime);
      shadow.draw(spriteBatch, gameTime);
      changeLog.draw(spriteBatch, gameTime);
      panel.draw(spriteBatch, gameTime);
      startGameText.draw(spriteBatch, gameTime);
      exitGameText.draw(spriteBatch, gameTime);
      versionInfo.draw(spriteBatch, gameTime);
      buildInfo.draw(spriteBatch, gameTime);

      spriteBatch.Draw((Texture2D) ShaderRenderTarget, new Vector2(0, 0), Color.White); // My RenderTarget rendered on the corner
    }

that is called by:

    protected override void Draw(GameTime gameTime) {
      performanceCounter.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
      GraphicsDevice.Clear(Color.Black);

      spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
      sceneManager.draw(spriteBatch, gameTime);
      spriteBatch.End();

      base.Draw(gameTime);
    }

The ShaderRenderTarget is my RenderTarget2D. I’m casting to Texture2D because I saw a example doing that, but the result is the same without the cast.

Ok so to make things easier, here is a very small example of the problem (with just the thing I need):