diff --git a/Source/Examples/Data/JuliaColorTable.bmp b/Source/Examples/Data/JuliaColorTable.bmp
new file mode 100644
index 00000000..d99204f9
Binary files /dev/null and b/Source/Examples/Data/JuliaColorTable.bmp differ
diff --git a/Source/Examples/Data/Shaders/JuliaSet_SM2_FS.glsl b/Source/Examples/Data/Shaders/JuliaSet_SM2_FS.glsl
new file mode 100644
index 00000000..8f1da575
--- /dev/null
+++ b/Source/Examples/Data/Shaders/JuliaSet_SM2_FS.glsl
@@ -0,0 +1,66 @@
+#version 110
+// www.OpenTK.net GLSL Julia Set (c) 2008 Christoph Brandtner
+
+// uniforms from OpenGL
+uniform sampler1D COLORTABLE;
+uniform float CETX;
+uniform float CETY;
+uniform float SCALINGX;
+uniform float SCALINGY;
+uniform float OFFSETX;
+uniform float OFFSETY;
+
+// GLSL internal variables.
+const int MAXIterations = 16; // must be greater than zero, 16 is a good blend between detail and speed
+float XPos;
+float YPos;
+float XQuad;
+float YQuad; // half precision floating point could be used on those 4 floats for speed, but will throw a warning.
+int TableIndex;
+int LoopCount;
+
+// this function reduces duplicate code
+void Iterate(void)
+{
+ YPos = 2.0 * XPos * YPos + CETY;
+ XPos = XQuad - YQuad + CETX;
+ XQuad = pow(XPos, 2.0);
+ YQuad = pow(YPos, 2.0);
+ TableIndex++;
+ if ( (XQuad + YQuad) > 4.0 ) LoopCount = MAXIterations; // skip further iterations for this Pixel
+ LoopCount++;
+}
+
+// Shader entry point, this is executed per Pixel
+void main(void)
+{
+ XPos = gl_FragCoord.x / SCALINGX - OFFSETX;
+ YPos = gl_FragCoord.y / SCALINGY - OFFSETY;
+ XQuad = pow(XPos, 2.0);
+ YQuad = pow(YPos, 2.0);
+ TableIndex = -1;
+ LoopCount = 0;
+ // the loop is unrolled for SM 2.0 compatibility
+ if ( LoopCount <= MAXIterations ) Iterate(); // TableIndex==0
+ if ( LoopCount > 1 ) discard; // attempt to early-out, will affect ~1/3 of all Pixels
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate();
+ if ( LoopCount <= MAXIterations ) Iterate(); // TableIndex==16
+ float FinalTableIndex = float( TableIndex ) / float( MAXIterations );
+
+ gl_FragColor = texture1D( COLORTABLE, FinalTableIndex ); // lookup texture for output
+// gl_FragColor.rgb = vec3(FinalTableIndex); // Debug: output greyscale
+}
\ No newline at end of file
diff --git a/Source/Examples/Data/Shaders/JuliaSet_SM3_FS.glsl b/Source/Examples/Data/Shaders/JuliaSet_SM3_FS.glsl
new file mode 100644
index 00000000..cf619a5a
--- /dev/null
+++ b/Source/Examples/Data/Shaders/JuliaSet_SM3_FS.glsl
@@ -0,0 +1,41 @@
+#version 120
+// www.OpenTK.net GLSL Julia Set (c) 2008 Christoph Brandtner
+
+uniform sampler1D COLORTABLE;
+uniform float CETX;
+uniform float CETY;
+uniform float SCALINGX;
+uniform float SCALINGY;
+uniform float OFFSETX;
+uniform float OFFSETY;
+
+const int MAXIterations = 32; // *must* be > 0
+
+void main(void)
+{
+ float XPos = gl_FragCoord.x / SCALINGX - OFFSETX;
+ float YPos = gl_FragCoord.y / SCALINGY - OFFSETY;
+ float XQuad = pow( XPos, 2.0 );
+ float YQuad = pow( YPos, 2.0 );
+ int TableIndex = -1;
+ int LoopCount = 0;
+ while ( LoopCount <= MAXIterations )
+ {
+ YPos = 2.0 * XPos * YPos + CETY;
+ XPos = XQuad - YQuad + CETX;
+ XQuad = pow( XPos, 2.0 );
+ YQuad = pow( YPos, 2.0 );
+ TableIndex++;
+ if ( (XQuad + YQuad) > 4.0 )
+ {
+ if (TableIndex == 0)
+ discard;
+ LoopCount = MAXIterations;
+ }
+ LoopCount++;
+ }
+ float FinalTableIndex = float( TableIndex ) / float( MAXIterations );
+
+ gl_FragColor = texture1D( COLORTABLE, FinalTableIndex ); // lookup texture for output
+ // gl_FragColor.rgb = vec3( FinalTableIndex ); // Debug: output greyscale
+}
\ No newline at end of file
diff --git a/Source/Examples/Data/Shaders/JuliaSet_VS.glsl b/Source/Examples/Data/Shaders/JuliaSet_VS.glsl
new file mode 100644
index 00000000..63b25edf
--- /dev/null
+++ b/Source/Examples/Data/Shaders/JuliaSet_VS.glsl
@@ -0,0 +1,4 @@
+void main(void)
+{
+ gl_Position = ftransform(); // gl_ModelViewProjectionMatrix * gl_Vertex;
+}
\ No newline at end of file
diff --git a/Source/Examples/OpenGL/T11_Julia_Set.cs b/Source/Examples/OpenGL/T11_Julia_Set.cs
new file mode 100644
index 00000000..f4fc2427
--- /dev/null
+++ b/Source/Examples/OpenGL/T11_Julia_Set.cs
@@ -0,0 +1,277 @@
+#region --- License ---
+/* Copyright (c) 2008 Christoph Brandtner
+ * See license.txt for license info
+ */
+#endregion
+
+using System;
+using System.Windows.Forms;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace Examples.Tutorial
+{
+ ///
+ /// Demonstrates how to render an animated Julia Set in real-time. Quality is sacrificed for speed.
+ /// Info about the fractal: http://en.wikipedia.org/wiki/Julia_set
+ /// One more major optimization could be applied (exploit the symmetry of the image with RTT), but
+ /// that would make the program alot more complicated to follow. You can do this as an exercise.
+ ///
+ [Example("GLSL Animated Julia Set", ExampleCategory.GLSL)]
+ public class T11_Julia_Set : GameWindow
+ {
+ public T11_Julia_Set()
+ : base(512, 512)
+ {
+ }
+
+ #region private Fields
+ // GLSL Objects
+ int VertexShaderObject, FragmentShaderObject, ProgramObject;
+ int TextureObject;
+
+ // Julia Variables for animation
+ float AnimOffsetX = 0.213f; // using non-zero as starting point to make it more interesting
+ float AnimOffsetY = 0.63f;
+
+ const double AnimSpeedX = 0.65; // anim speed scaling is solely used to make the anim more interesting
+ const double AnimSpeedY = 1.05;
+ const double AnimCosinusPercent = 0.85f; // scales the cosinus down to 85% to avoid the (boring) borders
+
+ float UniformScaleFactorX; // fractal horizontal scaling is only affected by window resize
+ float UniformScaleFactorY; // fractal vertical scaling is only affected by window resize
+ float UniformOffsetX = 1.8f; // fractal horizontal offset
+ float UniformOffsetY = 1.8f; // fractal vertical offset
+ #endregion private Fields
+
+ #region OnLoad
+ ///
+ /// Setup OpenGL and load resources here.
+ ///
+ /// Not used.
+ public override void OnLoad(EventArgs e)
+ {
+
+ // Check for necessary capabilities:
+ if (!GL.SupportsExtension("VERSION_2_0"))
+ {
+ MessageBox.Show("You need at least OpenGL 2.0 to run this example. Aborting.",
+ "GLSL not supported", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ this.Exit();
+ }
+
+ this.VSync = VSyncMode.Off;
+
+ GL.Disable(EnableCap.Dither);
+ GL.ClearColor(0.2f, 0f, 0.4f, 0f);
+
+ // declare some variables for tracking which shader did compile, and which texture to use
+ string[] ShaderFilenames = new string[2];
+ ShaderFilenames[0] = "Data/Shaders/JuliaSet_SM3_FS.glsl";
+ ShaderFilenames[1] = "Data/Shaders/JuliaSet_SM2_FS.glsl";
+
+ byte CurrentOption = 0;
+ string LogInfo;
+
+ #region Shaders
+ // Load&Compile Vertex Shader
+ using (StreamReader sr = new StreamReader("Data/Shaders/JuliaSet_VS.glsl"))
+ {
+ VertexShaderObject = GL.CreateShader(ShaderType.VertexShader);
+ GL.ShaderSource(VertexShaderObject, sr.ReadToEnd());
+ GL.CompileShader(VertexShaderObject);
+ }
+
+ GL.GetShaderInfoLog(VertexShaderObject, out LogInfo);
+ if (LogInfo.Length > 0 && !LogInfo.Contains("hardware"))
+ Trace.WriteLine("Vertex Shader Log:\n" + LogInfo);
+ else
+ Trace.WriteLine("Vertex Shader compiled without complaint.");
+
+ // Load&Compile Fragment Shader
+
+
+ FragmentShaderObject = GL.CreateShader(ShaderType.FragmentShader);
+ do
+ {
+ using (StreamReader sr = new StreamReader(ShaderFilenames[CurrentOption]))
+ {
+
+ GL.ShaderSource(FragmentShaderObject, sr.ReadToEnd());
+ GL.CompileShader(FragmentShaderObject);
+ }
+ GL.GetShaderInfoLog(FragmentShaderObject, out LogInfo);
+
+ if (LogInfo.Length > 0 && !LogInfo.Contains("hardware"))
+ Trace.WriteLine("Compiling " + ShaderFilenames[CurrentOption] + " failed!\nLog:\n" + LogInfo);
+ else
+ {
+ Trace.WriteLine("Fragment Shader compiled without complaint.");
+ break;
+ }
+
+ if (++CurrentOption > 1)
+ {
+ MessageBox.Show("Neither SM2 nor SM3 Fragment Shader compiled successfully. Aborting.",
+ "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ this.Exit();
+ }
+ } while (true);
+
+ // Link the Shaders to a usable Program
+ ProgramObject = GL.CreateProgram();
+ GL.AttachShader(ProgramObject, VertexShaderObject);
+ GL.AttachShader(ProgramObject, FragmentShaderObject);
+ GL.LinkProgram(ProgramObject);
+
+ // make current
+ GL.UseProgram(ProgramObject);
+
+ // Flag ShaderObjects for delete when app exits
+ GL.DeleteShader(VertexShaderObject);
+ GL.DeleteShader(FragmentShaderObject);
+ #endregion Shaders
+
+ #region Textures
+ // Load&Bind the 1D texture for color lookups
+ GL.ActiveTexture(TextureUnit.Texture0); // select TMU0
+ GL.GenTextures(1, out TextureObject);
+ GL.TexParameter(TextureTarget.Texture1D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture1D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture1D, TextureParameterName.TextureWrapS, (int)(TextureWrapMode)All.ClampToEdge);
+
+ Bitmap bitmap = new Bitmap("Data/JuliaColorTable.bmp");
+ BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
+ GL.TexImage1D(TextureTarget.Texture1D, 0, PixelInternalFormat.Rgb8, data.Width, 0, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
+ bitmap.UnlockBits(data);
+ #endregion Textures
+ }
+
+ #endregion
+
+ #region OnUnLoad
+
+ public override void OnUnload(EventArgs e)
+ {
+ GL.DeleteTextures(1, ref TextureObject);
+ GL.DeleteProgram(ProgramObject); // implies deleting the previously flagged ShaderObjects
+
+ base.OnUnload(e);
+ }
+
+ #endregion
+
+ #region OnResize
+ ///
+ /// Respond to resize events here.
+ ///
+ /// Contains information on the new GameWindow size.
+ /// There is no need to call the base implementation.
+ protected override void OnResize(OpenTK.Platform.ResizeEventArgs e)
+ {
+ // magic numbers so the fractal almost fits inside the window.
+ // If changing this, also change the -1.6f offset in the fragment shader accordingly.
+ UniformScaleFactorX = Width / (UniformOffsetX * 2f);
+ UniformScaleFactorY = Height / (UniformOffsetY * 2f);
+
+ GL.Viewport(0, 0, Width, Height);
+
+ GL.MatrixMode(MatrixMode.Projection);
+ GL.LoadIdentity();
+ GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 0.1); // 2D setup
+
+ GL.MatrixMode(MatrixMode.Modelview);
+ GL.LoadIdentity();
+
+ base.OnResize(e);
+ }
+
+ #endregion
+
+ #region OnUpdateFrame
+
+ ///
+ /// Add your game logic here.
+ ///
+ /// Contains timing information.
+ /// There is no need to call the base implementation.
+ public override void OnUpdateFrame(UpdateFrameEventArgs e)
+ {
+ base.OnUpdateFrame(e);
+
+ if (Keyboard[OpenTK.Input.Key.Escape])
+ {
+ this.Exit();
+ }
+ }
+
+ #endregion
+
+ #region OnRenderFrame
+
+ ///
+ /// Add your game rendering code here.
+ ///
+ /// Contains timing information.
+ /// There is no need to call the base implementation.
+ public override void OnRenderFrame(RenderFrameEventArgs e)
+ {
+ this.Title = "FPS: " + 1 / e.Time;
+ GL.Clear(ClearBufferMask.ColorBufferBit);
+
+ // advance the animation by elapsed time, scaling is solely used to make the anim more interesting
+ AnimOffsetX += (float)(e.Time * AnimSpeedX);
+ AnimOffsetY += (float)(e.Time * AnimSpeedY);
+
+ // pass uniforms into the fragment shader
+ // first the texture
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "COLORTABLE"), TextureObject);
+ // the rest are floats
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "CETX"), (float)(Math.Cos(AnimOffsetX) * AnimCosinusPercent));
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "CETY"), (float)(Math.Cos(AnimOffsetY) * AnimCosinusPercent));
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "SCALINGX"), UniformScaleFactorX);
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "SCALINGY"), UniformScaleFactorY);
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "OFFSETX"), UniformOffsetX);
+ GL.Uniform1(GL.GetUniformLocation(ProgramObject, "OFFSETY"), UniformOffsetY);
+
+ // Fullscreen quad. Using immediate mode, since this app is fragment shader limited anyways.
+ GL.Begin(BeginMode.Quads);
+ {
+ GL.Vertex3(-1.0f, -1.0f, 0f);
+ GL.Vertex3(1.0f, -1.0f, 0f);
+ GL.Vertex3(1.0f, 1.0f, 0f);
+ GL.Vertex3(-1.0f, 1.0f, 0f);
+ }
+ GL.End();
+
+ this.SwapBuffers();
+ }
+
+ #endregion
+
+ #region public static void Main()
+
+ ///
+ /// Entry point of this example.
+ ///
+ [STAThread]
+ public static void Main()
+ {
+
+ using (T11_Julia_Set example = new T11_Julia_Set())
+ {
+ // Get the title and category of this example using reflection.
+ ExampleAttribute info = ((ExampleAttribute)example.GetType().GetCustomAttributes(false)[0]);
+ example.Title = String.Format("OpenTK | {0} {1}: {2}", info.Category, info.Difficulty, info.Title);
+ example.Run(30.0, 60.0);
+ }
+ }
+
+ #endregion
+ }
+}