#include <sh/sh.hpp>
#include <GL/glut.h>
#include <GL/glext.h>
#include <GL/glu.h>
#include "Camera.hpp"
using namespace SH;
ShMatrix4x4f MV, MD, VM; // transformations
ShPoint3f light_pv; // VCS light source position
Camera camera; // camera object (see Camera.hpp)
ShProgram vsh, fsh; // vertex and fragment shader objects
// Glut data
int buttons[5] = GLUT_UP, GLUT_UP, GLUT_UP, GLUT_UP, GLUT_UP;
int cur_x, cur_y; // for trackball
// initialize program objects for shaders
void init_shaders()
vsh = SH_BEGIN_PROGRAM("gpu:vertex")
ShInputNormal3f nm; // MCS normal
ShInputPoint4f pm; // MCS position
ShOutputNormal3f nv; // VCS normal
ShOutputVector3f lv; // VCS light vector
ShOutputPosition4f pd; // DCS position
ShPoint3f pv = (MV|pm)(0,1,2); // Compute viewspace position
lv = light_pv - pv; // Compute light direction in view space
pd = MD|pm; // Transform position to device space
nv = normalize((nm|VM)(0,1,2)); // Transform normal to view space
SH_END;
// declare and initialize diffuse color
ShColor3f kd = ShColor3f(0.5, 0.7, 0.9);
fsh = SH_BEGIN_PROGRAM("gpu:fragment")
ShInputNormal3f nv; // VCS normal
ShInputVector3f lv; // VCS light vector
ShOutputColor3f c; // fragment color
// normalize interpolated vectors to unit length
nv = normalize(nv);
lv = normalize(lv);
// per-pixel diffuse lighting model
c = kd * pos(nv|lv);
SH_END;
// GLUT callback
// draw using glut teapot (which has several faults, but)
void display ()
// make sure Sh uniform parameters are synchronized
shUpdate();
// clear framebuffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw teapot
glFrontFace(GL_CW);
glutSolidTeapot(2.5);
glFrontFace(GL_CCW);
// swap front and back buffers
glutSwapBuffers();
void setup_view ()
MV = camera.shModelView();
VM = inverse(MV);
MD = camera.shModelViewProjection(ShMatrix4x4f());
// GLUT callback
// called on window resize
void reshape (int width, int height)
glViewport(0, 0, width, height);
setup_view();
// GLUT callback
// called on mouse movement
void motion (int x, int y)
const double factor = 20.0;
bool changed = false;
// process UI events
if (buttons[GLUT_LEFT_BUTTON] == GLUT_DOWN)
// rotate camera orientation using left mouse button
camera.orbit(cur_x, cur_y, x, y,
glutGet(GLUT_WINDOW_WIDTH),
glutGet(GLUT_WINDOW_HEIGHT));
changed = true;
if (buttons[GLUT_MIDDLE_BUTTON] == GLUT_DOWN)
// track camera forward and back using middle mouse button
camera.move(0, 0, (y - cur_y)/factor);
changed = true;
if (buttons[GLUT_RIGHT_BUTTON] == GLUT_DOWN)
// pan camera using right mouse button
camera.move((x - cur_x)/factor, (cur_y - y)/factor, 0);
changed = true;
// update everything
if (changed)
setup_view();
glutPostRedisplay();
void mouse (int button, int state, int x, int y)
buttons[button] = state;
cur_x = x;
cur_y = y;
int main (int argc, char** argv)
// set up GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(512, 512);
glutCreateWindow("glutex: Sh Example");
// register callback functions
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
// initialize Sh
shInit();
// set up default GPU backend (optional)
shSetBackend("arb");
// initialize OpenGL state, turn on shader support
glEnable(GL_DEPTH_TEST);
glEnable(GL_VERTEX_PROGRAM_ARB);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glClearColor(0.0, 0.0, 0.0, 1.0);
setup_view();
// Place the camera at its initial position
camera.move(0.0, 0.0, -15.0);
// Set up the light position
qv = ShPoint3f(5.0, 5.0, 5.0);
// define our shader programs
init_shaders();
// bind shaders
shBind(vsh);
shBind(fsh);
// hand over event processing to GLUT
glutMainLoop();