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;
}