From 90cd690843d3c4d67a0961683b3a7a6e1d7bc6ed Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Sun, 13 Apr 2008 16:52:15 +0000 Subject: [PATCH] Added Inertia's Julia Fractal example. --- Source/Examples/Data/JuliaColorTable.bmp | Bin 0 -> 104 bytes .../Data/Shaders/JuliaSet_SM2_FS.glsl | 66 +++++ .../Data/Shaders/JuliaSet_SM3_FS.glsl | 41 +++ Source/Examples/Data/Shaders/JuliaSet_VS.glsl | 4 + Source/Examples/OpenGL/T11_Julia_Set.cs | 277 ++++++++++++++++++ 5 files changed, 388 insertions(+) create mode 100644 Source/Examples/Data/JuliaColorTable.bmp create mode 100644 Source/Examples/Data/Shaders/JuliaSet_SM2_FS.glsl create mode 100644 Source/Examples/Data/Shaders/JuliaSet_SM3_FS.glsl create mode 100644 Source/Examples/Data/Shaders/JuliaSet_VS.glsl create mode 100644 Source/Examples/OpenGL/T11_Julia_Set.cs diff --git a/Source/Examples/Data/JuliaColorTable.bmp b/Source/Examples/Data/JuliaColorTable.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d99204f99e5121ab93f0b22ccc56293e622b1eef GIT binary patch literal 104 zcmZ?r&0v54Ga#h_!~#If2*wgX5(0#{85lqq2BvWZtd#USWaM`{IPhJ0$oDC}Usu|_ j-J$d3i2S{a@{jHty?PV!`FripKg<67JM-`V3kC)NSuh|N literal 0 HcmV?d00001 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 + } +}