www.xbdev.net xbdev - software development
Tuesday January 23, 2018
home | about | contact | Donations

Image Warping Effect....
by bkenwright@xbdev.net

Can you believe it!  It took me ages to get this really simple piece of code to work!  But I've made it really simple... you should be able to understand the principle of what it does and how it works. 

 

If you compile and run the code you'll get an animated swirly blurry effect... of course by changing only a few lines you can create any number of effects.... the most common effect I can think of is one of the windows screensavers which has a magnifying glass move around the screen... sort of creating a zoom.. well you could do that using this principle.

The code is relatively simple, its just two files, one for the windows init etc, and the directX code which is in the only other file.

main.cpp dxdraw.cpp

Download Source Code

So how does it work?  Well it works like this, we load a texture in...okay... still with me ;)  Then we create a grid of triangles you can change how many, then higher the number the more detailed the effect is... then we call a random function each time after a small delay and shift them slightly creating a sort of blurring/warping effect.  Again depending on how you change the tu/tv values for each vertice in your array... depends on the effect you generate... for example you could use a bit of trig and have them change randomly but in a circular direction... creating a swirly effect of the image.

Lets have a looksy at the code shall we:

/***************************************************************************/

/*                                                                         */

/* File: dxdraw.cpp                                                        */

/* Author: bkenwright@xbdev.net                                            */

/* Date: 10-11-2002                                                        */

/* Image Warping                                                           */

/*                                                                         */

/***************************************************************************/

 

LPDIRECT3D8 g_pD3D = NULL;

LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;

 

#include <time.h>

 

// Texture

LPDIRECT3DTEXTURE8 pTex;

  

LPDIRECT3DVERTEXBUFFER8 g_pVBGrid;

LPDIRECT3DINDEXBUFFER8  g_pIBGrid;

 

struct my_vertex

{

      FLOAT x, y, z;      // D3DFVF_XYZ

      DWORD colour;       // D3DFVF_DIFFUSE

      FLOAT     tu, tv;   // D3DFVF_TEX1

};

UINT my_vertex_description = ( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 );

 

int VERTEX_GRID_DENSITY = 50;

 

 

void init(HWND hWnd)

{

    //First of all, create the main D3D object. If it is created successfully we

    //should get a pointer to an IDirect3D8 interface.

    g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

    //Get the current display mode

    D3DDISPLAYMODE d3ddm;

    g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

    //Create a structure to hold the settings for our device

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));

    //Fill the structure.

    //We want our program to be windowed, and set the back buffer to a format

    //that matches our current display mode

    d3dpp.Windowed = TRUE;

    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

    d3dpp.BackBufferFormat = d3ddm.Format;

    //For depth buffering (e.g.) the z-buffer

    d3dpp.BackBufferCount=1;

    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    d3dpp.EnableAutoDepthStencil = TRUE;

    //Create a Direct3D device

    g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDevice);

    //Turn off lighting becuase we are specifying that our vertices have colour

    g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

    //Turn on z-buffering

    g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

 

    //Turn on back face culling. This is becuase we want to hide the back of our polygons

    g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); //D3DCULL_CCW);

 

    /*****************Init Image Stuff Here*************************************/

    D3DXCreateTextureFromFile( g_pD3DDevice, "image.bmp", &pTex); 

 

    g_pD3DDevice->CreateVertexBuffer( VERTEX_GRID_DENSITY*VERTEX_GRID_DENSITY*sizeof(my_vertex),

                                                            0, my_vertex_description, D3DPOOL_MANAGED, &g_pVBGrid);

 

    g_pD3DDevice->CreateIndexBuffer( VERTEX_GRID_DENSITY*VERTEX_GRID_DENSITY * 3 * 2 * sizeof(WORD),

                                                            D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,

                                                            D3DPOOL_MANAGED, &g_pIBGrid);

 

    // We set the vertex buffer once here, as we only change the positions of the vertices and not

    // add or delete any of them.

    WORD *pIndices;

    g_pIBGrid->Lock( 0, (VERTEX_GRID_DENSITY)*(VERTEX_GRID_DENSITY)*3 * 2 * sizeof(WORD) , (unsigned char**)&pIndices, 0);

 

    WORD *pIndex = pIndices;

    for(int x=0; x< VERTEX_GRID_DENSITY-1; x++)

    {

        for(int y=0; y< VERTEX_GRID_DENSITY-1; y++)

        {

           // First Triangle.

           *(pIndex++) = (  (y) * VERTEX_GRID_DENSITY) +x;

           *(pIndex++) = (  (y) * VERTEX_GRID_DENSITY) +x+1;

           *(pIndex++) = ((y+1) * VERTEX_GRID_DENSITY) +x+1;

           // Second Triangle.

           *(pIndex++) = ((y)  * VERTEX_GRID_DENSITY) +x;

           *(pIndex++) = ((y+1)* VERTEX_GRID_DENSITY) +x+1;

           *(pIndex++) = ((y+1)* VERTEX_GRID_DENSITY) +x;

        }

    }

    g_pIBGrid->Unlock();

 

    srand( time(NULL) );

}

 

