Problems to get the Water Rippling

Started by
5 comments, last by TheAdmiral 17 years, 8 months ago
I render my scene, then I render my scene again to texture(1024x1024) with y-flipped camera. That's ok, reflection is correct. I also know how to map that texture to mesh. But, deforming the mesh won't deform the texture :( Also my rippling code is all messed up, it won't work good enough. Class:

class DX9_Water
{
	public:
		Vertex *vertices;
		DWORD *indices;
		DWORD dwNumVertices, dwNumIndices, dwNumGridsAcross;
		LPDIRECT3DSURFACE9 surface, stencil;
		LPDIRECT3DTEXTURE9 lpReflectionTexture;
		D3DSURFACE_DESC desc;
		LPDIRECT3DTEXTURE9 psBumpMap;

		Plane clip_plane;
		Matrix matReflect, matRemap;
		Material material;
		float fSeaLevel, fA, fBlurryFactor;
		float *fVerticeLevels; // Height of vertices

		void Init(Vector vMin, Vector vMax, float fSeaLevel, float fBlurryLevel, DWORD dwNumGridsAcross, float fA, float fR, float fG, float fB);
		void Render(DX9_Texture *tex);
		void BuildReflectionTexture(void (*pRenderItems)(), DWORD color);
		void Update(float fSpeed, float fWaveDepth);
		DX9_Water();
		virtual ~DX9_Water();
};

Init:

void DX9_Water::Init(Vector vMin, Vector vMax, float fSeaLevel, float fBlurryFactor, DWORD dwNumGridsAcross, float fA, float fR, float fG, float fB)
{
	this ->dwNumGridsAcross = dwNumGridsAcross;
	this ->fSeaLevel = fSeaLevel;
	this ->fA = fA;
	this ->fBlurryFactor = fBlurryFactor;
	// Calculate x and z sizes
	float fXSize = vMax.x - vMin.x;
	float fZSize = vMax.z - vMin.z;

	// Calculate sizes in world units

	// Movement across the map is calculated as this
	float fMovWorldDeltaX = (float)(fXSize / (float)dwNumGridsAcross);
	float fMovWorldDeltaZ = (float)(fZSize / (float)dwNumGridsAcross);

	// Calculate the number of vertices
	dwNumVertices = (dwNumGridsAcross + 1) * (dwNumGridsAcross + 1);
	// Calculate the number of indices
	dwNumIndices = (dwNumGridsAcross * dwNumGridsAcross) * 6;

	// Create the arrays
	vertices = new Vertex[dwNumVertices];
	memset(vertices, 0, sizeof(Vertex) * dwNumVertices);
	indices = new DWORD[dwNumIndices];
	memset(indices, 0, sizeof(DWORD) * dwNumIndices);

	// Loop trough the whole map
	float fWorldX = vMin.x, fWorldZ = vMin.z;
	int iV = 0;

	for(DWORD y = 0 ; y <=  dwNumGridsAcross  ; y++)
	{
		for(DWORD x = 0 ; x <=  dwNumGridsAcross ; x++)
		{
			// The most important thing is to calculate 
			// U and V as the functions of sin
			float U = (float)x / (float)dwNumGridsAcross;
			float V = (float)y / (float)dwNumGridsAcross;

			vertices[iV] = Vertex(Vector(fWorldX, 0, fWorldZ), Vector(0,1,0), U*32, V*32);

			// Next vertices
			iV++;
				
			// Go on
			fWorldX += fMovWorldDeltaX;
		}

		// Go on
		fWorldX = vMin.x;
		fWorldZ += fMovWorldDeltaZ;
	}

	// Create index table
	DWORD dwI = 0;
	DWORD dwG = 0;

	// Calculate index table
	for(y = 0 ; y <= dwNumGridsAcross - 1 ; y++)
	{
		for(DWORD x = 0 ; x <= dwNumGridsAcross - 1 ; x++)
		{
			indices[dwI+0] =  dwG + dwNumGridsAcross + 1;
			indices[dwI+1] =  dwG + 1;
			indices[dwI+2] =  dwG;			

			indices[dwI+3] =  dwG + dwNumGridsAcross + 2;
			indices[dwI+4] =  dwG + 1;
			indices[dwI+5] =  dwG + dwNumGridsAcross + 1;

			dwI += 6;
			dwG++;
		}
		
		dwG++;
	}

	// Calculate the normals
	int _h = dwNumGridsAcross - 1;

	for(int i = 0; i < _h; i++)
	for(int j = 0; j < _h; j++)
	{
		Vector p1, p2, p3, v1, v2,tmp;
		int v[4];

		v[0] = i * dwNumGridsAcross + j;	
		v[1] = i * dwNumGridsAcross + j + 1;
		v[2] = (i + 1) * dwNumGridsAcross + j;	
		v[3] = (i + 1) * dwNumGridsAcross + j + 1;

		 // Store the positions of first triangle into 3 usable vertices
		p1 = Vector(vertices[v[0]].x,vertices[v[0]].y,vertices[v[0]].z);
		p2 = Vector(vertices[v[3]].x,vertices[v[3]].y,vertices[v[3]].z);
		p3 = Vector(vertices[v[2]].x,vertices[v[2]].y,vertices[v[2]].z);

		// Calculate the triangle sides
		v1 = p2 - p1;
		v2 = p3 - p1;

		// Calculate normal
		D3DXVec3Cross(&tmp, &v1, &v2);

		// add normal to the normal of all the vertices in the triangle
		vertices[v[0]].nx += tmp.x; vertices[v[0]].ny += tmp.y; vertices[v[0]].nz += tmp.z;
		vertices[v[3]].nx += tmp.x; vertices[v[3]].ny += tmp.y; vertices[v[3]].nz += tmp.z;
		vertices[v[2]].nx += tmp.x; vertices[v[2]].ny += tmp.y; vertices[v[2]].nz += tmp.z;
 
		// store the positions of second triangle into 3 usable vertices
		p1 = Vector(vertices[v[1]].x,vertices[v[1]].y,vertices[v[1]].z);
		p2 = Vector(vertices[v[3]].x,vertices[v[3]].y,vertices[v[3]].z);
		p3 = Vector(vertices[v[0]].x,vertices[v[0]].y,vertices[v[0]].z);

		// Calculate the triangle sides
		v1 = p2 - p1;
		v2 = p3 - p1;

		// Calculate normal
		D3DXVec3Cross(&tmp, &v1, &v2);
		// add normal to the normal of all the vertices in the triangle
		vertices[v[1]].nx += tmp.x; vertices[v[1]].ny += tmp.y; vertices[v[1]].nz += tmp.z;
		vertices[v[3]].nx += tmp.x; vertices[v[3]].ny += tmp.y; vertices[v[3]].nz += tmp.z;
		vertices[v[0]].nx += tmp.x; vertices[v[0]].ny += tmp.y; vertices[v[0]].nz += tmp.z;
	}

	// normalize all vertices
	for(DWORD d = 0; d < dwNumVertices; d++)
	{
		Vector normal;
		Vector normal_n(vertices[d].nx, vertices[d].ny, vertices[d].nz);
		D3DXVec3Normalize(&normal, &normal_n);
		
		vertices[d].nx = -normal.x;
		vertices[d].ny = -normal.y;
		vertices[d].nz = -normal.z;
	}

	D3DXCreateTexture(GFX.lpDevice, 1024, 1024, 1, D3DUSAGE_RENDERTARGET, 
		GFX.best_texture_format, D3DPOOL_DEFAULT, &lpReflectionTexture);

	// Create a new rendertarget
	lpReflectionTexture -> GetSurfaceLevel(0, &surface);
	surface -> GetDesc(&desc);

	// Create stencil
	GFX.lpDevice->CreateDepthStencilSurface(desc.Width, desc.Height, GFX.d3d_modes[GFX.dwCurMode].formatStencil,D3DMULTISAMPLE_NONE,0,0,&stencil,0);

	// Create vertice level array
	fVerticeLevels = new float[dwNumVertices];
	for(DWORD x = 0 ; x < dwNumVertices ; x++)
	{
		fVerticeLevels[x] = ((rand()%360) / 360.0f) * DOUB_PII;
	}

	// Reflect matrix
    Vector vPoint(0, fSeaLevel,0);
	Vector vNormal(0,1,0);
    D3DXPlaneFromPointNormal(&clip_plane, &vPoint, &vNormal );
    D3DXMatrixReflect(&matReflect, &clip_plane );

	// Material
	memset(&material, 0, sizeof(Material));
	material.Diffuse.r = 1;
	material.Diffuse.g = 1;
	material.Diffuse.b = 1;
	material.Diffuse.a = fA;
	
	// Remap matrix
	ZeroMemory(&matRemap,sizeof(Matrix));
	// Calculate mirror transformation matrix
	float fy = 1.0f / tanf(GFX.fCurFOV * 0.5f);

	matRemap._11 =     fy * 0.5f;     // xscale 
	matRemap._22 =  -matRemap._11;    // yscale  
	matRemap._31 =           0.5f;    // xpos 
	matRemap._32 =           0.5f;    // ypos  
	matRemap._33 =           1.0f;
	matRemap._44 =           1.0f;
}

BuildReflectionTexture:

void DX9_Water::BuildReflectionTexture(void(*pRenderItems)(void), DWORD color)
{
	// Save the view and proj matrix
    Matrix matViewSaved, matNewView;
    GFX.lpDevice -> GetTransform( D3DTS_VIEW, &matViewSaved );

    // Reflect camera in X-Z plane mirror
	D3DXMatrixMultiply(&matNewView, &matReflect, &matViewSaved );
	GFX.lpDevice -> SetTransform( D3DTS_VIEW, &matNewView );

    // Set a clip plane, so that only objects above the plane are reflected
    GFX.lpDevice -> SetClipPlane(0, clip_plane);
    GFX.lpDevice -> SetRenderState( D3DRS_CLIPPLANEENABLE, 0x01 );
	GFX.lpDevice -> SetRenderState( D3DRS_CULLMODE,     D3DCULL_CW );

	GFX.lpDevice->SetRenderTarget(0,surface);
	GFX.lpDevice->SetDepthStencilSurface(stencil);

    // Clear the target & zbuffer  & stencil
	GFX.lpDevice -> Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, color, 1, 0);
	GFX.lpDevice -> Clear(0,0,D3DCLEAR_STENCIL, 0, 0, 0);
	
    // Render the scene
    pRenderItems();

	GFX.lpDevice -> SetRenderTarget(0,GFX.back_buffer);
	GFX.lpDevice -> SetDepthStencilSurface(GFX.stencil_buffer);

    // Restore render states
	GFX.lpDevice -> SetRenderState( D3DRS_CULLMODE,         D3DCULL_CCW );
    GFX.lpDevice -> SetRenderState( D3DRS_CLIPPLANEENABLE,  0x00 );
    GFX.lpDevice -> SetTransform( D3DTS_VIEW, &matViewSaved );

}

