• Advertisement
Sign in to follow this  

Correct way of using multipass effects in Effects framework

This topic is 1144 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello.

I am using sharpDX with effect framework and I want to render multipass effect, but faced with the issue that I could not apply second pass:

 

Here is my effect code:

Texture2D Texture;
SamplerState TextureSampler;

matrix OrthoMatrix;

struct VertexInputType
{
   float4 position : SV_POSITION;
   float4 color: COLOR;
   float2 tex : TEXCOORD0;
};

struct PixelInputType
{
   float4 position : SV_POSITION;
   float4 color: COLOR;
   float2 tex : TEXCOORD0;
};

PixelInputType SpriteVertexShader(VertexInputType input)
{
   PixelInputType output;
   output.color = float4(0, 0, 0, 0);
   output.position = float4(0, 0, 0, 0);
   
   // Change the position vector to be 4 units for proper matrix calculations.
   input.position.w = 1.0f;

   output.position = mul(input.position, OrthoMatrix);

   // Store the texture coordinates for the pixel shader.
   output.tex = input.tex;
   output.color = input.color;
   return output;
}

float4 SpritePixelShader(PixelInputType input) : SV_TARGET
{
   return Texture.Sample(TextureSampler, input.tex)* input.color;
}

float4 SpritePixelShader2(PixelInputType input) : SV_TARGET
{
   /*return Texture.Sample(TextureSampler, input.tex)+ input.color;*/
   return float4(0,1,0,0);
}


technique10 SpriteBatch
{
   pass P0
   {
      SetGeometryShader(0);
      SetVertexShader(CompileShader(vs_4_0, SpriteVertexShader()));
      SetPixelShader(CompileShader(ps_4_0, SpritePixelShader()));
   }

   pass P1
   {
      SetGeometryShader(0);
      SetVertexShader(0);
      SetPixelShader(CompileShader(ps_4_0, SpritePixelShader2()));
   }
}

And here is how I use effect:

var effectByteCode = ShaderBytecode.CompileFromFile(@"Content\Effects\SpriteBatchEffect.fx", "fx_5_0");
effect = new Effect(this.graphicsDevice, effectByteCode);

int passesCount = effect.GetTechniqueByName("SpriteBatch").Description.PassCount;
         var technique = effect.GetTechniqueByName("SpriteBatch");

         graphicsDevice.SetVertexBuffers(0, vertexBufferBinding);
         EffectPass localpass;
         for (int i = 0; i < passesCount; i++)
         {
            localpass = technique.GetPassByIndex(i);

            var passSignature = localpass.Description.Signature;

            layout = new InputLayout(graphicsDevice, passSignature, inputElements);

            graphicsDevice.InputLayout = layout;

            
            localpass.Apply(graphicsDevice);

            graphicsDevice.DrawIndexed(indexCount, 0, 0);
         }

On this line var 

passSignature = localpass.Description.Signature;

in the second pass I receive outofrange exception.

 

If I comment that line, There is no exception, but the second pass will never apply.

 

Could someone, please, say what I am doing wrong here?

Share this post


Link to post
Share on other sites
Advertisement

Input layouts are probably not something you want to allocate on the fly per drawcall like that.

 

But other than that, you might check that localpass.IsValid.  I've had cases where everything compiled but IsValid was false, and I can't remember what caused it.

 

Also don't you need a vertex shader for your second pass?

Share this post


Link to post
Share on other sites

I do like this now:

spriteBatchEffect.GetVariableByName("TextureSampler").AsSampler().SetSampler(0, textureSampler);
         spriteBatchEffect.GetVariableByName("Texture").AsShaderResource().SetResource(texture);
         spriteBatchEffect.GetVariableByName("OrthoMatrix").AsMatrix().SetMatrix(Ortho);

         int passesCount = spriteBatchEffect.GetTechniqueByName("SpriteBatch").Description.PassCount;
         var technique = spriteBatchEffect.GetTechniqueByName("SpriteBatch");
         EffectPass localpass;
         for (int i = 0; i < passesCount; i++)
         {
            localpass = technique.GetPassByIndex(i);
            if (localpass.IsValid)
            {
               localpass.Apply(graphicsDevice);
               graphicsDevice.DrawIndexed(indexCount, 0, 0);
            }
         
         }
         
      }

and add vertex shader to 2nd pass

pass P1
   {
      SetGeometryShader(0);
      SetVertexShader(CompileShader(vs_4_0, SpriteVertexShader()));
      SetPixelShader(CompileShader(ps_4_0, SpritePixelShader2()));
   }

But now I just dont see sprites. No errors, but also no image, but passes are valid.

 

Update:

I missed setting vertex buffer and now It rendering, but still only one path. I dont know why, but it ignores the second one.

Edited by BlackJoker

Share this post


Link to post
Share on other sites

If you put a breakpoint on the drawindexed, and see it calling on the second pass, my guess is your renderstates are to blame.

 

I use 2pass for drawing point and directional shadows onto my world geometry, so I set my depthfunc to EQUAL, and BlendOp to REV_SUBTRACT to get the shadow to draw onto what is already there.

 

In your case for spritey stuff, you might want to DepthEnable false, and do a traditional alpha blend (states very on that depending on if you are premultiplying or not)

Share this post


Link to post
Share on other sites

Interesting.... Could you please explain me a little about that. I just tested with depth disabled and second path applied to sprites, but for now I dont really understand why?

Does shader takes depth into account?

Share this post


Link to post
Share on other sites

Yes I think the default is only to draw a pixel if the depth is less than the current depth.  Usually for 2D stuff you'd just turn it off.

 

You can also manually reject a pixel with the clip() instruction.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement