2
\$\begingroup\$

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: https://i.stack.imgur.com/434Ug.png

But when I use a simple toon shader program to draw the objects, the reflection isn't there: https://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;
}
\$\endgroup\$
3
  • \$\begingroup\$ 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 \$\endgroup\$
    – user13213
    Jul 24, 2012 at 11:53
  • \$\begingroup\$ 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 \$\endgroup\$
    – pratyai
    Jul 24, 2012 at 12:55
  • \$\begingroup\$ 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? \$\endgroup\$
    – pratyai
    Jul 24, 2012 at 18:27

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ 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; \$\endgroup\$
    – pratyai
    Jul 24, 2012 at 20:51
  • \$\begingroup\$ gl_ClipDistance still doesn't work though as mesa implementation of openGL doesn't support after GLSL1.20 \$\endgroup\$
    – pratyai
    Jul 24, 2012 at 21:02
  • \$\begingroup\$ @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? \$\endgroup\$ Jul 24, 2012 at 21:09
  • \$\begingroup\$ Apparently gl_ClipDistance should work after GLSL1.20, but the Mesa is supporting GLSL1.20 and not beyond : mesa3d.org/shading.html#glsl120 \$\endgroup\$
    – pratyai
    Jul 24, 2012 at 21:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .