Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am trying to draw a scene with objects floating around and a mirror among them to reflect the objects in front of it.

I managed to draw the reflection properly when the floating objects aren't drawn using a glsl shader program: http://i.stack.imgur.com/434Ug.png

But when I use a simple toon shader program to draw the objects, the reflection isn't there: http://i.stack.imgur.com/s2AlB.png

So, I need some advice on how to do in properly so that it would work for both cases.

FYI, I am using negative scaling to draw the reflection, using stencil buffer to draw it only on the right areas on mirror, using a clipping plane so that objects behind the mirror wouldn't have reflections.

Here is the code I used to draw the scene in the images (I apologize for all those includes and defines, most of which aren't used anyway):

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <deque>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
#include <fstream>
using namespace std;

typedef double dbl;
typedef float flt;
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;

#define abs(x) ((x)<0?-(x):(x))
#define sqr(x) ((x)*(x))
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define intclz(x) __builtin_clz(x)
#define intctz(x) __builtin_ctz(x)
#define intln(x) (32-intclz(x))
#define intbc(x) __builtin_popcount(x)
#define atbit(x,i) (((x)>>(i))&1)
#define tof(x) __typeof(x)
#define FORab(i,a,b) for (int i=(a); i<=(b); ++i)
#define FOR1(i,n) FORab(i,1,n)
#define FOR(i,n) FORab(i,0,n-1)
#define allstl(i,x) for (tof((x).begin()) i = (x).begin(); i!=(x).end(); ++i)
#define begend(x) (x).begin(),(x).end()
#define ms(a,v) memset(a,v,sizeof(a))
#define msn(a,v,n) memset(a,v,n*sizeof(a[0]))
#define mcp(d,s,n) memcpy(d,s,n*sizeof(s[0]))
#define clamp(x,a,b) min(max(a,x),b)

// Camera works

flt camrotx = 0, camroty = 0;
flt camposx = 0, camposy = 0, camposz = -15;
int screenWidth = 640, screenHeight = 480;
int movx = 0, movy = 0;

const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 0.0f, 4.0f, 4.0f, 0.0f };

const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };

// Shader works

static int readfile(const char* filename, char* buffer, int buflen, char** strs)
{
    FILE* fx = fopen(filename, "rt");
    char* buf = buffer;
    int ret = 0;
    while ((buf=fgets(buf,buflen-(buf-buffer)-1,fx))!=NULL) strs[ret++] = buf, buf += strlen(buf)+1;
    fclose(fx);
    return ret;
}

GLuint vtxshaderId=0, frgshaderId=0, myshaderprogId=0;

static GLuint loadshader(const char* filename, GLenum shadertype)
{
    GLuint ret = 0;
    char buffer[10000];
    char* lines[100];
    int linec = readfile(filename, buffer, sizeof(buffer), lines);
    ret = glCreateShader(shadertype);
    glShaderSource(ret, linec, (const GLchar**)lines, NULL);
    glCompileShader(ret);
    glGetShaderInfoLog(ret,sizeof(buffer),&linec,buffer);
    cout<<buffer<<endl;
    return ret;
}

static void des_shaders()
{
    if (vtxshaderId) glDeleteShader(vtxshaderId);
    if (frgshaderId) glDeleteShader(frgshaderId);
    if (myshaderprogId) glDeleteProgram(myshaderprogId);
}

static void gen_shaders()
{
    des_shaders();
    myshaderprogId = glCreateProgram();

    vtxshaderId = loadshader("/home/prat/Desktop/tatka/upalGL/water/assets/crow1.vert", GL_VERTEX_SHADER);
    glAttachShader(myshaderprogId,vtxshaderId);
    frgshaderId = loadshader("/home/prat/Desktop/tatka/upalGL/water/assets/crow1.frag", GL_FRAGMENT_SHADER);
    glAttachShader(myshaderprogId,frgshaderId);

    glLinkProgram(myshaderprogId);
}

// Scene works

const int slices = 32, stacks = 32, circlecount = 10;
struct circle
{
    flt x, y, z, r, red, green, blue;
};
circle sceneCircles[circlecount];

static void gen_sceneCircles()
{
    sceneCircles[0] = {0, 0, 0, 3, 1, 1, 1};
    FOR1(i,circlecount-1) sceneCircles[i] = { (rand()%1000)/100.0-5.0, (rand()%1000)/100.0-5.0, (rand()%1000)/100.0-5.0, (rand()%100)/100.0+0.4, (rand()%100)/100.0, (rand()%100)/100.0, (rand()%100)/100.0 };
}

