Original Post
Hi - first post here :) I'm trying to render a simple planar reflection to a texture in opengl, but I'm facing problems with the texture coordinate projection. I'm not sure if I'm doing it right - also when I increase the number of subdivisions in the plane (WATER_PTS), it looks better, but I want to be able to do a reflection with a single quad (ie WATER_PTS = 2). Any help?
#include <gl/glut.h>
#define TEXTURE_SIZE 512
#define WATER_SIZE 10.0
#define WATER_PTS 10
#define ROT_STEP 0.5f
float rotx = 0.0f, roty = 0.0f, rotz = 0.0f;
float srotx = 10.0f, sroty = 10.0f, srotz = 0.0f;
float P[3] = {0.0f, 0.0f, 0.0f };
float V[3] = {0.0f, 1.0f, 0.0f };
float M[16];
struct Point{
double x, y, z;
double u, v;
};
Point water[WATER_PTS][WATER_PTS];
int win_h = 900, win_w = 600;
static unsigned char texture[3 * TEXTURE_SIZE * TEXTURE_SIZE];
static unsigned int texture_id;
void UpdateUVCoords()
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
double X, Y, Z;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
for(int i = 0; i < WATER_PTS; i++)
{
for(int j = 0; j < WATER_PTS; j++)
{
gluProject(water[j].x, water[j].y, water[j].z, modelview, projection, viewport, &X, &Y, &Z );
water[j].u = X / win_w;
water[j].v = Y / win_h;
}
}
}
void GetReflectionMatrix(float*m, float*p, float* v)
{
float vp = p[0] * v[0] + p[1] * v[1] + p[2] * v[2];
m[0] = 1.0f - 2.0f * v[0] * v[0];
m[1] = -2.0f * v[0] * v[1];
m[2] = -2.0f * v[0] * v[2];
m[3] = 0.0f;
m[4] = m[1];
m[5] = 1.0f - 2.0f * v[1] * v[1];
m[6] = -2.0f * v[1] * v[2];
m[7] = 0.0f;
m[8] = m[2];
m[9] = m[6];
m[10] = 1.0f - 2.0f * v[2] * v[2];
m[11] = 0.0f;
m[12] = 2.0f * vp * v[0];
m[13] = 2.0f * vp * v[1];
m[14] = 2.0f * vp * v[2];
m[15] = 1.0f;
}
void DrawCube(float r, float g, float b)
{
glDisable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(0.0f,1.0f,0.0f);
glRotatef(rotx,1,0,0);
glRotatef(roty,0,1,0);
glRotatef(rotz,0,0,1);
glColor3f(r,g,b);
glutSolidCube(1);
glPopMatrix();
glEnable(GL_TEXTURE_2D);
}
void DrawWater()
{
glColor3f(1,1,1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture_id);
for(int i = 0; i < WATER_PTS - 1; i++)
{
glBegin(GL_QUAD_STRIP);
for(int j = 0; j < WATER_PTS; j++)
{
glNormal3d(0.0, 1.0, 0.0);
glTexCoord2f(water[j].u, water[j].v);
glVertex3f(water[j].x, water[j].y, water[j].z);
glTexCoord2f(water[i+1][j].u, water[i+1][j].v);
glVertex3f(water[i+1][j].x, water[i+1][j].y, water[i+1][j].z);
}
glEnd();
}
glDisable(GL_TEXTURE_2D);
}
bool init(void)
{
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
GetReflectionMatrix(M, P, V);
glEnable (GL_TEXTURE_2D);
glGenTextures (1, &texture_id);
glBindTexture (GL_TEXTURE_2D, texture_id);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, texture);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
double step = WATER_SIZE / (WATER_PTS - 1);
for(int i = 0; i < WATER_PTS; i++)
{
for(int j = 0; j < WATER_PTS; j++)
{
water[j].x = -WATER_SIZE + 2 * i * step;
water[j].y = 0.0;
water[j].z = -WATER_SIZE + 2 * j * step;
}
}
return true;
}
void render(void)
{
glPushMatrix();
glTranslatef(0.0f, 0.0f, -10.0f);
glRotatef(srotx,1,0,0);
glRotatef(sroty,0,1,0);
glRotatef(srotz,0,0,1);
glPushMatrix();
glMultMatrixf(M);
glViewport(0, 0, TEXTURE_SIZE, TEXTURE_SIZE);
glClearColor (0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawCube(1,0,0);
glEnable(GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, texture_id);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, TEXTURE_SIZE, TEXTURE_SIZE);
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glViewport(0, 0, win_w, win_h);
glMatrixMode(GL_MODELVIEW);
glClearColor (0.8, 0.8, 0.8, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawCube(0,1,0);
UpdateUVCoords();
DrawWater();
glPopMatrix();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (h == 0) h = 1;
gluPerspective(45, (float)w/(float)h, 1.0, 5000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
win_w = w;
win_h = h;
}
void key(unsigned char key, int x, int y)
{
switch(key){
case ',':
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(4.0f);
break;
case '.':
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glLineWidth(1.0f);
break;
case 'o':
exit(0);
break;
case 'q':
rotx+= ROT_STEP;
break;
case 'Q':
rotx-= ROT_STEP;
break;
case 'a':
roty+= ROT_STEP;
break;
case 'A':
roty-= ROT_STEP;
break;
case 'z':
rotz+= ROT_STEP;
break;
case 'Z':
rotz-= ROT_STEP;
break;
case 'w':
srotx+= ROT_STEP;
break;
case 'W':
srotx-= ROT_STEP;
break;
case 's':
sroty+= ROT_STEP;
break;
case 'S':
sroty-= ROT_STEP;
break;
case 'x':
srotz+= ROT_STEP;
break;
case 'X':
srotz-= ROT_STEP;
break;
}
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowSize(win_h, win_w);
glutCreateWindow("Reflection Test");
init();
glutDisplayFunc(render);
glutReshapeFunc(reshape);
glutKeyboardFunc(key);
glutMainLoop();
return 0;
}