Render:

void DX9_Water::Render(DX9_Texture *tex)
{
	// MATERIAL
	GFX.lpDevice -> SetMaterial(&material);

	// REFLECTION
	GFX.lpDevice -> SetTexture(1, lpReflectionTexture);
	GFX.lpDevice -> SetTextureStageState(1,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACEPOSITION);
	GFX.lpDevice -> SetTextureStageState(1,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT3 | D3DTTFF_PROJECTED);
    GFX.lpDevice -> SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    GFX.lpDevice -> SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );

	// REMAP
	GFX.lpDevice -> SetTransform(D3DTS_TEXTURE1, &matRemap);

	// BUMPWAVES
	tex->Use(0);
	GFX.lpDevice -> SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,0);
 
	// Left right
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVMAT00,   F2DW(0));
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVMAT10,   F2DW(0));
	// Up down
	GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVMAT01,   F2DW(0.25f));
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVMAT11,   F2DW(-0.20f));
	GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVLSCALE,  F2DW((float)GFX.d3d_modes[GFX.dwCurMode].dwHeight / 1024));
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_BUMPENVLOFFSET, F2DW(0.20f) );
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_BUMPENVMAP);

	GFX.lpDevice -> SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
	GFX.lpDevice -> SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);

	GFX.lpDevice -> SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    GFX.lpDevice -> SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );

	// NO CULL
	GFX.lpDevice -> SetRenderState( D3DRS_CULLMODE,         D3DCULL_NONE );

	// TRANSPARENCY
    GFX.lpDevice -> SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
	GFX.lpDevice -> SetRenderState(D3DRS_LIGHTING, 1);
	GFX.lpDevice -> SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	GFX.lpDevice -> SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	// RENDER
	GFX.lpDevice -> SetFVF(VertexFVF);
	GFX.lpDevice -> DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, dwNumVertices, dwNumIndices / 3,	indices,D3DFMT_INDEX32, vertices, sizeof(Vertex));	

	GFX.lpDevice -> SetRenderState( D3DRS_CULLMODE,         D3DCULL_CCW );

	GFX.lpDevice -> SetTextureStageState(0,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_DISABLE);
	GFX.lpDevice -> SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,0);
	GFX.lpDevice -> SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE);
	GFX.lpDevice -> SetTextureStageState( 2, D3DTSS_COLOROP,   D3DTOP_DISABLE );
}