static void draw_sceneCircles()
{
    FOR(i, circlecount)
    {
        const circle& c = sceneCircles[i];
        glColor3f(c.red,c.green,c.blue);
        glPushMatrix();
        glTranslated(c.x,c.y,c.z);
        glutSolidSphere(c.r,slices,stacks);
        glPopMatrix();
    }
}

// GLUT callback Handlers

static void resize(int width, int height)
{
    screenWidth = width, screenHeight = height;
    glViewport(0, 0, screenWidth, screenHeight);
}

static void display_init_cam(void)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (float)(screenWidth)/screenHeight, 0.5, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslated(camposx, camposy, camposz);
    glRotatef(camroty, 1, 0, 0);
    glRotatef(camrotx, 0, 1, 0);
}

bool flg = false;
const int mirrorpos = -0, mirrorsize = 10;
GLdouble mirrorplane[] = {0, 1, 0, -mirrorpos};

static void display(void)
{
    #define outtahere glutSwapBuffers();return
    #define glStencilFuncDefault() glStencilFunc(GL_ALWAYS,0,0xFFFFFFFF)
    #define glStencilOpDefault() glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
    #define glColorMaskDefault() glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE)
    #define glDepthMaskDefault() glDepthMask(GL_TRUE)
    #define glCullFaceDefault() glCullFace(GL_BACK)
    #define glDepthFuncDefault() glDepthFunc(GL_LEQUAL)
    glClearColor(0, 0, 0, 0);
    glClearStencil(0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    display_init_cam();
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    // mask

    glStencilFunc(GL_ALWAYS, 1, 0x1);
    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glBegin(GL_QUADS);
    glNormal3f(0, 1, 0);
    glColor4f(1,1,1,1);
    glVertex3f(mirrorsize, mirrorpos, mirrorsize);
    glColor4f(.5,.5,.5,1);
    glVertex3f(mirrorsize, mirrorpos, -mirrorsize);
    glColor4f(0,0,0,1);
    glVertex3f(-mirrorsize, mirrorpos, -mirrorsize);
    glColor4f(.5,.5,.5,1);
    glVertex3f(-mirrorsize, mirrorpos, mirrorsize);
    glEnd();
    glStencilFunc(GL_ALWAYS, 0, 0x1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    if (flg) glUseProgram(myshaderprogId);
    draw_sceneCircles();
    glUseProgram(0);
    glStencilFuncDefault();
    glStencilOpDefault();

    // reflection

    glStencilFunc(GL_EQUAL, 1, 0x1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glCullFace(GL_FRONT);
    glClear(GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glScalef(1, -1, 1);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glTranslatef(0, -2*mirrorpos, 0);
    if (flg) glUseProgram(myshaderprogId);
    glEnable(GL_CLIP_PLANE0);
    glClipPlane(GL_CLIP_PLANE0, mirrorplane);
    draw_sceneCircles();
    glDisable(GL_CLIP_PLANE0);
    glUseProgram(0);
    glPopMatrix();
    glStencilFuncDefault();
    glStencilOpDefault();
    glCullFaceDefault();

    outtahere;
}

static void key(unsigned char key, int x, int y)
{
    #define GLUT_KEY_ESC 27
    #define GLUT_KEY_BKSP 8
    if (key==GLUT_KEY_ESC) exit(EXIT_SUCCESS);
    else if (key==GLUT_KEY_BKSP) glutFullScreenToggle();
    else if (key=='=') gen_shaders();
    else if (tolower(key)=='a') camrotx += 5;
    else if (tolower(key)=='d') camrotx -= 5;
    else if (tolower(key)=='w') camposz += 1;
    else if (tolower(key)=='s') camposz -= 1;
    else if (key==' ') flg = !flg;
    glutPostRedisplay();
}

static void idle(void)
{
    glutPostRedisplay();
}

int mx=-1, my=-1;
int mx2=-1, my2=-1;

static void click(int button, int state, int x, int y)
{
    if (button==GLUT_LEFT_BUTTON)
    {
        if (state==GLUT_DOWN) mx = x, my = y;
        else mx = my = -1;
    }
    else if (button==GLUT_RIGHT_BUTTON)
    {
        if (state==GLUT_DOWN) mx2 = x, my2 = y;
        else mx2 = my2 = -1;
    }
    else if (button==3) camposz += 0.2;
    else if (button==4) camposz -= 0.2;
}

static void mousemove(int x, int y)
{
    if (mx>=0 and my>=0)
    {
        int dx = x-mx, dy = y-my;
        camrotx += dx/10.0;
        camroty += dy/10.0;
        const flt clamp_range = 90;
        camroty = clamp(camroty,-clamp_range,clamp_range);
        mx = x, my = y;
        glutPostRedisplay();
    }
    if (mx2>=0 and my2>=0)
    {
        int dx = x-mx2, dy = y-my2;
        camposx += dx/100.0;
        camposy -= dy/100.0;
        mx2 = x, my2 = y;
        glutPostRedisplay();
    }
}

// Program entry point

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitWindowSize(screenWidth,screenHeight);
    glutInitWindowPosition(10,10);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA | GLUT_STENCIL);
    glutCreateWindow("Sample Scene");

    glewInit();

    glutReshapeFunc(resize);
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);
    glutMouseFunc(click);
    glutMotionFunc(mousemove);

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_ALPHA_TEST);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_LIGHT0);
    glEnable(GL_NORMALIZE);
    glEnable(GL_RESCALE_NORMAL);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_NORMALIZE);
    glCullFace(GL_BACK);
    glDepthFunc(GL_LEQUAL);
    glShadeModel(GL_SMOOTH);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
    glClearStencil(0);
    glClearDepth(1);

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

    if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader) printf("Ready for GLSL\n");
    if (glewIsSupported("GL_VERSION_2_0")) printf("Ready for OpenGL 2.0\n");

    gen_sceneCircles();
    gen_shaders();

    glutMainLoop();

    des_shaders();

    return EXIT_SUCCESS;
}

