# I've developed water, how do i increase framerate

This topic is 4514 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi all, i'm currently having my final year project, writting it in C++ and DirectX and i need some help to get better waves. Currently the waves i'm having is to distort the vertices randomly and then smoothen it. It works for the moment but there are afew problems. 1) Waves are jerky 2) Takes up too much framerate with the intensive calculations. 3) Unable to generate a large piece of terrain due to the intense calculations. Solution: 1) Implementing the formula posted at gamedev tutorials 2) Using Perlin noise I have problem implementing the formula at gamedev and noise, please take a look thank you. The formula posted at gamedev was fine when i tried to render the waves once, but if i was rendering it every second, the waves will appear to fly out of the screen. As for perlin noise, the formula stated does not seem to be able to match the way i render my triangles.
#include "StdAfx.h"
#include "terrain.h"
#include &lt;time.h&gt;

// FVF
DWORD		TERRAIN_FVF = ( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 );

Terrain::Terrain()
{
// number of grid elements.. pass in as
m_Width		=256;
m_Height	= 256;

// size of vertex array
m_VertexC	= m_Width*m_Height;	// so a 129x129 grid has 16641 vertices

// number of indices: number of triangles * 3
// so a 129x129 grid has 32768 triangles, or 98304 indices
m_IndexC	= (m_Width-1)*(m_Height-1) * 2 * 3;

// allocate space for arrays
m_VertexA	= new TerrainVertex[m_VertexC];
m_IndexA	= new unsigned short[m_IndexC];

// clear points
m_pVB = NULL;
m_pIB = NULL;

m_pBaseTexture= NULL;
}

Terrain::~Terrain()
{
// final cleanup
delete [] m_VertexA;
delete [] m_IndexA;
}

HRESULT Terrain::Render( LPDIRECT3DDEVICE9 pd3dDevice )
{
// visualize the grid
pd3dDevice-&gt;SetFVF(TERRAIN_FVF);

// disable lighting
pd3dDevice-&gt;SetRenderState( D3DRS_LIGHTING,            FALSE);

pd3dDevice-&gt;SetTexture(0, m_pBaseTexture);

pd3dDevice-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE, true);
pd3dDevice-&gt;SetRenderState(D3DRS_ZWRITEENABLE, false);

pd3dDevice-&gt;SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pd3dDevice-&gt;SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

pd3dDevice-&gt;SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
pd3dDevice-&gt;SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
pd3dDevice-&gt;SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

pd3dDevice-&gt;SetIndices(  m_pIB );
pd3dDevice-&gt;SetStreamSource(  0,                    // Stream ID
m_pVB,				// vertex buffer
0,					// first vertex = 0
sizeof(TerrainVertex));// size per vertex
HRESULT hRes =
pd3dDevice-&gt;DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,			// primitive type
0,							// offset to add to the index array
0, m_VertexC,				// range of vertices used [0..all]
0,							// first index
m_IndexC / 3);				// number of triangles.

pd3dDevice-&gt;SetRenderState(D3DRS_ZWRITEENABLE, true);
pd3dDevice-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE, false);

return S_OK;	// finish rendering
}

// return a random value between Low and High
float			GetRandom(float Low, float High)
{
float RandomValue = rand() / (float)RAND_MAX;
return Low + (RandomValue * (High-Low));
}

double			Terrain::GetHeight(double	x, double y)
{
if (x &lt; 0) x = m_Width - x;
if (y &lt; 0) y = m_Height - y;
int Index = x + (y*m_Width);
return m_VertexA[Index].y;
}

void			Terrain::SetHeight(int	x, int y, float Height)
{
if (x &lt; 0) x = m_Width - x;
if (y &lt; 0) y = m_Height - y;
int Index = x + (y*m_Width);
m_VertexA[Index].y = Height;
}

// calculate the index into the vertex buffer from the x and y position
int				Terrain::GetIndex(int x, int y)
{
return x + (y * m_Width);
}

