Skip to main content
gamedev.net
🔒 Locked 🎮 Unity

projective texturing for water

Started by Khaos Dragon Aug 7, 2004 at 11:12 PM 9 replies 2.9k views
Original Post
Khaos Dragon
Khaos Dragon
Edit: The problem has been changed. Originally I needed help on projective texturing but the problem quickly turned into problems with tearing. I am now trying to implement oblique frustum culling. Since then I've gotten some advice and using it, I have added the following functions...However now I just get a clear or very mangled screen. Highlighted in orange is where I call setupclipplane and ModifyProjectionMatrix.

void setupclipplane()
{
	float modelview[16];
	glGetFloatv(GL_MODELVIEW_MATRIX , modelview);
	//transforming point{A,B,C,D} to camera space point {pX,pY,pZ,pW}
	float A = 1, B = 0, C = 1, D = 0;
	float pX = A*modelview[0] +  B*modelview[1] +  C*modelview[2] +  D*modelview[3];
	float pY = A*modelview[4] +  B*modelview[5] +  C*modelview[6] +  D*modelview[7];
	float pZ = A*modelview[8] +  B*modelview[9] +  C*modelview[10] + D*modelview[11];
	float pW = A*modelview[12] + B*modelview[13] + C*modelview[14] + D*modelview[15];
	//transforming waterplane normal {A, B, C, D} to camera space normal {nX, nY, nZ, nW } 
	A = 0, B = 1, C = 0, D = 0;
	float nX = A*modelview[0] + B*modelview[1] + C*modelview[2] + D*modelview[3];
	float nY = A*modelview[4] + B*modelview[5] + C*modelview[6] + D*modelview[7];
	float nZ = A*modelview[8] + B*modelview[9] + C*modelview[10] + D*modelview[11];
	float nW = A*modelview[12] + B*modelview[13] + C*modelview[14] + D*modelview[15];
	//computing dotproduct between camera space normal and camera space point
	plane.x = nX;
	plane.y = nY;
	plane.z = nZ;
	plane.w = -(pX*nX + pY*nY + pZ*nZ + pW*nW);
}

void ModifyProjectionMatrix(const Vector4D& clipPlane)
{
	float		matrix[16];
	Vector4D	q;

	// Grab the current projection matrix from OpenGL
	glGetFloatv(GL_PROJECTION_MATRIX, matrix);
	
	// Calculate the clip-space corner point opposite the clipping plane
	// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
	// transform it into camera space by multiplying it
	// by the inverse of the projection matrix
	
	q.x = (sgn(clipPlane.x) + matrix[8]) / matrix[0];
	q.y = (sgn(clipPlane.y) + matrix[9]) / matrix[5];
	q.z = -1.0F;
	q.w = (1.0F + matrix[10]) / matrix[14];
	
	// Calculate the scaled plane vector
	Vector4D c = clipPlane * (2.0F / (clipPlane * q));
	
	// Replace the third row of the projection matrix
	matrix[2] = c.x;
	matrix[6] = c.y;
	matrix[10] = c.z + 1.0F;
	matrix[14] = c.w;

	// Load it back into OpenGL
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(matrix);
}

inline float sgn(float a)
{
	if (a > 0.0F) return (1.0F);
	if (a < 0.0F) return (-1.0F);
	return (0.0F);
}

struct Vector4D
{
	float	x, y, z, w;
	
	Vector4D() {}
	
	Vector4D(float a, float b, float c, float d)
	{
		x = a; y = b; z = c; w = d;
	}

	// Scalar product
	Vector4D operator *(float s) const
	{
		return (Vector4D(x * s, y * s, z * s, w * s));
	}

	// Dot product
	float operator *(const Vector4D& v) const
	{
		return (x * v.x + y * v.y + z * v.z + w * v.w);
	}
} plane;