Vertex Shader:

varying vec3 normal, lightDir;

void main()
{
    lightDir = normalize(vec3(gl_LightSource[0].position));
    normal = normalize(gl_NormalMatrix * gl_Normal);

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Fragment Shader:

varying vec3 normal, lightDir;

void main()
{
    float intensity;
    vec3 n;
    vec4 color;

    n = normalize(normal);
    intensity = max(dot(lightDir,n),0.0);

    if (intensity > 0.98)
        color = vec4(0.8,0.8,0.8,1.0);
    else if (intensity > 0.5)
        color = vec4(0.4,0.4,0.8,1.0);
    else if (intensity > 0.25)
        color = vec4(0.2,0.2,0.4,1.0);
    else
        color = vec4(0.1,0.1,0.1,1.0);

    gl_FragColor = color;
}
share|improve this question
    
If you are using negative scaling along an odd number of axes, that will essentially flip your faces, maybe try changing the triangle winding order for the reflection, or switching face culling from back to front culling –  melak47 Jul 24 '12 at 11:53
    
Thanks, I am already switching face culling, otherwise it the faces would be drawn wrong even without using shader program. But, now I think the problem may be related to clipping, as at some angle I see the reflection being drawn partially - clipped awkwardly. i.imgur.com/Xrrk3.png –  pratyai Jul 24 '12 at 12:55
    
I just have learned that the very same code as above renders the reflections correctly on some other computer (using windows). So I am wondering if this has something to do with the Mesa3D implementation of OpenGL that my linux OS uses. Any ideas? –  pratyai Jul 24 '12 at 18:27
add comment

1 Answer 1

up vote 2 down vote accepted

I think when using shaders you have to calculate the clip planes yourself in the vertex shader and output the clip value (plane dotted with position) to the gl_ClipDistance[0] built-in output variable. See the opengl.org man page for some more details.

The plane equation set in glClipPlane in C++ may also be available in GLSL as a built-in variable; I'm not sure. If not, you'd have to send it down to the shader as a uniform.

share|improve this answer
    
True! This was one of the reasons, sadly not the only one. Just solved the problem by updating to mesa8.1 from this ppa(launchpad.net/~oibaf/+archive/graphics-drivers)! I tried with gl_ClipDistance before, but apparently it is not recognized by the glsl compiler available in mesa8.0.4 or below, and gl_ClipVertex also won't work. After that this line was enough to do the trick, gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex; –  pratyai Jul 24 '12 at 20:51
    
gl_ClipDistance still doesn't work though as mesa implementation of openGL doesn't support after GLSL1.20 –  pratyai Jul 24 '12 at 21:02
    
@pratyai That's odd, the opengl.org page says gl_ClipDistance should work all the way up to GLSL 4.20, the current version. gl_ClipVertex is a deprecated feature after GLSL 1.20. Are you saying in Mesa it's the other way around? –  Nathan Reed Jul 24 '12 at 21:09
    
Apparently gl_ClipDistance should work after GLSL1.20, but the Mesa is supporting GLSL1.20 and not beyond : mesa3d.org/shading.html#glsl120 –  pratyai Jul 24 '12 at 21:35
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.