Please, I've provided you good start. Help me to get my water rippling like in FarCry for example. Also, feel free to criticize and comment my code. :< Tired
Advertisement
You need more than the cheap reflection you are doing now. Render the scene to a cube map and draw the water with a pixel shader that does cube map lookups based on the reflected ray from the water at each pixel. Modify the water normal at each pixel (rippels) and you have your deformed reflection.
"C lets you shoot yourself in the foot rather easily. C++ allows you to reuse the bullet!"
Thanks vNistelrooy but isn't it a real FPS eater to render
the scene 7 times per frame ?

Once for the real, 6 times for the cube map.

Isn't there no other way to get texture rippling?
You don't need all the 8 sides, 7 would be enough. EDIT: Oooops, there are 6 sides to cube map. You only need 5... [smile]
Render to low resolution cube map.
Render with fov set to 180 degrees so you can render the cube map in 2 passes. (Tricky)
"C lets you shoot yourself in the foot rather easily. C++ allows you to reuse the bullet!"
Quote:Original post by Anonymous Poster
Thanks vNistelrooy but isn't it a real FPS eater to render
the scene 7 times per frame ?

Once for the real, 6 times for the cube map.

Isn't there no other way to get texture rippling?


A nice way to get ripples is to create a bump map with a couple of sine wave functions and alter the normals with it. You are going to animate the waves of course, you could do it interactively with a shader.
Quote:Original post by Xmon
Quote:Original post by Anonymous Poster
Thanks vNistelrooy but isn't it a real FPS eater to render
the scene 7 times per frame ?