<font color=purple>/////////////////////
//RENDER TO TEXTURE//
/////////////////////</font><font color=green>
//set clipping at the water plane</font>
double water_plane[] = {0,-1, 0, 0};
glClipPlane(GL_CLIP_PLANE0, water_plane);
glEnable(GL_CLIP_PLANE0);					
<font color=green>//clear buffers and flip modelview matrix about water plane
//also setup a viewport equal to the dimensions of the reflection texture</font>
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT  );
glLoadIdentity();	
gluLookAt( cam_x, cam_y , cam_z,  eye_x, eye_y, eye_z,  0, 1 ,0);
glViewport(0, 0, 512, 512);
glPushMatrix();
glScalef(1,-1,1);
<font color=green>//draw the scene as normal minus the water of course
//also return to the original modelview matrix</font>
draw_scene();
glPopMatrix();
<font color=green>//copy color buffer into texture</font>
glBindTexture(GL_TEXTURE_2D, reflection_texture );
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 512, 512, 0);
<font color=purple>//////////////////////////////
//RENDER SCENE THE USUAL WAY//
//////////////////////////////</font><font color=green>
//disable clipping</font>
glDisable(GL_CLIP_PLANE0);
<font color=green>//clear buffers and set the viewport to screen resolution</font>
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT  );
glViewport(0, 0, 1024, 768);
<font color=green>//draw the scene</font>
draw_scene();
<font color=green>//draw the water with the reflection texture
// Setup texture coordinate equations</font>
static GLfloat lsPlaneS[] = { 1, 0, 0, 0 };
static GLfloat lsPlaneT[] = { 0, 1, 0, 0 };
static GLfloat lsPlaneR[] = { 0, 0, 1, 0 };
static GLfloat lsPlaneQ[] = { 0, 0, 0, 1 };
glPushMatrix();
glLoadIdentity();
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, lsPlaneS);
glTexGenfv(GL_T, GL_EYE_PLANE, lsPlaneT);
glTexGenfv(GL_R, GL_EYE_PLANE, lsPlaneR);
glTexGenfv(GL_Q, GL_EYE_PLANE, lsPlaneQ);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glPopMatrix();
<font color=green>//setting up texture matrix</font>
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glTranslatef(.5f, .5f, 0);
glScalef(.5f,.5f, 1);
<font color=orange>setupclipplane();
ModifyProjectionMatrix(plane);</font>
gluPerspective( 60.0, 1024.0f / 768.0f, 1, 8000.0 );
glMatrixMode(GL_MODELVIEW); 
<font color=green>//Now you render your water plane here</font>
glBindTexture(GL_TEXTURE_2D, reflection_texture );
draw_water();
<font color=green>//To finish, disable auto texture generation.</font>
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q); 




swap_buffers();








[Edited by - Khaos Dragon on August 9, 2004 12:43:54 AM]
Bruno
Bruno
I think the part where you grab the inverted scene to a texture is correct., however make sure the clipplane is correctly set.
Render the texture you obtain to a quad, and make sure it's what you want, if not, play around with the clipplane y value.

When rendering the water, you need to project the texture into the water planet, wich you aren't, you are simply rendering the water plane with the reflected texture.
Before rendering the water, you need to do something like this :

enable automatic texture generation

// Setup texture coordinate equations
static GLfloat lsPlaneS[] = { 1.f, 0, 0, 0 };
static GLfloat lsPlaneT[] = { 0, 1.f, 0, 0 };
static GLfloat lsPlaneR[] = { 0, 0, 1.f, 0 };
static GLfloat lsPlaneQ[] = { 0, 0, 0, 1.f };

glPushMatrix();
glLoadIdentity();
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, lsPlaneS);
glTexGenfv(GL_T, GL_EYE_PLANE, lsPlaneT);
glTexGenfv(GL_R, GL_EYE_PLANE, lsPlaneR);
glTexGenfv(GL_Q, GL_EYE_PLANE, lsPlaneQ);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glPopMatrix();

setting up texture matrix

glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glTranslatef(.5f, .5f, 0);
glScalef(.5f, .5f, 1.f);
gluPerspective(bla bla);

glMatrixMode(GL_MODELVIEW);
glBindTexture(GL_TEXTURE_2D,reflection_texture);

Now you render your water plane here

draw_water();

To finish, disable auto texture generation.

glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);


Khaos Dragon
Khaos Dragon
Thanks for the help Bruno, it seems to work:



However I am getting excessive tearing where the water plane and terrain meet, especially when I am moving. To highlight this I have drawn the following.



Does anyone have any idea what could be causing this? I was thinking perhaps I should try that extension which dynamically creates mipmaps for this specific texture.
Bruno
Bruno
Cool that you got it working :)

