Correct way of using multipass effects in Effects framework

Started by
4 comments, last by kbaird 9 years, 1 month ago

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?

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?

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.

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)

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?

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.

This topic is closed to new replies.

Advertisement