void			Terrain::GenerateRecursion(int x1, int y1, int x2, int y2, float Height, float Roughness )
{
if (x2 - x1 &lt; 2) return;	// end of recursion

// sum of heights around us
float	BaseHeight =   (GetHeight(x1, y1) +
GetHeight(x1, y2) +
GetHeight(x2, y1) +
GetHeight(x2, y2) ) / 4.0f;
// center of the current area
int	xp = x1 + ((x2-x1)/2);
int	yp = y1 + ((y2-y1)/2);

// fill up a cross-shaped slice of landscape
SetHeight(xp, y1, GetRandom(BaseHeight-Height, BaseHeight+Height));
SetHeight(xp, yp, GetRandom(BaseHeight-Height, BaseHeight+Height));
SetHeight(xp, y2, GetRandom(BaseHeight-Height, BaseHeight+Height));
SetHeight(x1, yp, GetRandom(BaseHeight-Height, BaseHeight+Height));

// recursion
GenerateRecursion(x1, y1, xp, yp, Height*Roughness, Roughness);	// top left
GenerateRecursion(xp, y1, x2, yp, Height*Roughness, Roughness); // top right
GenerateRecursion(x1, yp, xp, y2, Height*Roughness, Roughness);	// bottom left
GenerateRecursion(xp, yp, x2, y2, Height*Roughness, Roughness); // bottom right
}

void			Terrain::GenerateTerrain(float	Height, float Roughness )
{
// initialize the random number generator.
// remember to #include &lt;time.h&gt;, otherwise this won't compile
srand( (unsigned)time( NULL ) );

// seed the corners
SetHeight(0, 0, GetRandom(0, Height));
SetHeight(m_Width-1, 0, GetRandom(0, Height));
SetHeight(0, m_Height-1, GetRandom(0, Height));
SetHeight(m_Width-1, m_Height-1, GetRandom(0, Height));

// seed the cross of the center
int	dx = m_Width /2;
int	dy = m_Height /2;
SetHeight(0, 0, GetRandom(0, Height));
SetHeight(dx, dy, GetRandom(0, Height));
SetHeight(dx, m_Height-1, GetRandom(0, Height));
SetHeight(0, dy, GetRandom(0, Height));
SetHeight(m_Width-1, dy, GetRandom(0, Height));

// start the recursion
GenerateRecursion(0, 0, m_Width-1, m_Height-1, Height, Roughness);
};

HRESULT Terrain::Create( LPDIRECT3DDEVICE9 pd3dDevice )
{
// initialize grid
const float	W = 1.0f;
const float	H = 1.0f;
int	Index = 0;
for (int x=0; x&lt;m_Width; x++)
for (int z=0; z&lt;m_Height; z++)
{
// setup the position
m_VertexA[Index].x = W * x;
m_VertexA[Index].y = 0;
m_VertexA[Index].z = H * z;

m_VertexA[Index].nx = 0.0f;
m_VertexA[Index].ny = 1.0f;
m_VertexA[Index].nz = 0.0f;

m_VertexA[Index].u = (float) x / (float) m_Width;
m_VertexA[Index].v = (float) z / (float) m_Height;

// let the color move across the grid
m_VertexA[Index].Color = D3DCOLOR_ARGB(255, 0, x, z);

// increment, so we're dealing with the next vertex
Index++;
}

// reset the index again!
Index = 0;
// for each vertex, except the last one on each row
for (int x=0; x&lt;m_Width-1; x++)
for (int z=0; z&lt;m_Height-1; z++)
{
// first triangle in this square
m_IndexA[Index++] = GetIndex(x, z);
m_IndexA[Index++] = GetIndex(x+1, z);
m_IndexA[Index++] = GetIndex(x+1, z+1);

// second triangle in this square
m_IndexA[Index++] = GetIndex(x, z);
m_IndexA[Index++] = GetIndex(x+1, z+1);
m_IndexA[Index++] = GetIndex(x, z+1);
}
// after we've initialized the rest of the terrain, generate here
// 40: the max height.. change this to make bigger mountains
// 0.2f: the roughness. Change this to make smoother or rockier terrain
//GenerateTerrain(40, 0.4f);
D3DXCreateTextureFromFileA(pd3dDevice, "water_texture.png", &m_pBaseTexture);

return S_OK;	// finish creation
}

HRESULT Terrain::RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
{
// create index buffer
unsigned int	IndexSize = m_IndexC * sizeof(unsigned short);
HRESULT	hRes = 	pd3dDevice-&gt;CreateIndexBuffer(IndexSize,   // size of buffer
D3DUSAGE_WRITEONLY, // usage
D3DFMT_INDEX16,     // Unsigned short
D3DPOOL_MANAGED,    // optimal pool for static
&m_pIB,             // pointer to VB
NULL );             // reserved
if (FAILED(hRes)) 		return E_FAIL;					   // creation failed

unsigned short*	pIndices = NULL;	// pointer to data
hRes = m_pIB-&gt;Lock(0, IndexSize, (void**)&pIndices, 0 );
if (SUCCEEDED(hRes))
{
memcpy(pIndices, m_IndexA, IndexSize);
// write to buffer
m_pIB-&gt;Unlock();
}

// create vertex buffer
unsigned int	VertexSize = sizeof(TerrainVertex) * m_VertexC;
hRes = 	pd3dDevice-&gt;CreateVertexBuffer( VertexSize,          // size of buffer
D3DUSAGE_WRITEONLY, // usage
TERRAIN_FVF,        // Same as the FVF we use to render
D3DPOOL_MANAGED,    // optimal pool
&m_pVB,             // pointer to VB
NULL );             // NULL
if (FAILED(hRes)) 		return E_FAIL;					   // creation failed
TerrainVertex*	pVertices = NULL;	// pointer to data
hRes = m_pVB-&gt;Lock(0, VertexSize, (void**)&pVertices, 0 );
if (SUCCEEDED(hRes))
{
memcpy(pVertices, m_VertexA, VertexSize);
// write to buffer
m_pVB-&gt;Unlock();
}

}