Once for the real, 6 times for the cube map.

Isn't there no other way to get texture rippling?


A nice way to get ripples is to create a bump map whit a coulpe of sine wave functions and alter the normals whit it.


So? This is a way to generate the normals, but what about reflections/refractions?
"C lets you shoot yourself in the foot rather easily. C++ allows you to reuse the bullet!"
You'll probably want to write a simple shader to get the rippling effect you speak of. If your ripples are very small (or your water mesh has a high resolution) you can get away with doing the work in the vertex shader. Otherwise, your fillrate should be happy enough with the work being done in the shader.

If you want accurate reflections, you'll need to perform some elementary trig (or HLSL - reflect) to reflect the viewing ray about the normal. The resulting vector can be used directly as the lookup on a cube-map, if you choose that method, or can still be used with your existing reflection if you're careful.

A really cheap way to get the reflections from your newly perturbed normals is to simply add the horizontal components of the (normalised) normal to their respective texture coordinates before performing a projective texture lookup. This is a one-liner in the pixel shader. However, this method uses the simplification 'cos(x) ~ 1', so you'll need to keep the wave amplitudes small compared to their spatial frequency, or things will start to look really funky.

Something the bear in mind (since you're reluctant to use a cube map) is that these texture coordinate perturbations will cause nasty artifacts on the 'shoreline' if you cull away underwater geometry (which you'll probably need to do) as the normal perturbation pushes the texture coordinates past the land-sky boundary. The result is shimmering streaks of reflected sky at the land-water boundary (where it shouldn't be), which only get worse as the waves get rougher. You can combat this problem by bringing your clipping plane below water-level, but this comes at the cost of introducing new artifacts (ghost reflections as underwater geometry finds itself reflecting on the water surface). Proceed carefully.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.

This topic is closed to new replies.

Advertisement