demos: import GLSL raytracing demos
authorRALOVICH, Kristóf <tade60@freemail.hu>
Wed, 24 Mar 2010 01:48:15 +0000 (21:48 -0400)
committerBrian Paul <brianp@vmware.com>
Wed, 24 Mar 2010 15:04:14 +0000 (09:04 -0600)
progs/glsl/Makefile
progs/glsl/fsraytrace.c [new file with mode: 0644]
progs/glsl/vsraytrace.c [new file with mode: 0644]

index 3b5a5959aeebd2a5a70f054f63e74f927638e563..6030c8002f5054dead4d3244e2969d40af97bc96 100644 (file)
@@ -26,6 +26,7 @@ PROG_SOURCES = \
        convolutions.c \
        deriv.c \
        fragcoord.c \
+       fsraytrace.c \
        identity.c \
        linktest.c \
        mandelbrot.c \
@@ -46,7 +47,8 @@ PROG_SOURCES = \
        trirast.c \
        twoside.c \
        vert-or-frag-only.c \
-       vert-tex.c
+       vert-tex.c \
+       vsraytrace.c
 
 UTIL_HEADERS = \
        extfuncs.h \
diff --git a/progs/glsl/fsraytrace.c b/progs/glsl/fsraytrace.c
new file mode 100644 (file)
index 0000000..dca8fd2
--- /dev/null
@@ -0,0 +1,385 @@
+// -*- mode: c; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/*
+  Copyright (c) 2010 Kristóf Ralovich
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+#include <GL/glut.h>
+#include "shaderutil.h"
+
+
+static int Win;
+static int WinWidth = 512, WinHeight = 512;
+static GLfloat Xrot = 0, Yrot = 0;
+static int mouseGrabbed = 0;
+static GLuint vertShader;
+static GLuint fragShader;
+static GLuint program;
+
+static const char* vsSource =
+  "varying vec2 rayDir;\n"
+  "\n"
+  "void main()\n"
+  "{\n"
+  "  rayDir = gl_MultiTexCoord0.xy - vec2(0.5,0.5);\n"
+  "  gl_Position = gl_ProjectionMatrix * gl_Vertex;\n"
+  "}\n";
+
+static const char* fsSource =
+  "const float INF     = 9999.9;                                       \n"
+  "const float EPSILON = 0.00001;                                      \n"
+  "const vec3 lightPos = vec3(0.0, 8.0, 1.0);                          \n"
+  "const vec4 backgroundColor = vec4(0.2,0.3,0.4,1);                   \n"
+  "                                                                    \n"
+  "varying vec2 rayDir;                                                \n"
+  "                                                                    \n"
+  "uniform mat3 rot;                                                   \n"
+  "                                                                    \n"
+  "struct Ray                                                          \n"
+  "{                                                                   \n"
+  "vec3 orig;                                                          \n"
+  "vec3 dir;                                                           \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "struct Sphere                                                       \n"
+  "{                                                                   \n"
+  "  vec3 c;                                                           \n"
+  "  float r;                                                          \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "struct Isec                                                         \n"
+  "{                                                                   \n"
+  "  float t;                                                          \n"
+  "  int idx;                                                          \n"
+  "  vec3 hit;                                                         \n"
+  "  vec3 n;                                                           \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "const Sphere spheres0 = Sphere( vec3(0.0,0.0,-1.0), 0.5 );          \n"
+  "const Sphere spheres1 = Sphere( vec3(-3.0,0.0,-1.0), 1.5 );         \n"
+  "const Sphere spheres2 = Sphere( vec3(0.0,3.0,-1.0), 0.5 );          \n"
+  "const Sphere spheres3 = Sphere( vec3(2.0,0.0,-1.0), 1.0 );          \n"
+  "                                                                    \n"
+  "// Mesa intel gen4 generates \"unsupported IR in fragment shader 13\" for\n"
+  "// sqrt, let's work around.                                         \n"
+  "float                                                               \n"
+  "sqrt_hack(float f2)                                                 \n"
+  "{                                                                   \n"
+  "  vec3 v = vec3(f2,0.0,0.0);                                        \n"
+  "  return length(v);                                                 \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "void                                                                \n"
+  "intersect(const in Ray ray,                                         \n"
+  "          const in Sphere sph,                                      \n"
+  "          const in int idx,                                         \n"
+  "          inout Isec isec)                                          \n"
+  "{                                                                   \n"
+  "  // Project both o and the sphere to the plane perpendicular to d  \n"
+  "  // and containing c. Let x be the point where the ray intersects  \n"
+  "  // the plane. If |x-c| < r, the ray intersects the sphere.        \n"
+  "  vec3 o = ray.orig;                                                \n"
+  "  vec3 d = ray.dir;                                                 \n"
+  "  vec3 n = -d;                                                      \n"
+  "  vec3 c = sph.c;                                                   \n"
+  "  float r = sph.r;                                                  \n"
+  "  float t = dot(c-o,n)/dot(n,d);                                    \n"
+  "  vec3 x = o+d*t;                                                   \n"
+  "  float e = length(x-c);                                            \n"
+  "  if(e > r)                                                         \n"
+  "  {                                                                 \n"
+  "    // no intersection                                              \n"
+  "    return;                                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  // Apply Pythagorean theorem on the (intersection,x,c) triangle   \n"
+  "  // to get the distance between c and the intersection.            \n"
+  "#ifndef BUGGY_INTEL_GEN4_GLSL                                       \n"
+  "  float f = sqrt(r*r - e*e);                                        \n"
+  "#else                                                               \n"
+  "  float f = sqrt_hack(r*r - e*e);                                   \n"
+  "#endif                                                              \n"
+  "  float dist = t - f;                                               \n"
+  "  if(dist < 0.0)                                                    \n"
+  "  {                                                                 \n"
+  "    // inside the sphere                                            \n"
+  "    return;                                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  if(dist < EPSILON)                                                \n"
+  "    return;                                                         \n"
+  "                                                                    \n"
+  "  if(dist > isec.t)                                                 \n"
+  "    return;                                                         \n"
+  "                                                                    \n"
+  "  isec.t = dist;                                                    \n"
+  "  isec.idx = idx;                                                   \n"
+  "                                                                    \n"
+  "  isec.hit  = ray.orig + ray.dir * isec.t;                          \n"
+  "  isec.n = (isec.hit - c) / r;                                      \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "Isec                                                                \n"
+  "intersect(const in Ray ray,                                         \n"
+  "          const in float max_t /*= INF*/)                           \n"
+  "{                                                                   \n"
+  "  Isec nearest;                                                     \n"
+  "  nearest.t = max_t;                                                \n"
+  "  nearest.idx = -1;                                                 \n"
+  "                                                                    \n"
+  "  intersect(ray, spheres0, 0, nearest);                             \n"
+  "  intersect(ray, spheres1, 1, nearest);                             \n"
+  "  intersect(ray, spheres2, 2, nearest);                             \n"
+  "  intersect(ray, spheres3, 3, nearest);                             \n"
+  "                                                                    \n"
+  "  return nearest;                                                   \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "idx2color(const in int idx)                                         \n"
+  "{                                                                   \n"
+  "  vec4 diff;                                                        \n"
+  "  if(idx == 0)                                                      \n"
+  "    diff = vec4(1.0, 0.0, 0.0, 0.0);                                \n"
+  "  else if(idx == 1)                                                 \n"
+  "    diff = vec4(0.0, 1.0, 0.0, 0.0);                                \n"
+  "  else if(idx == 2)                                                 \n"
+  "    diff = vec4(0.0, 0.0, 1.0, 0.0);                                \n"
+  "  else if(idx == 3)                                                 \n"
+  "    diff = vec4(1.0, 1.0, 0.0, 0.0);                                \n"
+  "  return diff;                                                      \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "trace0(const in Ray ray)                                            \n"
+  "{                                                                   \n"
+  "  Isec isec = intersect(ray, INF);                                  \n"
+  "                                                                    \n"
+  "  if(isec.idx == -1)                                                \n"
+  "  {                                                                 \n"
+  "    return backgroundColor;                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  vec4 diff = idx2color(isec.idx);                                  \n"
+  "                                                                    \n"
+  "  vec3 N = isec.n;                                                  \n"
+  "  vec3 L = normalize(lightPos-isec.hit);                            \n"
+  "  vec3 camera_dir = normalize(ray.orig - isec.hit);                 \n"
+  "  return dot(N,L)*diff + pow(                                       \n"
+  "    clamp(dot(reflect(-L,N),camera_dir),0.0,1.0),16.0);             \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "trace1(const in Ray ray)                                            \n"
+  "{                                                                   \n"
+  "  Isec isec = intersect(ray, INF);                                  \n"
+  "                                                                    \n"
+  "  if(isec.idx == -1)                                                \n"
+  "  {                                                                 \n"
+  "    return backgroundColor;                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  Ray reflRay = Ray(isec.hit, reflect(ray.dir, isec.n));            \n"
+  "                                                                    \n"
+  "  vec4 reflCol = trace0(reflRay);                                   \n"
+  "                                                                    \n"
+  "  vec4 diff = idx2color(isec.idx) + reflCol;                        \n"
+  "                                                                    \n"
+  "  vec3 N = isec.n;                                                  \n"
+  "  vec3 L = normalize(lightPos-isec.hit);                            \n"
+  "  vec3 camera_dir = normalize(ray.orig - isec.hit);                 \n"
+  "  return dot(N,L)*diff + pow(                                       \n"
+  "    clamp(dot(reflect(-L,N),camera_dir),0.0,1.0),16.0);             \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "\n"
+  "void\n"
+  "main()\n"
+  "{\n"
+  "  const float z = -0.5;\n"
+  "  const vec3 cameraPos = vec3(0,0,3);                               \n"
+  "  Ray r = Ray(cameraPos, normalize(vec3(rayDir, z) * rot));\n"
+  "  gl_FragColor = trace1(r);\n"
+  "}\n";
+
+
+
+static void
+Idle(void)
+{
+  glutPostRedisplay();
+}
+
+
+static void
+Draw(void)
+{
+  float rot[9] = {1,0,0,  0,1,0,   0,0,1};
+  GLint location = glGetUniformLocation(program, "rot");
+
+  glUseProgram(program);
+  glUniformMatrix3fv(location, 1, 0, rot);
+  static const float m = -10.F;
+  static const float p =  10.F;
+  static const float d = -0.5F;
+  glBegin(GL_QUADS);
+  {
+    glTexCoord2f(0.0F, 0.0F); glVertex3f(m, m, d);
+    glTexCoord2f(1.0F, 0.0F); glVertex3f(p, m, d);
+    glTexCoord2f(1.0F, 1.0F); glVertex3f(p, p, d);
+    glTexCoord2f(0.0F, 1.0F); glVertex3f(m, p, d);
+  }
+  glEnd();
+  glUseProgram(0);
+
+  glutSwapBuffers();
+
+  static int frames = 0;
+  static int t0 = 0;
+  static int t1 = 0;
+  frames++;
+  t1 = glutGet(GLUT_ELAPSED_TIME);
+  float dt = (float)(t1-t0)/1000.0F;
+  if(dt >= 5.0F)
+  {
+    float fps = (float)frames / dt;
+    printf("%f FPS (%d frames in %f seconds)\n", fps, frames, dt);
+    frames = 0;
+    t0 = t1;
+  }
+}
+
+
+static void
+Reshape(int width, int height)
+{
+  WinWidth = width;
+  WinHeight = height;
+  glViewport(0, 0, width, height);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrtho(-10, 10, -10, 10, -1, 1);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+}
+
+
+static void
+Key(unsigned char key, int x, int y)
+{
+  (void) x;
+  (void) y;
+  switch (key) {
+  case 27:
+    glutDestroyWindow(Win);
+    exit(0);
+    break;
+  }
+  glutPostRedisplay();
+}
+
+
+static
+void
+drag(int x, int y)
+{
+  float scale = 1.5F;
+  if(mouseGrabbed)
+  {
+    Xrot = (float)(x - WinWidth/2) / scale;
+    Yrot = (float)(y - WinHeight/2) / scale;
+    printf("%4.2f %4.2f\n", Xrot, Yrot);
+  }
+}
+
+
+static
+void
+mouse(int button, int state, int x, int y)
+{
+  if(state == GLUT_DOWN)
+  {
+    mouseGrabbed = !mouseGrabbed;
+  }
+}
+
+
+static void
+Init(void)
+{
+  glDisable(GL_DEPTH_TEST);
+
+  if(!ShadersSupported())
+  {
+    fprintf(stderr, "Shaders are not supported!\n");
+    exit(-1);
+  }
+
+  vertShader = CompileShaderText(GL_VERTEX_SHADER, vsSource);
+  fragShader = CompileShaderText(GL_FRAGMENT_SHADER, fsSource);
+  program = LinkShaders(vertShader, fragShader);
+  glUseProgram(0);
+
+  if(glGetError() != 0)
+  {
+    fprintf(stderr, "Shaders were not loaded!\n");
+    exit(-1);
+  }
+
+  if(!glIsShader(vertShader))
+  {
+    fprintf(stderr, "Vertex shader failed!\n");
+    exit(-1);
+  }
+
+  if(!glIsProgram(program))
+  {
+    fprintf(stderr, "Shader program failed!\n");
+    exit(-1);
+  }
+
+  printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
+}
+
+
+int
+main(int argc, char *argv[])
+{
+  /*setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);*/
+  glutInit(&argc, argv);
+  glutInitWindowSize(WinWidth, WinHeight);
+  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
+  Win = glutCreateWindow(argv[0]);
+  glewInit();
+  glutReshapeFunc(Reshape);
+  glutKeyboardFunc(Key);
+  glutDisplayFunc(Draw);
+  glutMouseFunc(mouse);
+  glutPassiveMotionFunc(drag);
+  glutIdleFunc(Idle);
+  Init();
+  glutMainLoop();
+  return 0;
+}
+
diff --git a/progs/glsl/vsraytrace.c b/progs/glsl/vsraytrace.c
new file mode 100644 (file)
index 0000000..6e1617a
--- /dev/null
@@ -0,0 +1,363 @@
+// -*- mode: c; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/*
+  Copyright (c) 2010 Kristóf Ralovich
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+#include <GL/glut.h>
+#include "shaderutil.h"
+
+
+static int Win;
+//static int WinWidth = 256, WinHeight = 256;
+static int WinWidth = 50, WinHeight = 50;
+static int mouseGrabbed = 0;
+
+static const char* vsSource =
+  "const float INF     = 9999.9;                                       \n"
+  "const float EPSILON = 0.00001;                                      \n"
+  "const vec3 lightPos = vec3(0.0, 8.0, 1.0);                          \n"
+  "const vec4 backgroundColor = vec4(0.2,0.3,0.4,1);                   \n"
+  "                                                                    \n"
+  "uniform mat3 rot;                                                   \n"
+  "                                                                    \n"
+  "struct Ray                                                          \n"
+  "{                                                                   \n"
+  "vec3 orig;                                                          \n"
+  "vec3 dir;                                                           \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "struct Sphere                                                       \n"
+  "{                                                                   \n"
+  "  vec3 c;                                                           \n"
+  "  float r;                                                          \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "struct Isec                                                         \n"
+  "{                                                                   \n"
+  "  float t;                                                          \n"
+  "  int idx;                                                          \n"
+  "  vec3 hit;                                                         \n"
+  "  vec3 n;                                                           \n"
+  "};                                                                  \n"
+  "                                                                    \n"
+  "const Sphere spheres0 = Sphere( vec3(0.0,0.0,-1.0), 0.5 );          \n"
+  "const Sphere spheres1 = Sphere( vec3(-3.0,0.0,-1.0), 1.5 );         \n"
+  "const Sphere spheres2 = Sphere( vec3(0.0,3.0,-1.0), 0.5 );          \n"
+  "const Sphere spheres3 = Sphere( vec3(2.0,0.0,-1.0), 1.0 );          \n"
+  "                                                                    \n"
+  "// Mesa intel gen4 generates \"unsupported IR in fragment shader 13\" for\n"
+  "// sqrt, let's work around.                                         \n"
+  "float                                                               \n"
+  "sqrt_hack(float f2)                                                 \n"
+  "{                                                                   \n"
+  "  vec3 v = vec3(f2,0.0,0.0);                                        \n"
+  "  return length(v);                                                 \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "void                                                                \n"
+  "intersect(const in Ray ray,                                         \n"
+  "          const in Sphere sph,                                      \n"
+  "          const in int idx,                                         \n"
+  "          inout Isec isec)                                          \n"
+  "{                                                                   \n"
+  "  // Project both o and the sphere to the plane perpendicular to d  \n"
+  "  // and containing c. Let x be the point where the ray intersects  \n"
+  "  // the plane. If |x-c| < r, the ray intersects the sphere.        \n"
+  "  vec3 o = ray.orig;                                                \n"
+  "  vec3 d = ray.dir;                                                 \n"
+  "  vec3 n = -d;                                                      \n"
+  "  vec3 c = sph.c;                                                   \n"
+  "  float r = sph.r;                                                  \n"
+  "  float t = dot(c-o,n)/dot(n,d);                                    \n"
+  "  vec3 x = o+d*t;                                                   \n"
+  "  float e = length(x-c);                                            \n"
+  "  if(e > r)                                                         \n"
+  "  {                                                                 \n"
+  "    // no intersection                                              \n"
+  "    return;                                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  // Apply Pythagorean theorem on the (intersection,x,c) triangle   \n"
+  "  // to get the distance between c and the intersection.            \n"
+  "#define BUGGY_INTEL_GEN4_GLSL 1                                     \n"
+  "#ifndef BUGGY_INTEL_GEN4_GLSL                                       \n"
+  "  float f = sqrt(r*r - e*e);                                        \n"
+  "#else                                                               \n"
+  "  float f = sqrt_hack(r*r - e*e);                                   \n"
+  "#endif                                                              \n"
+  "  float dist = t - f;                                               \n"
+  "  if(dist < 0.0)                                                    \n"
+  "  {                                                                 \n"
+  "    // inside the sphere                                            \n"
+  "    return;                                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  if(dist < EPSILON)                                                \n"
+  "    return;                                                         \n"
+  "                                                                    \n"
+  "  if(dist > isec.t)                                                 \n"
+  "    return;                                                         \n"
+  "                                                                    \n"
+  "  isec.t = dist;                                                    \n"
+  "  isec.idx = idx;                                                   \n"
+  "                                                                    \n"
+  "  isec.hit  = ray.orig + ray.dir * isec.t;                          \n"
+  "  isec.n = (isec.hit - c) / r;                                      \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "Isec                                                                \n"
+  "intersect(const in Ray ray,                                         \n"
+  "          const in float max_t /*= INF*/)                           \n"
+  "{                                                                   \n"
+  "  Isec nearest;                                                     \n"
+  "  nearest.t = max_t;                                                \n"
+  "  nearest.idx = -1;                                                 \n"
+  "                                                                    \n"
+  "  intersect(ray, spheres0, 0, nearest);                             \n"
+  "  intersect(ray, spheres1, 1, nearest);                             \n"
+  "  intersect(ray, spheres2, 2, nearest);                             \n"
+  "  intersect(ray, spheres3, 3, nearest);                             \n"
+  "                                                                    \n"
+  "  return nearest;                                                   \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "idx2color(const in int idx)                                         \n"
+  "{                                                                   \n"
+  "  vec4 diff;                                                        \n"
+  "  if(idx == 0)                                                      \n"
+  "    diff = vec4(1.0, 0.0, 0.0, 0.0);                                \n"
+  "  else if(idx == 1)                                                 \n"
+  "    diff = vec4(0.0, 1.0, 0.0, 0.0);                                \n"
+  "  else if(idx == 2)                                                 \n"
+  "    diff = vec4(0.0, 0.0, 1.0, 0.0);                                \n"
+  "  else if(idx == 3)                                                 \n"
+  "    diff = vec4(1.0, 1.0, 0.0, 0.0);                                \n"
+  "  return diff;                                                      \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "trace0(const in Ray ray)                                            \n"
+  "{                                                                   \n"
+  "  Isec isec = intersect(ray, INF);                                  \n"
+  "                                                                    \n"
+  "  if(isec.idx == -1)                                                \n"
+  "  {                                                                 \n"
+  "    return backgroundColor;                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  vec4 diff = idx2color(isec.idx);                                  \n"
+  "                                                                    \n"
+  "  vec3 N = isec.n;                                                  \n"
+  "  vec3 L = normalize(lightPos-isec.hit);                            \n"
+  "  vec3 camera_dir = normalize(ray.orig - isec.hit);                 \n"
+  "  return dot(N,L)*diff + pow(                                       \n"
+  "    clamp(dot(reflect(-L,N),camera_dir),0.0,1.0),16.0);             \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "vec4                                                                \n"
+  "trace1(const in Ray ray)                                            \n"
+  "{                                                                   \n"
+  "  Isec isec = intersect(ray, INF);                                  \n"
+  "                                                                    \n"
+  "  if(isec.idx == -1)                                                \n"
+  "  {                                                                 \n"
+  "    return backgroundColor;                                         \n"
+  "  }                                                                 \n"
+  "                                                                    \n"
+  "  Ray reflRay = Ray(isec.hit, reflect(ray.dir, isec.n));            \n"
+  "                                                                    \n"
+  "  vec4 reflCol = trace0(reflRay);                                   \n"
+  "                                                                    \n"
+  "  vec4 diff = idx2color(isec.idx) + reflCol;                        \n"
+  "                                                                    \n"
+  "  vec3 N = isec.n;                                                  \n"
+  "  vec3 L = normalize(lightPos-isec.hit);                            \n"
+  "  vec3 camera_dir = normalize(ray.orig - isec.hit);                 \n"
+  "  return dot(N,L)*diff + pow(                                       \n"
+  "    clamp(dot(reflect(-L,N),camera_dir),0.0,1.0),16.0);             \n"
+  "}                                                                   \n"
+  "                                                                    \n"
+  "void main()                                                         \n"
+  "{                                                                   \n"
+  "  const vec3 cameraPos = vec3(0,0,3);                               \n"
+  "  const vec3 rayDir = normalize(vec3(gl_Vertex.x, gl_Vertex.y, -1.0) * rot);\n"
+  "  Ray ray = Ray(cameraPos, rayDir);                                 \n"
+  "  gl_Position = gl_Vertex;                                          \n"
+  "  gl_FrontColor = trace1(ray);                                      \n"
+  "}                                                                   \n";
+
+static GLuint vertShader;
+static GLuint program;
+
+
+static void
+Draw(void)
+{
+  const float w = 0.5F * WinWidth;
+  const float h = 0.5F * WinHeight;
+  int x,y;
+
+  float rot[9] = {1,0,0,  0,1,0,   0,0,1};
+  GLint location = glGetUniformLocation(program, "rot");
+  //printf("loc=%d\n", location);
+
+  glUseProgram(program);
+  glUniformMatrix3fv(location, 1, 0, rot);
+  glBegin(GL_POINTS);
+  for(y = 0; y < WinHeight; y++)
+  {
+    for(x = 0; x < WinWidth; x++)
+    {
+      const float posx = x / w - 1.0F;
+      const float posy = y / h - 1.0F;
+      glVertex2f(posx, posy);
+    }
+  }
+  glEnd();
+  glUseProgram(0);
+
+  glutSwapBuffers();
+
+  static int frames = 0;
+  static int t0 = 0;
+  static int t1 = 0;
+  frames++;
+  t1 = glutGet(GLUT_ELAPSED_TIME);
+  float dt = (float)(t1-t0)/1000.0F;
+  if(dt >= 5.0F)
+  {
+    float fps = (float)frames / dt;
+    printf("%f FPS (%d frames in %f seconds)\n", fps, frames, dt);
+    frames = 0;
+    t0 = t1;
+  }
+}
+
+
+static void
+Reshape(int width, int height)
+{
+  WinWidth = width;
+  WinHeight = height;
+  glViewport(0, 0, width, height);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+}
+
+
+static void
+Key(unsigned char key, int x, int y)
+{
+  if(key == 27)
+  {
+    glutDestroyWindow(Win);
+    exit(0);
+  }
+  glutPostRedisplay();
+}
+
+
+static
+void
+drag(int x, int y)
+{
+  if(mouseGrabbed)
+  {
+    printf("%4d %4d\n", x, y);
+  }
+}
+
+
+static
+void
+mouse(int button, int state, int x, int y)
+{
+  if(state == GLUT_DOWN)
+  {
+    mouseGrabbed = !mouseGrabbed;
+  }
+}
+
+
+static void
+Init(void)
+{
+  glDisable(GL_DEPTH_TEST);
+
+  if(!ShadersSupported())
+  {
+    fprintf(stderr, "Shaders are not supported!\n");
+    exit(-1);
+  }
+
+  vertShader = CompileShaderText(GL_VERTEX_SHADER, vsSource);
+  program = LinkShaders(vertShader, 0);
+  glUseProgram(0);
+
+  if(glGetError() != 0)
+  {
+    fprintf(stderr, "Shaders were not loaded!\n");
+    exit(-1);
+  }
+
+  if(!glIsShader(vertShader))
+  {
+    fprintf(stderr, "Vertex shader failed!\n");
+    exit(-1);
+  }
+
+  if(!glIsProgram(program))
+  {
+    fprintf(stderr, "Shader program failed!\n");
+    exit(-1);
+  }
+
+  printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
+}
+
+
+int
+main(int argc, char *argv[])
+{
+//  setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
+  glutInit(&argc, argv);
+  glutInitWindowSize(WinWidth, WinHeight);
+  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
+  Win = glutCreateWindow(argv[0]);
+  glewInit();
+  glutReshapeFunc(Reshape);
+  glutKeyboardFunc(Key);
+  glutDisplayFunc(Draw);
+  glutIdleFunc(Draw);
+  glutMouseFunc(mouse);
+  glutMotionFunc(drag);
+  Init();
+  glutMainLoop();
+  return 0;
+}
+