HRESULT Terrain::InvalidateDeviceObjects()
{
if (m_pVB)	m_pVB-&gt;Release();
if (m_pIB)	m_pIB-&gt;Release();
if(m_pBaseTexture) m_pBaseTexture-&gt;Release();

return S_OK;	// release any video-memory assets
}

HRESULT Terrain::Destroy()
{
return S_OK;	// final destruction.. no comming back from this
}

HRESULT Terrain::Waves(LPDIRECT3DDEVICE9 pd3dDevice, double deltaTime){//Joshua This is the formula i got. Tested the variables
//	and it came out alright with damping.
// create vertex buffer
if(deltaTime&gt;100){
unsigned int	VertexSize = sizeof(TerrainVertex) * m_VertexC;
TerrainVertex*	pVertices = NULL;	// pointer to data
HRESULT hRes = m_pVB-&gt;Lock(0, VertexSize, (void**)&pVertices, 0 );
if (SUCCEEDED(hRes))
{
GenerateTerrain(1.0f, 0.8f);

memcpy(pVertices, m_VertexA, VertexSize);
// write to buffer
m_pVB-&gt;Unlock();
}

return S_OK;
}
}


[Edited by - Lonerboi on July 9, 2006 2:51:25 AM]

##### Share on other sites

If you are going for speed, you will want to move all the calculations into the GPU with a vertex shader. Not the easiest thing, g'luck if you try.

The randomness is probably fine for static terrain, but waves wouldn't be able to "flow" if they are random. Try having your waves approximate a sine wave. Just get the distance from the center (or an edge or any fixed position), then plug that value into a sine function. Also, if you scale that distance by time, you will get smoothly flowing waves.

Cheers.

##### Share on other sites
Hmmm sorry but i don't really get you.

Do you mean i get a position, plug it into a sin curve?

xPos = x*sin*deltaTime;
yPos = y*sin*deltaTime;

But this will only give me the sin curve that this coordinate. Or do i have to do this and my GenerateRecursion?

##### Share on other sites
I am not a moderator on this forum but I'd recommand using [ source ] tags, you have more chances to get feedback if you have nice syntax-colored code in a fixed-width font!

##### Share on other sites
Just a comment: most water rendering systems these days dont move geometry. Instead they tend to do bump mapped waves which are animated..

##### Share on other sites
Quote:
 Original post by Matt AufderheideJust a comment: most water rendering systems these days dont move geometry. Instead they tend to do bump mapped waves which are animated..

Actually, there are basically two forms of technique: regular rippling water (which you've just described) and ocean water (which does move the geometry)

##### Share on other sites
Yes you are right.

I was just pointing out that the majority of recent games I'm aware of go with the bump mapped waves, such as FarCry, Oblivion, etc..

##### Share on other sites
Thank you for your suggestion about using the [ source ] tags.

I need to move the geo due to camera perspective and the interaction between the waves and my object.

I really wish to implement the formula given at gamedev or perlin noise but i do not have that advance knowledge yet.

ResultMap[x, y] := (( CurrentSourceMap[x+1, y] +
CurrentSourceMap[x-1, y] +
CurrentSourceMap[x, y+1] +
CurrentSourceMap[x, y-1] ) DIV 2 ) -
PreviousResultMap[x, y]

Is it possible for me to plug this into the GenerateRecursion or GenerateTerrain function? Or do i do away with both? As my coordinates are not stored in arrays, do i put them into arrays or can i do this method twice, once for X, once for Y?

[Edited by - Lonerboi on July 9, 2006 3:57:29 PM]

1. 1
Rutin
37
2. 2
3. 3
4. 4
5. 5

• 12
• 14
• 9
• 9
• ### Forum Statistics

• Total Topics
633346
• Total Posts
3011451
• ### Who's Online (See full list)

There are no registered users currently online

×