DirectX10: apply an effect to a texture

717 views Asked by At

I have a texture that becomes the input to a pixel shader.

Code snippet:

mDevice->CreateShaderResourceView(myTexture, nullptr, byRef(mBckSRView));
...->AsShaderResource()->SetResource(mBckSRView);

However, I want to apply a blur filter to this texture before it is used in the shader. How do I go about this?

1

There are 1 answers

4
Ivan Aksamentov - Drop On BEST ANSWER

I want to apply a blur filter to this texture before it is used in the shader.

However, I want add other effects as well

How?

Simplest blur, algorithm is as follows: sample multiple texels around your current texel and combine their colors in whatever fashion you like: mean value, linear interpolation, multiply on weights, Gaussian function, combination of all of that etc.

Of course, that algorithm is a brute-force solution. There are huge field for optimizations. But it simple and can be enough for you. If not, just Google up for algorithm you need for language you using and read some papers.

Where?

  1. You can do it in pixel shader.

    • Pros: you can vary algorithm for each pixel for each frame. Just reload or branch shader. Wow!
    • Cons: sampling is pretty costly operation, for example, you sample 9 or 25 or 100 texels instead of one.
  2. You can do it on CPU side: just apply effect to raw bitmap data itself (array of pixels) before resource creation (after loading but before CreateTexture2D and CreateShaderResourceView).

    • Pros: you can still vary algorithm on runtime; there are libraries that can do all work for you
    • Cons: pixel shader remains unchanged and only one texel sampled;
  3. You can apply effect offline, i.e. before application even starts. Ask your artist to make bunch of textures with different effects applied in Photoshop or GIMP. Or automate it using scripts.

    • Pros: zero runtime costs; zero programming (artist's work ;))
    • Cons: fixed set of effects; more assets to load

Edit: combining complex multisample effects in shader.

So far so good, but what if you want to combine effects in shader? Well, you just applying them one by one in a serial way in shader code: after first effect you have a pixel's color and you use it as a source for second effect, etc.

But what if you want to apply two different blurs one after another? Blur requires neighbor pixels of the source texture to be sampled, but we have only current pixel. Solution is to render to texture and make another pass (draw call) with rendered texture as input and second blur shader.

Edit2

The issue is how to apply a shader to a texture2d object which becomes the input to another shader

I'll try to clarify. You have at least t̶w̶o̶ three alternatives. In short:

  1. Do it directly in "another shader", just imagine that instead of sampling, you sampling + applying first effect. Here is example for pixel shader (but it can be any shader stage):

    //pseudocode of example "multieffect" pixel shader
    Texture2D texture0;
    SamplerState defaultSampler;
    
    // first effect moved to function (surely it can be in main()'s body instead)
    float4 sampleAndApplyFirstEffect(Texture2D texture, SamplerState sampler, 
                                         float2 texcoord, float effectParameters)
    {
        float4 rawColor = texture.Sample(sampler, texcoord); // sample original  
        float4 color = ... // apply 1st effect's algo (you can use passed
                           // effectParameters to customize it)
        return color;
    }
    
    // shader entry point
    float4 anotherShaderMain(PS_IN psin)
    {
    
       // instead of sampling you get output of your first effect
       // (it doesn't really need to be a separate function, 
       // but that make things looks cleaner)
       float4 color = sampleAndApplyFirstEffect(texture0, defaultSampler, 
                                                psin.texcoord, 200.0f);
    
       color *= 0.5f; // apply your second effect
       color = MyThirdEffect(color); // third effect, etc.
       return color;
    }
    
  2. You cannot apply blur as "second effect" or MyThirdEffect, because it requires multiple texels to sample, but you have only one as output of first effect. For that we need to have whole texture being already preprocessed. To achieve that, you will need to render to texture and move second effect to second pass:

    // pseudocode of API calls:
    
    // pass 1:
    context->OMSetRenderTargets(texture1);    // use texture1 as destination
    context->PSSetShaderResource(rawTexture); // use rawTexture as a source
    context->IASetVertexBuffers(simpleQuadVB);// geometry is a quad
    context->VSSetShader(simpleQuadVS);       // use simple quad vertex shader
    context->PSSetShader(firstEffect);        // and First Effect's pixel shader
    context->Draw();       
    
    // pass 2:    
    context->OMSetRenderTargets(texture2);    // use texture2  as destination
    context->PSSetShaderResource(texture1);   // use texture1 as a source
    context->IASetVertexBuffers(simpleQuadVB);// geometry is a quad
    context->VSSetShader(simpleQuadVS);       // use simple quad vertex shader
    context->PSSetShader(secondEffectPS);     // and Second Effect's pixel shader
    context->Draw();                          
    // Note that in secondEffectPS you can sample 
    // as much texels of texture1 as you want
    
    // pass N: 
    // repeat as much times as you need
    
  3. If you can, use Compute shader and forget all of above =)

Hope it helps!