Opentk/Source/Examples/OpenGL/2.x/SwizzledParallax.cs
2014-07-21 17:20:19 +02:00

381 lines
15 KiB
C#

// 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.Diagnostics;
using System.Drawing;
using System.IO;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using Examples.TextureLoaders;
namespace Examples.Tutorial
{
/// <summary>Demonstrates Swizzled DXT5 Parallax Mapping
/// The idea is described in more detail right here: http://www.opentk.com/node/394
/// </summary>
[Example("Swizzled Parallax Mapping", ExampleCategory.OpenGL, "2.x", Documentation = "SwizzledParallax")]
public class T12_GLSL_Parallax: GameWindow
{
public T12_GLSL_Parallax( )
: base( 800, 600 )
{
}
#region internal Fields
// Shader
int VertexShaderObject, FragmentShaderObject, ProgramObject;
const string VertexShaderFilename = "Data/Shaders/Parallax_VS.glsl";
const string FragmentShaderFilename = "Data/Shaders/Parallax_FS.glsl";
const int AttribTangent = 5; // slot where to pass tangents to VS, not sure which are reserved besides 0
Vector3 Tangent = new Vector3( 1f, 0f, 0f );
Vector3 Normal = new Vector3( 0f, 0f, 1f );
// Material parameter
//Vector3 MaterialScaleAndBiasAndShininess = new Vector3( 0.07f, 0.0f, 38.0f ); // for Metal tex
Vector3 MaterialScaleAndBiasAndShininess = new Vector3( 0.04f, 0.0f, 92.0f ); // for Rock tex
// Textures
const TextureUnit TMU0_Unit = TextureUnit.Texture0;
const int TMU0_UnitInteger = 0;
const string TMU0_Filename = "Data/Textures/swizzled-rock-diffuse-height.dds";
uint TMU0_Handle;
TextureTarget TMU0_Target;
const TextureUnit TMU1_Unit = TextureUnit.Texture1;
const int TMU1_UnitInteger = 1;
const string TMU1_Filename = "Data/Textures/swizzled-rock-normal-gloss.dds";
uint TMU1_Handle;
TextureTarget TMU1_Target;
// Camera
Vector3 EyePos = new Vector3( 0.0f, 0.0f, 3.0f );
// Light
Vector3 LightPosition = new Vector3( 0.0f, 1.0f, 1.0f );
Vector3 LightDiffuse = new Vector3( 0.5f, 0.5f, 0.5f );
Vector3 LightSpecular = new Vector3( 1f, 1f, 1f );
#endregion internal Fields
/// <summary>Setup OpenGL and load resources here.</summary>
/// <param name="e">Not used.</param>
protected override void OnLoad(EventArgs e)
{
this.VSync = VSyncMode.Off;
// Check for necessary capabilities:
string extensions = GL.GetString(StringName.Extensions);
if ( !GL.GetString(StringName.Extensions).Contains("GL_ARB_shading_language"))
{
throw new NotSupportedException(String.Format("This example requires OpenGL 2.0. Found {0}. Aborting.",
GL.GetString(StringName.Version).Substring(0, 3)));
}
if (!extensions.Contains("GL_ARB_texture_compression") ||
!extensions.Contains("GL_EXT_texture_compression_s3tc"))
{
throw new NotSupportedException("This example requires support for texture compression. Aborting.");
}
int[] temp = new int[1];
GL.GetInteger( GetPName.MaxTextureImageUnits, out temp[0] );
Trace.WriteLine( temp[0] + " TMU's for Fragment Shaders found. (2 required)" );
GL.GetInteger( GetPName.MaxVaryingFloats, out temp[0] );
Trace.WriteLine( temp[0] + " varying floats between VS and FS allowed. (6 required)" );
GL.GetInteger( GetPName.MaxVertexUniformComponents, out temp[0] );
Trace.WriteLine( temp[0] + " uniform components allowed in Vertex Shader. (6 required)" );
GL.GetInteger( GetPName.MaxFragmentUniformComponents, out temp[0] );
Trace.WriteLine( temp[0] + " uniform components allowed in Fragment Shader. (11 required)" );
Trace.WriteLine("" );
#region GL State
GL.ClearColor( 0.2f, 0f, 0.4f, 0f );
GL.PointSize( 10f );
GL.Disable( EnableCap.Dither );
GL.FrontFace( FrontFaceDirection.Ccw );
GL.PolygonMode( MaterialFace.Front, PolygonMode.Fill );
GL.PolygonMode( MaterialFace.Back, PolygonMode.Line );
#endregion GL State
#region Shaders
string LogInfo;
// Load&Compile Vertex Shader
using ( StreamReader sr = new StreamReader( VertexShaderFilename ) )
{
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 failed!\nLog:\n" + LogInfo );
else
Trace.WriteLine( "Vertex Shader compiled without complaint." );
// Load&Compile Fragment Shader
using ( StreamReader sr = new StreamReader( FragmentShaderFilename ) )
{
FragmentShaderObject = GL.CreateShader( ShaderType.FragmentShader );
GL.ShaderSource( FragmentShaderObject, sr.ReadToEnd( ) );
GL.CompileShader( FragmentShaderObject );
}
GL.GetShaderInfoLog( FragmentShaderObject, out LogInfo );
if ( LogInfo.Length > 0 && !LogInfo.Contains( "hardware" ) )
Trace.WriteLine( "Fragment Shader failed!\nLog:\n" + LogInfo );
else
Trace.WriteLine( "Fragment Shader compiled without complaint." );
// Link the Shaders to a usable Program
ProgramObject = GL.CreateProgram( );
GL.AttachShader( ProgramObject, VertexShaderObject );
GL.AttachShader( ProgramObject, FragmentShaderObject );
// must bind the attribute before linking
GL.BindAttribLocation( ProgramObject, AttribTangent, "AttributeTangent" );
// link it all together
GL.LinkProgram( ProgramObject );
// flag ShaderObjects for delete when not used anymore
GL.DeleteShader( VertexShaderObject );
GL.DeleteShader( FragmentShaderObject );
GL.GetProgram(ProgramObject, GetProgramParameterName.LinkStatus, out temp[0]);
Trace.WriteLine( "Linking Program (" + ProgramObject + ") " + ( ( temp[0] == 1 ) ? "succeeded." : "FAILED!" ) );
if ( temp[0] != 1 )
{
GL.GetProgramInfoLog( ProgramObject, out LogInfo );
Trace.WriteLine( "Program Log:\n" + LogInfo );
}
GL.GetProgram(ProgramObject, GetProgramParameterName.ActiveAttributes, out temp[0]);
Trace.WriteLine( "Program registered " + temp[0] + " Attributes. (Should be 4: Pos, UV, Normal, Tangent)" );
Trace.WriteLine( "Tangent attribute bind location: " + GL.GetAttribLocation( ProgramObject, "AttributeTangent" ) );
Trace.WriteLine( "End of Shader build. GL Error: " + GL.GetError( ) );
#endregion Shaders
#region Textures
TextureLoaderParameters.MagnificationFilter = TextureMagFilter.Linear;
TextureLoaderParameters.MinificationFilter = TextureMinFilter.LinearMipmapLinear;
TextureLoaderParameters.WrapModeS = TextureWrapMode.ClampToBorder;
TextureLoaderParameters.WrapModeT = TextureWrapMode.ClampToBorder;
TextureLoaderParameters.EnvMode = TextureEnvMode.Modulate;
ImageDDS.LoadFromDisk( TMU0_Filename, out TMU0_Handle, out TMU0_Target );
Trace.WriteLine( "Loaded " + TMU0_Filename + " with handle " + TMU0_Handle + " as " + TMU0_Target );
ImageDDS.LoadFromDisk( TMU1_Filename, out TMU1_Handle, out TMU1_Target );
Trace.WriteLine( "Loaded " + TMU1_Filename + " with handle " + TMU1_Handle + " as " + TMU1_Target );
#endregion Textures
Trace.WriteLine( "End of Texture Loading. GL Error: " + GL.GetError( ) );
Trace.WriteLine( "");
}
protected override void OnUnload(EventArgs e)
{
GL.DeleteProgram( ProgramObject );
GL.DeleteTextures( 1, ref TMU0_Handle );
GL.DeleteTextures( 1, ref TMU1_Handle );
base.OnUnload( e );
}
/// <summary>Respond to resize events here.</summary>
/// <param name="e">Contains information on the new GameWindow size.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnResize( EventArgs e )
{
GL.Viewport( 0, 0, Width, Height );
GL.MatrixMode( MatrixMode.Projection );
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Width / (float)Height, 0.1f, 100.0f);
GL.LoadMatrix(ref p);
GL.MatrixMode( MatrixMode.Modelview );
GL.LoadIdentity( );
base.OnResize( e );
}
protected override void OnKeyDown(OpenTK.Input.KeyboardKeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Keyboard[OpenTK.Input.Key.Escape])
this.Exit();
if (e.Keyboard[OpenTK.Input.Key.Space])
Trace.WriteLine("GL: " + GL.GetError());
if (e.Keyboard[OpenTK.Input.Key.Q])
{
MaterialScaleAndBiasAndShininess.X += 0.01f;
Trace.WriteLine("Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y);
}
if (e.Keyboard[OpenTK.Input.Key.A])
{
MaterialScaleAndBiasAndShininess.X -= 0.01f;
Trace.WriteLine("Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y);
}
if (e.Keyboard[OpenTK.Input.Key.W])
{
MaterialScaleAndBiasAndShininess.Y += 0.01f;
Trace.WriteLine("Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y);
}
if (e.Keyboard[OpenTK.Input.Key.S])
{
MaterialScaleAndBiasAndShininess.Y -= 0.01f;
Trace.WriteLine("Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y);
}
if (e.Keyboard[OpenTK.Input.Key.E])
{
MaterialScaleAndBiasAndShininess.Z += 1f;
Trace.WriteLine("Shininess: " + MaterialScaleAndBiasAndShininess.Z);
}
if (e.Keyboard[OpenTK.Input.Key.D])
{
MaterialScaleAndBiasAndShininess.Z -= 1f;
Trace.WriteLine("Shininess: " + MaterialScaleAndBiasAndShininess.Z);
}
}
protected override void OnMouseMove(OpenTK.Input.MouseMoveEventArgs e)
{
base.OnMouseMove(e);
LightPosition.X = (-(this.Width / 2) + e.Mouse.X) / 100f;
LightPosition.Y = ((this.Height / 2) - e.Mouse.Y) / 100f;
EyePos.Y = e.Mouse.Wheel * 0.5f;
}
/// <summary>Add your game logic here.</summary>
/// <param name="e">Contains timing information.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnUpdateFrame( FrameEventArgs e )
{
base.OnUpdateFrame( e );
}
/// <summary>Add your game rendering code here.</summary>
/// <param name="e">Contains timing information.</param>
/// <remarks>There is no need to call the base implementation.</remarks>
protected override void OnRenderFrame( FrameEventArgs e )
{
this.Title = "FPS: " + (1 / e.Time).ToString("0.");
GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
GL.UseProgram( ProgramObject );
#region Textures
GL.ActiveTexture( TMU0_Unit );
GL.BindTexture( TMU0_Target, TMU0_Handle );
GL.ActiveTexture( TMU1_Unit );
GL.BindTexture( TMU1_Target, TMU1_Handle );
#endregion Textures
#region Uniforms
// first Material's uniforms
GL.Uniform1( GL.GetUniformLocation( ProgramObject, "Material_DiffuseAndHeight" ), TMU0_UnitInteger );
GL.Uniform1( GL.GetUniformLocation( ProgramObject, "Material_NormalAndGloss" ), TMU1_UnitInteger );
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Material_ScaleBiasShininess" ), MaterialScaleAndBiasAndShininess.X, MaterialScaleAndBiasAndShininess.Y, MaterialScaleAndBiasAndShininess.Z );
// the rest are vectors
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Camera_Position" ), EyePos.X, EyePos.Y, EyePos.Z );
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_Position" ), LightPosition.X, LightPosition.Y, LightPosition.Z );
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_DiffuseColor" ), LightDiffuse.X, LightDiffuse.Y, LightDiffuse.Z );
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_SpecularColor" ), LightSpecular.X, LightSpecular.Y, LightSpecular.Z );
#endregion Uniforms
GL.PushMatrix( );
Matrix4 t = Matrix4.LookAt( EyePos, Vector3.Zero, Vector3.UnitY );
GL.MultMatrix(ref t);
#region Draw Quad
GL.Color3( 1f, 1f, 1f );
GL.Begin(PrimitiveType.Quads);
{
GL.Normal3( Normal );
GL.VertexAttrib3( AttribTangent, ref Tangent );
GL.MultiTexCoord2( TextureUnit.Texture0, 0f, 1f );
GL.Vertex3( -1.0f, 1.0f, 0.0f );
GL.Normal3( Normal );
GL.VertexAttrib3( AttribTangent, ref Tangent );
GL.MultiTexCoord2( TextureUnit.Texture0, 0f, 0f );
GL.Vertex3( -1.0f, -1.0f, 0.0f );
GL.Normal3( Normal );
GL.VertexAttrib3( AttribTangent, ref Tangent );
GL.MultiTexCoord2( TextureUnit.Texture0, 1f, 0f );
GL.Vertex3( 1.0f, -1.0f, 0.0f );
GL.Normal3( Normal );
GL.VertexAttrib3( AttribTangent, ref Tangent );
GL.MultiTexCoord2( TextureUnit.Texture0, 1f, 1f );
GL.Vertex3( 1.0f, 1.0f, 0.0f );
}
GL.End( );
#endregion Draw Quad
GL.UseProgram( 0 );
// visualize the light position 'somehow'
GL.Begin(PrimitiveType.Points);
{
GL.Color3( LightSpecular );
GL.Vertex3( LightPosition );
}
GL.End( );
GL.PopMatrix( );
this.SwapBuffers( );
}
/// <summary>Entry point</summary>
[STAThread]
public static void Main( )
{
using ( T12_GLSL_Parallax example = new T12_GLSL_Parallax( ) )
{
Utilities.SetWindowTitle( example );
example.Run( 30.0, 0.0 );
}
}
}
}