void de_init()

{

    g_pD3DDevice->Release();

    g_pD3DDevice = NULL;

    g_pD3D->Release();

    g_pD3D = NULL;

 

    /*****************De-Init Image Code here on********************************/

 

    pTex->Release();

    g_pVBGrid->Release();

    g_pIBGrid->Release();

}

 

void set_camera()

{

    // [1] D3DTS_VIEW

    D3DXMATRIX v;

    g_pD3DDevice->SetTransform(D3DTS_VIEW, D3DXMatrixLookAtLH(&v, &D3DXVECTOR3(0,0,-2),

                                                                  &D3DXVECTOR3(0,0,0),

                                                                  &D3DXVECTOR3(0,1,0))); 

    // [2] D3DTS_PROJECTION

    D3DXMATRIX p;

    g_pD3DDevice->SetTransform( D3DTS_PROJECTION, D3DXMatrixPerspectiveFovLH( &p, 

                                                                              D3DX_PI/4,  1.0f,

                                                                              1.0f, 1000.0f));

};

 

int RandomNumber(int iMin, int iMax)

{

  if (iMin == iMax) return(iMin);

  return((rand() % (abs(iMax-iMin)+1))+iMin);

}

 

// This adjusts our Vertex Buffer, we do it each time in a loop, and use the Inex buffer to reference it.

void SetupVertexGrid(LPDIRECT3DVERTEXBUFFER8 pVBGrid, int iGridWidth, int iGridHeight)

{

  my_vertex* pV;

 

  pVBGrid->Lock( 0, iGridWidth*iGridHeight*sizeof(my_vertex), (BYTE**)&pV, 0);

 

  for( int x=0; x< iGridWidth; x++)

  {

    for( int y=0; y< iGridHeight; y++)

    {

      //  its between 0 and 1, this makes it to 0 to 2, then subtract 1, so its -1 to 1.

      //                                                                                |

      //  x loops from 0 to iGridWidth-1, so dividing by                                |

      //  (iGridwidth-1) scales our values from 0 to 1.                                 |

      //                                        |                                       |

      //                          ______________|______________                   ______|______

      //                         /                             \                 /             \

      pV[ (y*iGridWidth)+x].x = (((float)x/(float)(iGridWidth-1))                  * 2.0f) - 1.0f;

      pV[ (y*iGridWidth)+x].y = (((float)(iGridHeight-1-y)/(float)(iGridHeight-1)) * 2.0f) - 1.0f;

      //                         \_____________________________________________________________/

      //                                                           |

      //  As above for the x, but we start at iGridHeight-1 and work backwards... else if you just use

      //  y you'll end up with an upside down image.

 

      pV[(y*iGridWidth)+x].z = 1.0f;

 

      pV[ (y*iGridWidth)+x].colour = 0xff00ff00;

      pV[ (y*iGridWidth)+x].tu = (float)x/(float)(iGridWidth-1); // tex coors between 0 and 1.

      pV[ (y*iGridWidth)+x].tv = (float)y/(float)(iGridHeight-1);

 

      // These few lines of code introduces our image distortion.. if you comment them out you'll

      // have the originial image.

      float fRandX = RandomNumber(-75,100)/50.0f;

      float fRandY = RandomNumber(-75,100)/50.0f;

 

      pV[ (y*iGridWidth)+x ].tu += fRandX/(float)(iGridWidth*2);

      pV[ (y*iGridWidth)+x ].tv += fRandY/(float)(iGridHeight*2);      

     }

   }

   pVBGrid->Unlock();

}

 

 

 