About that problem, how are you generating your texture ?
Might also be the clipplane that has a very little offset, either up or down., instead of -1, try -0.99 or something like that.
Khaos Dragon
Khaos Dragon
I have tried offsetting the clip plane in both directions and it had no effect. I increased the texture size to 1024 by 512 instead of 512 by 512 and it definitely smoothed out the pixelation of the texture, but had no effect on the tearing.

But the following is how I created my texture object:
unsigned int reflection_texture, tex_width = 1024, tex_height = 512;unsigned char* buffer_for_reflection_tex;glGenTextures(1, &reflection_texture );glBindTexture(GL_TEXTURE_2D, reflection_texture );buffer_for_reflection_tex = new unsigned char[tex_width*tex_height*4];glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer_for_reflection_tex);


Edit: I have tried without success to use a 1024 by 1024 texture. I first assumed that in creating a 1024 by 1024 viewport, that even though the resolution only had a height of 768 that the bottom 256 pixels would be saved in the screen buffer anyways. I then tried to figure out a way so that the vertical coordinates of the texture could be scaled down to 3/4 their original values in order to map from 1024 to 768, but have had no such success in doing this. I have even tried glScalef(.5f, 0.75f*.5f, 1.f) on the texture matrix but this had no effect.

I have noticed that although a lot of the texture is messed up when using 1024 by 1024, the parts that show are very nice and there is no tearing, so I think figuring this out may be the solution to the problem.

[Edited by - Khaos Dragon on August 8, 2004 3:40:47 PM]
Bruno
Bruno
I think the problem is in your texture creation you have a border on it , you need to clamp_to_edge on the texture.,

This is how i create mine :

glGenTextures(1, &water_ref);
glBindTexture(GL_TEXTURE_2D, water_ref);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, waterX, waterY, 0);


That should do it :) , i hope
Khaos Dragon
Khaos Dragon
Quote:
Original post by Bruno
I think the problem is in your texture creation you have a border on it , you need to clamp_to_edge on the texture.,

This is how i create mine :

glGenTextures(1, &water_ref);
glBindTexture(GL_TEXTURE_2D, water_ref);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, waterX, waterY, 0);


That should do it :) , i hope


:( Unfortunately the clamping does not seem to be making any improvements.
_the_phantom_
_the_phantom_
you've basicaly got a problem where in boarder cases the geometry shows through the plane and get included in the texture, there are various ways to fix this, one being setting up a different projection matrix which culls to the plane on the near plane, this should remove the effects you can see.

I had a thread on this a short time ago (probably on about page 2 or 3 by now), titled Reflections : , have a look and have a look at YannL's explaination towards the end of it which cleared up the confusion for me (and the thread I referenced at the start which also holds a fair amount of infomation on it).
Khaos Dragon
Khaos Dragon
Quote:
Original post by zedzeek
r u using glClipPlane(..)?
perhaps try using polygon offset.
make te znear value 100x greater, does the problem go away?


Hmm, the problem still persists after trying that. I am using
_the_phantom_
_the_phantom_
meh, your matrix multiplication is totaly wrong.

This is why I pointed you are the website here

OpenGL's matrix are build as
0     4     8     121     5     9     132     6     10    143     7     11    15


or column major
You've transposed the matrix in your calculations to row-major.

You calculation should look like this for the point
NewX = X * Matrix[0] + Y * Matrix[4] + Z * Matrix[8] + Matrix[13];NewY = X * Matrix[1] + Y * Matrix[5] + Z * Matrix[9] + Matrix[13];NewZ = X * Matrix[2] + Y * Matrix[6] + Z * Matrix[10] + Matrix[14];NewW = 1;

and like this for the normal (only transform by the upper 3x3 matrix)
NewX = X * Matrix[0] + Y * Matrix[4] + Z * Matrix[8];NewY = X * Matrix[1] + Y * Matrix[5] + Z * Matrix[9];NewZ = X * Matrix[2] + Y * Matrix[6] + Z * Matrix[10];


Your point should be of the form (x,y,z,1) and your normal doesnt matter about the W coord (I set it to zero).

You also have to normalise the normal once you've transformed it.

btw, as your code stands you are setting up a clip plane on the Y=0 plane not Y=-1 as you said earlier on IRC.

Make sure you understand everything i've said above, dont go blindly using it as it could well bite you in the arse later (most importantly read up on how OpenGL mastrix multiplication works and how things are transformed)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.