Thursday, November 17, 2011

Little COM smart pointer tip

Many people use CComPtr or Boost smart pointers to help with reference counting of COM/Direct3D objects, like vertex/index buffers, textures, etc. (see here for a good overview on the topic).

However CComPtr requires ATL and doesn't work with VS Express.

An alternative is to use _com_ptr_t which also is available in VS Express, with a little macro help you can wrap things up quite nicely:

#include "comip.h"

#define DECLARE_COM_REF(x) typedef \ 
_com_ptr_t< _com_IIID< IDirect3D##x##9, &IID_IDirect3D##x##9 > > \
x##Ref;

DECLARE_COM_REF(VertexBuffer);
DECLARE_COM_REF(IndexBuffer);
DECLARE_COM_REF(CubeTexture);
// etc.

With this you can then create some nice wrapper classes like this e.g.:

class VertexBuffer : public VertexBufferRef {
public:
    VertexBuffer(IDirect3DVertexBuffer9* vertex_buffer = NULL) { 
        Attach(vertex_buffer);
    }

    void* lock(UINT offset, UINT size, DWORD flags)
    {
        IDirect3DVertexBuffer9* p = GetInterfacePtr();
        assert(NULL != p);
        void* mem = 0;
        p->Lock(offset, size, &mem, flags);
        assert(NULL != mem);
        return mem;
    }

    void unlock()
    {
        GetInterfacePtr()->Unlock();
    }
};

Wednesday, January 12, 2011

Deferred Rendering: Reconstructing Position from Depth

Here is a short snippet of how to reconstruct view-/world-space position from depth in deferred rendering.

This was originally described by fpuig, but here is the cleaned up and bugfixed (hopefully..) version.

What's nice about this code is that it works both for light volume geometry (e.g. spheres for omni lights) and fullscreen quads:

1) In your GBuffer pass store positionInViewSpace.z in the depth rendertarget.

2) The lighting vertex-shader calculates the eye-to-pixel rays (in view-/world-space):
    OUT.position =  mul(matrixWVP, float4(IN.position,1));
    OUT.vPos = ConvertToVPos(OUT.position); // sm2 has no VPOS..
    OUT.vEyeRayVS = float3(OUT.position.x*TanHalfFOV*ViewAspect, 
                           OUT.position.y*TanHalfFOV, OUT.position.w);
    OUT.vEyeRay = mul(matrixViewInv, OUT.vEyeRayVS);
In shader model 2 we don't have the VPOS interpolator, so we can use the following function (RTWidth/Height is the size of your screen/rendertargets):
    float4 ConvertToVPos(float4 p)
    {
        return float4(0.5*(float2(p.x+p.w, p.w-p.y) + 
                      p.w*float2(1.0f/RTWidth, 1.0f/RTHeight)), p.zw);
    }
3) Then, in the pixel-shader one can compute the view-space and world-space position per pixel:
    float depth = tex2Dproj(GBufferDepthSampler, IN.vPos); // divide by W
    IN.vEyeRayVS.xyz /= IN.vEyeRayVS.z; // divide by W
    float3 pixelPosVS = IN.vEyeRayVS.xyz * depth;
    float3 pixelPosWS = IN.vEyeRay.xyz * depth + cameraPosition.xyz;

This can be optimized a lot, of course.