Jump to content
  • Advertisement
Sign in to follow this  
blueshogun96

Gaussian blur algorithm

This topic is 3332 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

I'm working on my implementation of a gaussian blur algorithm. The code I'm using is based off of one from a gamedev.net article: http://www.gamedev.net/reference/programming/features/imageproc/page2.asp While the code didn't appear to be API specific, my implementation uses Direct3D. I'm not sure whether the problem lies within Direct3D or the algorithm itself. The reason I'm doing this is to create a glow effect as described in these documents: http://www.gamasutra.com/view/feature/2107/realtime_glow.php http://developer.nvidia.com/docs/IO/8230/D3DTutorial_EffectsNV.pdf Below is my code for performing the gaussian blur (just the algorithm itself):
.....

	// Perform the gaussian blur
	if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
	{
		pdwPixels = (DWORD*) LockedRect.pBits;
		Pitch = (INT) LockedRect.Pitch;

		// Horizontal blur
		for( int i = 1; i < Width - 1; i++ )
		{
			for( int j = 1; j < Height - 1; j++ )
			{
				// Clear colour fields
				dwRed = dwGreen = dwBlue = dwAlpha = 0;

				for( int k = 0; k < GaussWidth; k++ )
				{
					INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
					INT y = j;
					DWORD dwColour = pdwPixels[y*Pitch+x];

					DWORD a = ( dwColour >> 24 ) & 0xFF;
					DWORD r = ( dwColour >> 16 ) & 0xFF;
					DWORD g = ( dwColour >> 8 ) & 0xFF;
					DWORD b = ( dwColour >> 0 ) & 0xFF;

					dwAlpha += a * GaussFact[k];
					dwRed   += r * GaussFact[k];
					dwGreen += g * GaussFact[k];
					dwBlue  += b * GaussFact[k];
				}

				// Draw blurred pixel
				pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
			}
		}

		// Vertical blur
		for( int i = 1; i < Width - 1; i++ )
		{
			for( int j = 1; j < Height - 1; j++ )
			{
				// Clear colour fields
				dwRed = dwGreen = dwBlue = dwAlpha = 0;

				for( int k = 0; k < GaussWidth; k++ )
				{
					INT x = i;
					INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
					DWORD dwColour = pdwPixels[y*Pitch+x];

					DWORD a = ( dwColour >> 24 ) & 0xFF;
					DWORD r = ( dwColour >> 16 ) & 0xFF;
					DWORD g = ( dwColour >> 8 ) & 0xFF;
					DWORD b = ( dwColour >> 0 ) & 0xFF;

					dwAlpha += a * GaussFact[k];
					dwRed   += r * GaussFact[k];
					dwGreen += g * GaussFact[k];
					dwBlue  += b * GaussFact[k];
				}

				dwAlpha /= GaussSum;
				dwRed   /= GaussSum;
				dwGreen /= GaussSum;
				dwBlue  /= GaussSum;

				// Draw blurred pixel
				pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
			}
		}

		// Unlock the texture
		pTempSurface->UnlockRect();

.....

And here is the full source to the function I wrote to do this (including Direct3D code).
//--------------------------------------------------------------------------------------
// Name: DoGaussianBlur
// Desc: Performs the gaussian blur algorithm on the entire render target, then saves the
//		 output to a texture of the same size and format.  This code assumes the texture 
//		 is 32-bit (D3DFMT_A8R8G8B8).
//--------------------------------------------------------------------------------------
bool DoGaussianBlur( INT Width, INT Height, IDirect3DSurface9* pRTSurface, IDirect3DTexture9** ppTexture )
{
	IDirect3DSurface9* pTempSurface = NULL;
	DWORD	dwRed = 0, dwGreen = 0, dwBlue = 0, dwAlpha = 0;
	const INT GaussWidth = 7;
	INT		GaussFact[GaussWidth] = { 1, 6, 15, 20, 15, 6, 1 };
	INT		GaussSum = 64;
	D3DLOCKED_RECT LockedRect;
	DWORD*	pdwPixels = NULL;
	INT		Pitch = 0;

	// Verify this texture
	if( !pRTSurface )
		return false;

	// Create a temporary surface.
	HRESULT hr = lpD3DDevice->CreateOffscreenPlainSurface( Width, Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pTempSurface, NULL );
	if( FAILED( hr ) )
	{
		OutputDebugStringA( "Error creating temporary surface!\n" );
		return false;
	}

	// Copy the contents of the render target to the newly created surface
	hr = lpD3DDevice->GetRenderTargetData( pRTSurface, pTempSurface );
	if( FAILED( hr ) )
	{
		OutputDebugStringA( "Error getting render target data!\n" );
		SAFE_RELEASE( pTempSurface );
		return false;
	}

	// Perform the gaussian blur
	if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
	{
		pdwPixels = (DWORD*) LockedRect.pBits;
		Pitch = (INT) LockedRect.Pitch;

		// Horizontal blur
		for( int i = 1; i < Width - 1; i++ )
		{
			for( int j = 1; j < Height - 1; j++ )
			{
				// Clear colour fields
				dwRed = dwGreen = dwBlue = dwAlpha = 0;

				for( int k = 0; k < GaussWidth; k++ )
				{
					INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
					INT y = j;
					DWORD dwColour = pdwPixels[y*Pitch+x];

					DWORD a = ( dwColour >> 24 ) & 0xFF;
					DWORD r = ( dwColour >> 16 ) & 0xFF;
					DWORD g = ( dwColour >> 8 ) & 0xFF;
					DWORD b = ( dwColour >> 0 ) & 0xFF;

					dwAlpha += a * GaussFact[k];
					dwRed   += r * GaussFact[k];
					dwGreen += g * GaussFact[k];
					dwBlue  += b * GaussFact[k];
				}

				// Draw blurred pixel
				pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
			}
		}

		// Vertical blur
		for( int i = 1; i < Width - 1; i++ )
		{
			for( int j = 1; j < Height - 1; j++ )
			{
				// Clear colour fields
				dwRed = dwGreen = dwBlue = dwAlpha = 0;

				for( int k = 0; k < GaussWidth; k++ )
				{
					INT x = i;
					INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
					DWORD dwColour = pdwPixels[y*Pitch+x];

					DWORD a = ( dwColour >> 24 ) & 0xFF;
					DWORD r = ( dwColour >> 16 ) & 0xFF;
					DWORD g = ( dwColour >> 8 ) & 0xFF;
					DWORD b = ( dwColour >> 0 ) & 0xFF;

					dwAlpha += a * GaussFact[k];
					dwRed   += r * GaussFact[k];
					dwGreen += g * GaussFact[k];
					dwBlue  += b * GaussFact[k];
				}

				dwAlpha /= GaussSum;
				dwRed   /= GaussSum;
				dwGreen /= GaussSum;
				dwBlue  /= GaussSum;

				// Draw blurred pixel
				pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
			}
		}

		// Unlock the texture
		pTempSurface->UnlockRect();
	}
	else
	{
		OutputDebugStringA( "Error locking surface!\n\n" );
		SAFE_RELEASE( pTempSurface );
		return false;
	}

	// Create a dynamic texture to write the blurred surface to.
	if( !ppTexture )
	{
		hr = lpD3DDevice->CreateTexture( Width, Height, 0, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, 
										 D3DPOOL_DEFAULT, ppTexture, NULL );
		if( FAILED( hr ) )
		{
			SAFE_RELEASE( pTempSurface );
			OutputDebugStringA( "Error creating copy texture!\n" );
			return false;
		}
	}

	DWORD* pdwSurface = NULL;
	DWORD* pdwTexture = NULL;
	D3DLOCKED_RECT LockedSurface, LockedTexture;
	bool bError = false;

	// Lock the temporary surface
	if( SUCCEEDED( pTempSurface->LockRect( &LockedSurface, NULL, 0 ) ) )
	{
		// Lock the texture
		if( SUCCEEDED( (*ppTexture)->LockRect( 0, &LockedTexture, NULL, 0 ) ) )
		{
			// Copy the data from the surface to the texture
			pdwSurface = (DWORD*) LockedSurface.pBits;
			pdwTexture = (DWORD*) LockedTexture.pBits;

			::CopyMemory( pdwTexture, pdwSurface, sizeof( pdwSurface ) );

			// Unlock
			(*ppTexture)->UnlockRect(0);
		}
		else
		{
			OutputDebugStringA( "Error locking duplicate texture!\n" );
			bError = true;
		}

		// Unlock
		pTempSurface->UnlockRect();
	}
	else
	{
		OutputDebugStringA( "Error locking temporary surface!\n" );
		bError = true;
	}

	return !bError;
}

The crash occurs when reading the first few pixels for modification. I followed the algorithm exactly (or at least to my best understanding). Is there a better way to do this? Or should I try another approach to the glow effect entirely? Thanks.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Haladria
Is there a reason you're not doing it in a shader?


Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.

Share this post


Link to post
Share on other sites
Quote:
Original post by blueshogun96
Quote:
Original post by Haladria
Is there a reason you're not doing it in a shader?


Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.


No, there is no simple way I know of. You need to render a fullscreen quad with a guassian blur shader to a texture. (this should be separated in two passes for better performance) This is the shader I usually use:
http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/

Share this post


Link to post
Share on other sites
It looks like the reason your code your code is crashing is that you're reading off the edge of the image at:

INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;

In the case where i is 1 and k is 0 that will give you -2. It looks like that code was designed for a GaussWidth of 3, and won't work for any other value without modification.

Share this post


Link to post
Share on other sites
Quote:
Original post by Adam_42
(..) won't work for any other value without modification.


And those modifications include any one of:

* Changing your iteration ranges:

int offset = ( GaussWidth - 1 ) >> 1;
for( int i = offset ; i < Width - offset ; i++ )
{
for( int j = offset ; j < Height - offset ; j++ )



* Not reading outside of the buffer:

DWORD dwColour = x >= 0 && x < Width && y >= 0 && y < Height ? pdwPixels[y*Pitch+x] : 0;



* Clamping your iteration values (the best idea, since you can then iterate over the entire image range):

INT x = std::max(0, std::min(Width, i - ( ( GaussWidth - 1 ) >> 1 ) + k));




INT y = std::max(0, std::min(Height, j - ( ( GaussWidth - 1 ) >> 1 ) + k));

Share this post


Link to post
Share on other sites
Quote:
Original post by Haladria
Quote:
Original post by blueshogun96
Quote:
Original post by Haladria
Is there a reason you're not doing it in a shader?


Because I don't know how (unless there's a simpler way). Besides, I need a fixed-pipeline implementation either way (for OpenGL ES) later on.


No, there is no simple way I know of. You need to render a fullscreen quad with a guassian blur shader to a texture. (this should be separated in two passes for better performance) This is the shader I usually use:
http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/


TBH, your fragment program makes things look much easier. I'll go ahead and try that instead and see if it works. I'm sure I can easily convert it to a HLSL pixel shader. Thanks.

Quote:
Original post by mattd
Quote:
Original post by Adam_42
(..) won't work for any other value without modification.


And those modifications include any one of:

* Changing your iteration ranges:
*** Source Snippet Removed ***

* Not reading outside of the buffer:
*** Source Snippet Removed ***

* Clamping your iteration values (the best idea, since you can then iterate over the entire image range):
*** Source Snippet Removed ***

*** Source Snippet Removed ***


I'll go ahead and try this too just in case, but I'll probably stick with the shader since it appears to be easier to use and maintain. Thanks again.

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!