diff options
-rw-r--r-- | progs/glsl/Makefile | 4 | ||||
-rw-r--r-- | progs/glsl/fsraytrace.c | 385 | ||||
-rw-r--r-- | progs/glsl/vsraytrace.c | 363 |
3 files changed, 751 insertions, 1 deletions
diff --git a/progs/glsl/Makefile b/progs/glsl/Makefile index 3b5a5959aee..6030c8002f5 100644 --- a/progs/glsl/Makefile +++ b/progs/glsl/Makefile @@ -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 index 00000000000..dca8fd2db00 --- /dev/null +++ b/progs/glsl/fsraytrace.c @@ -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 index 00000000000..6e1617a7ebd --- /dev/null +++ b/progs/glsl/vsraytrace.c @@ -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; +} + |