// This code was written for the OpenTK library and has been released // to the Public Domain. // It is provided "as is" without express or implied warranty of any kind. using System; using System.Drawing; using System.Collections.Generic; using System.Diagnostics; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; using OpenTK.Input; namespace Examples.Tutorial { [Example( "Anaglyph Stereo", ExampleCategory.OpenGL, "1.x", Documentation = "Anaglyph" )] class Anaglyph : GameWindow { Examples.Shapes.DrawableShape Object; /// Creates a 800x600 window with the specified title. public Anaglyph() : base(800, 600, GraphicsMode.Default, "OpenTK Quick Start Sample", GameWindowFlags.Default, DisplayDevice.Default, 3, 1, GraphicsContextFlags.Default) { VSync = VSyncMode.On; } /// Load resources here. /// Not used. protected override void OnLoad(EventArgs e) { base.OnLoad(e); GL.ClearColor(System.Drawing.Color.Black); GL.Enable(EnableCap.DepthTest); GL.Enable( EnableCap.Lighting ); GL.Enable( EnableCap.Light0 ); Object = new Examples.Shapes.MengerSponge(1.0, Shapes.MengerSponge.eSubdivisions.Two, true ); // Object = new Examples.Shapes.TorusKnot( 256, 32, 0.1, 3, 4, 1, true ); } protected override void OnUnload( EventArgs e ) { base.OnUnload( e ); Object.Dispose(); } /// /// Called when your window is resized. Set your viewport here. It is also /// a good place to set up your projection matrix (which probably changes /// along when the aspect ratio of your window). /// /// Not used. protected override void OnResize(EventArgs e) { base.OnResize(e); GL.Viewport(ClientRectangle); } /// /// Called when it is time to setup the next frame. Add you game logic here. /// /// Contains timing information for framerate independent logic. protected override void OnUpdateFrame(FrameEventArgs e) { base.OnUpdateFrame(e); var keyboard = OpenTK.Input.Keyboard.GetState(); if (keyboard[Key.Escape]) Exit(); } struct Camera { public Vector3 Position, Direction, Up; public double NearPlane, FarPlane; public double EyeSeparation; public double Aperture; // FOV in degrees public double FocalLength; } enum Eye { left, right, } void SetupCamera( Eye eye ) { Camera camera; camera.Position = Vector3.UnitZ; camera.Up = Vector3.UnitY; camera.Direction = -Vector3.UnitZ; camera.NearPlane = 1.0; camera.FarPlane = 5.0; camera.FocalLength = 2.0; camera.EyeSeparation = camera.FocalLength / 30.0; camera.Aperture = 75.0; double left, right, bottom, top; double widthdiv2 = camera.NearPlane * Math.Tan( MathHelper.DegreesToRadians( (float)( camera.Aperture / 2.0 ) ) ); // aperture in radians double precalc1 = ClientRectangle.Width / (double)ClientRectangle.Height * widthdiv2; double precalc2 = 0.5 * camera.EyeSeparation * camera.NearPlane / camera.FocalLength; Vector3 Right = Vector3.Cross( camera.Direction, camera.Up ); // Each unit vectors Right.Normalize(); Right.X *= (float)( camera.EyeSeparation / 2.0 ); Right.Y *= (float)( camera.EyeSeparation / 2.0 ); Right.Z *= (float)( camera.EyeSeparation / 2.0 ); // Projection Matrix top = widthdiv2; bottom = -widthdiv2; if ( eye == Eye.right ) { left = -precalc1 - precalc2; right = precalc1 - precalc2; } else { left = -precalc1 + precalc2; right = precalc1 + precalc2; } GL.MatrixMode( MatrixMode.Projection ); GL.LoadIdentity(); GL.Frustum( left, right, bottom, top, camera.NearPlane, camera.FarPlane ); // Modelview Matrix Matrix4 modelview; if ( eye == Eye.right ) { modelview = Matrix4.LookAt( new Vector3( camera.Position.X + Right.X, camera.Position.Y + Right.Y, camera.Position.Z + Right.Z ), new Vector3( camera.Position.X + Right.X + camera.Direction.X, camera.Position.Y + Right.Y + camera.Direction.Y, camera.Position.Z + Right.Z + camera.Direction.Z ), camera.Up ); } else { modelview = Matrix4.LookAt( new Vector3( camera.Position.X - Right.X, camera.Position.Y - Right.Y, camera.Position.Z - Right.Z ), new Vector3( camera.Position.X - Right.X + camera.Direction.X, camera.Position.Y - Right.Y + camera.Direction.Y, camera.Position.Z - Right.Z + camera.Direction.Z ), camera.Up ); } GL.MatrixMode( MatrixMode.Modelview ); GL.LoadIdentity(); GL.MultMatrix( ref modelview ); } float Angle; void Draw() { GL.Translate( 0f, 0f, -2f ); GL.Rotate( Angle, Vector3.UnitY ); Object.Draw(); } /// /// Called when it is time to render the next frame. Add your rendering code here. /// /// Contains timing information. protected override void OnRenderFrame( FrameEventArgs e ) { Angle += (float)(e.Time *20.0); GL.Clear( ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit ); SetupCamera( Eye.right ); GL.ColorMask( true, false, false, true ); Draw(); GL.Clear( ClearBufferMask.DepthBufferBit ); // SetupCamera( Eye.left ); GL.ColorMask( false, true, true, true ); Draw(); GL.ColorMask( true, true, true, true ); SwapBuffers(); } /// /// The main entry point for the application. /// [STAThread] static void Main() { // The 'using' idiom guarantees proper resource cleanup. // We request 30 UpdateFrame events per second, and unlimited // RenderFrame events (as fast as the computer can handle). using (Anaglyph game = new Anaglyph()) { game.Run(10.0); } } } }