void Render()

{

      if(!g_pD3DDevice)return;

 

      set_camera();

 

      // For this tutorial I just added a few lines of code.. not the way it should be

      // done but introduces a small delay.  So the effect is better.

      static float delay=0.0f;

      delay+= 0.08f;

 

      if( delay > 1.0f)

      {

            SetupVertexGrid(g_pVBGrid, VERTEX_GRID_DENSITY, VERTEX_GRID_DENSITY);

            delay = 0.0f;

      };

                       

 

      // Clear the back buffer to a blue color

      g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

      g_pD3DDevice->BeginScene();

 

      g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

 

      g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );

      g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );

      g_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

 

      g_pD3DDevice->SetTexture( 0, pTex );

      g_pD3DDevice->SetStreamSource( 0, g_pVBGrid, sizeof(my_vertex) );

      g_pD3DDevice->SetIndices(g_pIBGrid, 0);

      g_pD3DDevice->SetVertexShader( my_vertex_description );

 

      unsigned int numTriangless = VERTEX_GRID_DENSITY * VERTEX_GRID_DENSITY * 2;

      unsigned int numVertss = VERTEX_GRID_DENSITY * VERTEX_GRID_DENSITY;

 

      g_pD3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0,

                        numVertss, 0, numTriangless    );

 

      g_pD3DDevice->EndScene();

      // After rendering the scene we display it.

      g_pD3DDevice->Present( NULL, NULL, NULL, NULL );

     

}

It looks like a lot, but the first half of it...the init() and de-init() functions are for the init and de-init of the directx code... this usually stays the same.

And the main.cpp file is nothing special just re-dundant code which you learn to type over and over again as you get older.

/***************************************************************************/

/*                                                                         */

/* File: main.cpp                                                          */

/* Author: bkenwright@xbdev.net                                       */

/* Date: 10-11-2002                                                        */

/* Image Warping                                                           */

/*                                                                         */

/***************************************************************************/

 

#include <windows.h>

#include <d3dx8.h>

 

 

WNDCLASS a; HWND hwnd; MSG c;

long _stdcall zzz (HWND, UINT, WPARAM, LPARAM);

 

 

#include "dxdraw.cpp"

 

 

void gameloop()

{

      Render();

}

 

 

int _stdcall WinMain(HINSTANCE i, HINSTANCE j, char *k, int l)

{

      a.lpszClassName="a1";

      a.hInstance = i;

      a.lpfnWndProc = zzz;

      a.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

      RegisterClass(&a);

      hwnd=CreateWindow("a1", "xbdev.net", WS_OVERLAPPEDWINDOW, 30,30,300,300,NULL,NULL,i,NULL);

 

      init(hwnd);                   // Initilize Direct3DX.

      ShowWindow(hwnd,1);

      while(1)

      {

           

            if (PeekMessage(&c, NULL, 0, 0, PM_NOREMOVE))

            {

                  if(!GetMessage(&c, 0,0,0))

                        break;

                  DispatchMessage(&c);

            }

            else

                  gameloop();       // Render Loop.

      }

 

      return 1;

}

 

long _stdcall zzz (HWND w, UINT x, WPARAM y, LPARAM z)

{

      if (x == WM_DESTROY)

      {

            de_init();              // Destroy DirectX3D.

     

            PostQuitMessage(0);

      }

      return DefWindowProc(w,x,y,z);

}

 

Well there you have it... I used an index buffer and a vertex buffer as its more efficient... it shouldn't be that hard to follow.  I do hope you understand what is in effect happening as its a really cool effect that is easy to generate.

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.