Jump to content
  • Advertisement
Sign in to follow this  
Lonerboi

I've developed water, how do i increase framerate

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

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 <time.h>

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


Terrain::Terrain()
{
	// number of grid elements.. pass in as
	// parameters instead?
	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->SetVertexShader(NULL);
	pd3dDevice->SetPixelShader(NULL);
	pd3dDevice->SetFVF(TERRAIN_FVF);

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

	pd3dDevice->SetTexture(0, m_pBaseTexture);

	pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);

	pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

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

	pd3dDevice->SetIndices(  m_pIB );
	pd3dDevice->SetStreamSource(  0,                    // Stream ID
		m_pVB,				// vertex buffer
		0,					// first vertex = 0
		sizeof(TerrainVertex));// size per vertex
	HRESULT hRes = 
		pd3dDevice->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->SetRenderState(D3DRS_ZWRITEENABLE, true);
	pd3dDevice->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 < 0) x = m_Width - x;
	if (y < 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 < 0) x = m_Width - x;
	if (y < 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 < 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 <time.h>, 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<m_Width; x++)	
		for (int z=0; z<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<m_Width-1; x++)	
			for (int z=0; z<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->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

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

	// create vertex buffer
	unsigned int	VertexSize = sizeof(TerrainVertex) * m_VertexC;
	hRes = 	pd3dDevice->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->Lock(0, VertexSize, (void**)&pVertices, 0 );
	if (SUCCEEDED(hRes))    
	{
		// load data
		memcpy(pVertices, m_VertexA, VertexSize);	
		// write to buffer
		m_pVB->Unlock();	
	}


	return S_OK;	// finish uploading assets to video memory
}

HRESULT Terrain::InvalidateDeviceObjects()
{
	if (m_pVB)	m_pVB->Release();
	if (m_pIB)	m_pIB->Release();
	if(m_pBaseTexture) m_pBaseTexture->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>100){
	unsigned int	VertexSize = sizeof(TerrainVertex) * m_VertexC;
	TerrainVertex*	pVertices = NULL;	// pointer to data
	HRESULT hRes = m_pVB->Lock(0, VertexSize, (void**)&pVertices, 0 );
	if (SUCCEEDED(hRes))    
	{	
		GenerateTerrain(1.0f, 0.8f);

		// load data
		memcpy(pVertices, m_VertexA, VertexSize);	
		// write to buffer
		m_pVB->Unlock();	
	}

	return S_OK;
}
}

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

Share this post


Link to post
Share on other sites
Advertisement
I admit I didn't read most of the code... but

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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by Matt Aufderheide
Just 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 this post


Link to post
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]

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!