diff --git a/Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj b/Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj
index 36f3f3c..ba75629 100644
--- a/Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj
+++ b/Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj
@@ -3,10 +3,11 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
-
+
+
diff --git a/Chapter1/1-CreatingAWindow/Program.cs b/Chapter1/1-CreatingAWindow/Program.cs
index adc2bd3..87306af 100644
--- a/Chapter1/1-CreatingAWindow/Program.cs
+++ b/Chapter1/1-CreatingAWindow/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Creating a Window",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/2-HelloTriangle/2-HelloTriangle.csproj b/Chapter1/2-HelloTriangle/2-HelloTriangle.csproj
index e968f4c..56a56ae 100644
--- a/Chapter1/2-HelloTriangle/2-HelloTriangle.csproj
+++ b/Chapter1/2-HelloTriangle/2-HelloTriangle.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,19 +11,7 @@
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
+
+
diff --git a/Chapter1/2-HelloTriangle/Program.cs b/Chapter1/2-HelloTriangle/Program.cs
index c3e9037..db9ff3a 100644
--- a/Chapter1/2-HelloTriangle/Program.cs
+++ b/Chapter1/2-HelloTriangle/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Creating a Window",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/2-HelloTriangle/Shaders/shader.frag b/Chapter1/2-HelloTriangle/Shaders/shader.frag
index db749de..6d69b56 100644
--- a/Chapter1/2-HelloTriangle/Shaders/shader.frag
+++ b/Chapter1/2-HelloTriangle/Shaders/shader.frag
@@ -4,5 +4,6 @@ out vec4 outputColor;
void main()
{
+ // Set the pixel color to RGB (1, 1, 0), a yellow color.
outputColor = vec4(1.0, 1.0, 0.0, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/2-HelloTriangle/Shaders/shader.vert b/Chapter1/2-HelloTriangle/Shaders/shader.vert
index e9e0e7b..6ebfb4e 100644
--- a/Chapter1/2-HelloTriangle/Shaders/shader.vert
+++ b/Chapter1/2-HelloTriangle/Shaders/shader.vert
@@ -18,14 +18,14 @@
// shader(vertex)
-// This defines our input variable, aPosition.
+// This defines our input variable, aPos.
// It starts with the line "layout(location = 0)". This defines where this input variable will be located, which is needed for GL.VertexAttribPointer.
-// However, you can omit it, and replace this with just "in vec3 aPosition". If you do that, you'll have to replace the 0 in GL.VertexAttribPointer with
+// However, you can omit it, and replace this with just "in vec3 aPos". If you do that, you'll have to replace the 0 in GL.VertexAttribPointer with
// a call to GL.GetAttribLocation(shaderHandle, attributeName)
// Next, the keyword "in" defines this as an input variable. We'll have an example of the "out" keyword in the next tutorial.
// Then, the keyword "vec3" means this is a vector with 3 floats inside.
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
// Like C, we have an entrypoint function. In this case, it takes void and returns void, and must be named main.
@@ -37,5 +37,5 @@ layout(location = 0) in vec3 aPosition;
void main(void)
{
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/2-HelloTriangle/Window.cs b/Chapter1/2-HelloTriangle/Window.cs
index 133aa2d..a5312b5 100644
--- a/Chapter1/2-HelloTriangle/Window.cs
+++ b/Chapter1/2-HelloTriangle/Window.cs
@@ -1,8 +1,9 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -34,7 +35,7 @@ public class Window : GameWindow
// This class is a wrapper around a shader, which helps us manage it.
// The shader class's code is in the Common project.
// What shaders are and what they're used for will be explained later in this tutorial.
- private Shader _shader;
+ private int _shader;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -112,13 +113,13 @@ protected override void OnLoad()
// Modern OpenGL makes this pipeline very free, giving us a lot of freedom on how vertices are turned to pixels.
// The drawback is that we actually need two more programs for this! These are called "shaders".
// Shaders are tiny programs that live on the GPU. OpenGL uses them to handle the vertex-to-pixel pipeline.
- // Check out the Shader class in Common to see how we create our shaders, as well as a more in-depth explanation of how shaders work.
+ // Check out the CompileProgram function to see how we create our shaders, as well as a more in-depth explanation of how shaders work.
// shader.vert and shader.frag contain the actual shader code.
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
// Now, enable the shader.
// Just like the VBO, this is global, so every function that uses a shader will modify this one until a new one is bound instead.
- _shader.Use();
+ GL.UseProgram(_shader);
// Setup is now complete! Now we move to the OnRenderFrame function to finally draw the triangle.
}
@@ -140,7 +141,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
// and then calling an OpenGL function to render.
// Bind the shader
- _shader.Use();
+ GL.UseProgram(_shader);
// Bind the VAO
GL.BindVertexArray(_vertexArrayObject);
@@ -176,13 +177,13 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
// When the window gets resized, we have to call GL.Viewport to resize OpenGL's viewport to match the new size.
// If we don't, the NDC will no longer be correct.
- GL.Viewport(0, 0, Size.X, Size.Y);
+ GL.Viewport(0, 0, e.Width, e.Height);
}
// Now, for cleanup.
@@ -197,7 +198,7 @@ protected override void OnResize(ResizeEventArgs e)
// longer used for whatever reason (e.g. a new scene is loaded that doesn't use a texture).
// This would free up video ram (VRAM) that can be used for new textures.
//
- // The coming chapters will not have this code.
+ // The coming chapters will not have this cleanup code.
protected override void OnUnload()
{
// Unbind all the resources by binding the targets to 0/null.
@@ -209,9 +210,70 @@ protected override void OnUnload()
GL.DeleteBuffer(_vertexBufferObject);
GL.DeleteVertexArray(_vertexArrayObject);
- GL.DeleteProgram(_shader.Handle);
+ GL.DeleteProgram(_shader);
base.OnUnload();
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ // Compile vertex shader.
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+
+ // We do the same for the fragment shader.
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ // These two shaders must then be merged into a shader program, which can then be used by OpenGL.
+ // To do this, create a program...
+ int handle = GL.CreateProgram();
+
+ // Attach both shaders...
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ // And then link them together.
+ GL.LinkProgram(handle);
+
+ // Check for linking errors
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetProgramInfoLog(program)` to get information about the error.
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ // When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
+ // Detach them, and then delete them.
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ // GL.CreateShader will create an empty shader. The ShaderType enum denotes which type of shader will be created.
+ int shader = GL.CreateShader(type);
+
+ // Upload the GLSL source code.
+ GL.ShaderSource(shader, source);
+
+ // Try to compile the shader
+ GL.CompileShader(shader);
+
+ // Check for compilation errors
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
}
}
diff --git a/Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj b/Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj
index 25afd09..56a56ae 100644
--- a/Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj
+++ b/Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/3-ElementBufferObjects/Program.cs b/Chapter1/3-ElementBufferObjects/Program.cs
index d5d1fe0..3e9c242 100644
--- a/Chapter1/3-ElementBufferObjects/Program.cs
+++ b/Chapter1/3-ElementBufferObjects/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Element Buffer Objects",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/3-ElementBufferObjects/Shaders/shader.vert b/Chapter1/3-ElementBufferObjects/Shaders/shader.vert
index 42a0e96..0a3cec8 100644
--- a/Chapter1/3-ElementBufferObjects/Shaders/shader.vert
+++ b/Chapter1/3-ElementBufferObjects/Shaders/shader.vert
@@ -1,8 +1,8 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
void main(void)
{
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/3-ElementBufferObjects/Window.cs b/Chapter1/3-ElementBufferObjects/Window.cs
index 012f8d8..d98b245 100644
--- a/Chapter1/3-ElementBufferObjects/Window.cs
+++ b/Chapter1/3-ElementBufferObjects/Window.cs
@@ -1,8 +1,9 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -38,7 +39,7 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
// Add a handle for the EBO
private int _elementBufferObject;
@@ -76,8 +77,8 @@ protected override void OnLoad()
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
// The EBO has now been properly setup. Go to the Render function to see how we draw our rectangle now!
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -86,7 +87,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.Clear(ClearBufferMask.ColorBufferBit);
- _shader.Use();
+ GL.UseProgram(_shader);
// Because ElementArrayObject is a property of the currently bound VAO,
// the buffer you will find in the ElementArrayBuffer will change with the currently bound VAO.
@@ -115,10 +116,56 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
{
- base.OnResize(e);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj b/Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj
index 25afd09..56a56ae 100644
--- a/Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj
+++ b/Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/4-Shaders-InsAndOuts/Program.cs b/Chapter1/4-Shaders-InsAndOuts/Program.cs
index 33574f3..7e6569b 100644
--- a/Chapter1/4-Shaders-InsAndOuts/Program.cs
+++ b/Chapter1/4-Shaders-InsAndOuts/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Shaders In and Outs!",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert b/Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert
index cb39b32..c87b6e7 100644
--- a/Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert
+++ b/Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
// the position variable has attribute position 0
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
// This variable uses the keyword out in order to pass on the value to the
// next shader down in the chain, in this case the frag shader
@@ -10,7 +10,7 @@ out vec4 vertexColor;
void main(void)
{
// see how we directly give a vec3 to vec4's constructor
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
// Here we assign the variable a dark red color to the out variable
vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
diff --git a/Chapter1/4-Shaders-InsAndOuts/Window.cs b/Chapter1/4-Shaders-InsAndOuts/Window.cs
index dd2f4f0..e066ea0 100644
--- a/Chapter1/4-Shaders-InsAndOuts/Window.cs
+++ b/Chapter1/4-Shaders-InsAndOuts/Window.cs
@@ -1,10 +1,10 @@
-using System;
-using OpenTK.Graphics.OpenGL4;
-using LearnOpenTK.Common;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
using System.Diagnostics;
+using System.IO;
namespace LearnOpenTK
@@ -25,7 +25,7 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -56,8 +56,8 @@ protected override void OnLoad()
GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -66,7 +66,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.Clear(ClearBufferMask.ColorBufferBit);
- _shader.Use();
+ GL.UseProgram(_shader);
GL.BindVertexArray(_vertexArrayObject);
@@ -87,11 +87,56 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj b/Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj
index 25afd09..56a56ae 100644
--- a/Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj
+++ b/Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/4-Shaders-MoreAttributes/Program.cs b/Chapter1/4-Shaders-MoreAttributes/Program.cs
index d5150d5..37d395e 100644
--- a/Chapter1/4-Shaders-MoreAttributes/Program.cs
+++ b/Chapter1/4-Shaders-MoreAttributes/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Shaders More Attributes!",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert b/Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert
index 364b96f..ada856b 100644
--- a/Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert
+++ b/Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
// the position variable has attribute position 0
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
// This is where the color values we assigned in the main program goes to
layout(location = 1) in vec3 aColor;
@@ -11,7 +11,7 @@ out vec3 ourColor; // output a color to the fragment shader
void main(void)
{
// see how we directly give a vec3 to vec4's constructor
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
// We use the outColor variable to pass on the color information to the frag shader
ourColor = aColor;
diff --git a/Chapter1/4-Shaders-MoreAttributes/Window.cs b/Chapter1/4-Shaders-MoreAttributes/Window.cs
index 53d8985..a7c81cc 100644
--- a/Chapter1/4-Shaders-MoreAttributes/Window.cs
+++ b/Chapter1/4-Shaders-MoreAttributes/Window.cs
@@ -1,10 +1,10 @@
-using System;
-using OpenTK.Graphics.OpenGL4;
-using LearnOpenTK.Common;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
using System.Diagnostics;
+using System.IO;
namespace LearnOpenTK
{
@@ -28,7 +28,7 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -67,8 +67,8 @@ protected override void OnLoad()
GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -77,7 +77,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.Clear(ClearBufferMask.ColorBufferBit);
- _shader.Use();
+ GL.UseProgram(_shader);
GL.BindVertexArray(_vertexArrayObject);
@@ -98,11 +98,56 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj b/Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj
index 25afd09..56a56ae 100644
--- a/Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj
+++ b/Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/4-Shaders-Uniforms/Program.cs b/Chapter1/4-Shaders-Uniforms/Program.cs
index 43c71c3..582f9c5 100644
--- a/Chapter1/4-Shaders-Uniforms/Program.cs
+++ b/Chapter1/4-Shaders-Uniforms/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Shaders Uniforms!",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/4-Shaders-Uniforms/Shaders/shader.vert b/Chapter1/4-Shaders-Uniforms/Shaders/shader.vert
index bb42695..a86fec7 100644
--- a/Chapter1/4-Shaders-Uniforms/Shaders/shader.vert
+++ b/Chapter1/4-Shaders-Uniforms/Shaders/shader.vert
@@ -1,9 +1,9 @@
#version 330 core
-layout(location = 0) in vec3 aPosition; // the position variable has attribute position 0
+layout(location = 0) in vec3 aPos; // the position variable has attribute position 0
void main(void)
{
// see how we directly give a vec3 to vec4's constructor
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/4-Shaders-Uniforms/Window.cs b/Chapter1/4-Shaders-Uniforms/Window.cs
index ece406d..c74bd02 100644
--- a/Chapter1/4-Shaders-Uniforms/Window.cs
+++ b/Chapter1/4-Shaders-Uniforms/Window.cs
@@ -1,10 +1,11 @@
-using System;
-using System.Diagnostics;
-using OpenTK.Graphics.OpenGL4;
-using LearnOpenTK.Common;
-using OpenTK.Windowing.Desktop;
+using OpenTK.Graphics.OpenGL4;
+using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
+using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
+using System.Diagnostics;
+using System.IO;
namespace LearnOpenTK
{
@@ -29,7 +30,7 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -56,8 +57,8 @@ protected override void OnLoad()
GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
// We start the stopwatch here as this method is only called once.
_timer = new Stopwatch();
@@ -70,7 +71,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.Clear(ClearBufferMask.ColorBufferBit);
- _shader.Use();
+ GL.UseProgram(_shader);
// Here, we get the total seconds that have elapsed since the last time this method has reset
// and we assign it to the timeValue variable so it can be used for the pulsating color.
@@ -83,7 +84,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
// This gets the uniform variable location from the frag shader so that we can
// assign the new green value to it.
- int vertexColorLocation = GL.GetUniformLocation(_shader.Handle, "ourColor");
+ int vertexColorLocation = GL.GetUniformLocation(_shader, "ourColor");
// Here we're assigning the ourColor variable in the frag shader
// via the OpenGL Uniform method which takes in the value as the individual vec values (which total 4 in this instance).
@@ -112,11 +113,56 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
{
- base.OnResize(e);
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
- GL.Viewport(0, 0, Size.X, Size.Y);
+ return shader;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/5-Textures/5-Textures.csproj b/Chapter1/5-Textures/5-Textures.csproj
index b5e0922..c8b95b6 100644
--- a/Chapter1/5-Textures/5-Textures.csproj
+++ b/Chapter1/5-Textures/5-Textures.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/5-Textures/Program.cs b/Chapter1/5-Textures/Program.cs
index 905d56a..1406055 100644
--- a/Chapter1/5-Textures/Program.cs
+++ b/Chapter1/5-Textures/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Textures",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/5-Textures/Shaders/shader.vert b/Chapter1/5-Textures/Shaders/shader.vert
index 494f81d..85c3668 100644
--- a/Chapter1/5-Textures/Shaders/shader.vert
+++ b/Chapter1/5-Textures/Shaders/shader.vert
@@ -1,6 +1,6 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
// We add another input variable for the texture coordinates.
@@ -18,5 +18,5 @@ void main(void)
texCoord = aTexCoord;
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/5-Textures/Window.cs b/Chapter1/5-Textures/Window.cs
index e8739a3..2548dbf 100644
--- a/Chapter1/5-Textures/Window.cs
+++ b/Chapter1/5-Textures/Window.cs
@@ -1,8 +1,10 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -32,10 +34,10 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
// For documentation on this, check Texture.cs.
- private Texture _texture;
+ private int _texture;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -59,26 +61,34 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
- // The shaders have been modified to include the texture coordinates, check them out after finishing the OnLoad function.
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
-
// Because there's now 5 floats between the start of the first vertex and the start of the second,
// we modify the stride from 3 * sizeof(float) to 5 * sizeof(float).
// This will now pass the new vertex array to the buffer.
- var vertexLocation = _shader.GetAttribLocation("aPosition");
+ var vertexLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
// Next, we also setup texture coordinates. It works in much the same way.
// We add an offset of 3, since the texture coordinates comes after the position data.
// We also change the amount of data to 2 because there's only 2 floats for texture coordinates.
- var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
+ var texCoordLocation = 1; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
- _texture = Texture.LoadFromFile("Resources/container.png");
- _texture.Use(TextureUnit.Texture0);
+ // The shaders have been modified to include the texture coordinates, check them out after finishing the OnLoad function.
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
+
+ // Next, we must setup the samplers in the shaders to use the right texture units.
+ // The int we send to the uniform indicates which texture unit the sampler should use.
+ // This is more important when using multiple textures as in the next chapter.
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0);
+
+ // Now we need to actually load the texture data itself.
+ // See the LoadTextureFromFile function for how to do this.
+ _texture = LoadTextureFromFile("Resources/container.png");
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -89,8 +99,10 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vertexArrayObject);
- _texture.Use(TextureUnit.Texture0);
- _shader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
+
+ GL.UseProgram(_shader);
GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
@@ -109,11 +121,117 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
{
- base.OnResize(e);
+ // Generate handle
+ int handle = GL.GenTexture();
+
+ // Bind the handle
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ // For this example, we're going to use the StbImageSharp library to load textures.
+
+ // OpenGL has it's texture origin in the lower left corner instead of the top left corner,
+ // so we tell StbImageSharp to flip the image when loading.
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ // Here we open a stream to the file and pass it to StbImageSharp to load.
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+
+ // Now that our pixels are prepared, it's time to generate a texture. We do this with GL.TexImage2D.
+ // Arguments:
+ // The type of texture we're generating. There are various different types of textures, but the only one we need right now is Texture2D.
+ // Level of detail. We can use this to start from a smaller mipmap (if we want), but we don't need to do that, so leave it at 0.
+ // Target format of the pixels. This is the format OpenGL will store our image with.
+ // Width of the image
+ // Height of the image.
+ // Border of the image. This must always be 0; it's a legacy parameter that Khronos never got rid of.
+ // The format of the pixels, explained above. Since we loaded the pixels as RGBA earlier, we need to use PixelFormat.Rgba.
+ // Data type of the pixels.
+ // And finally, the actual pixels.
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
- GL.Viewport(0, 0, Size.X, Size.Y);
+ // Now that our texture is loaded, we can set a few settings to affect how the image appears on rendering.
+
+ // First, we set the min and mag filter. These are used for when the texture is scaled down and up, respectively.
+ // Here, we use Linear for both. This means that OpenGL will try to blend pixels, meaning that textures scaled too far will look blurred.
+ // You could also use (amongst other options) Nearest, which just grabs the nearest pixel, which makes the texture look pixelated if scaled too far.
+ // NOTE: The default settings for both of these are LinearMipmap. If you leave these as default but don't generate mipmaps,
+ // your image will fail to render at all (usually resulting in pure black instead).
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ // Now, set the wrapping mode. S is for the X axis, and T is for the Y axis.
+ // We set this to Repeat so that textures will repeat when wrapped. Not demonstrated here since the texture coordinates exactly match
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ // Next, generate mipmaps.
+ // Mipmaps are smaller copies of the texture, scaled down. Each mipmap level is half the size of the previous one
+ // Generated mipmaps go all the way down to just one pixel.
+ // OpenGL will automatically switch between mipmaps when an object gets sufficiently far away.
+ // This prevents moiré effects, as well as saving on texture bandwidth.
+ // Here you can see and read about the morié effect https://en.wikipedia.org/wiki/Moir%C3%A9_pattern
+ // Here is an example of mips in action https://en.wikipedia.org/wiki/File:Mipmap_Aliasing_Comparison.png
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/6-MultipleTextures/6-MultipleTextures.csproj b/Chapter1/6-MultipleTextures/6-MultipleTextures.csproj
index b5e0922..c8b95b6 100644
--- a/Chapter1/6-MultipleTextures/6-MultipleTextures.csproj
+++ b/Chapter1/6-MultipleTextures/6-MultipleTextures.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/6-MultipleTextures/Program.cs b/Chapter1/6-MultipleTextures/Program.cs
index 3f29f8a..99cff2b 100644
--- a/Chapter1/6-MultipleTextures/Program.cs
+++ b/Chapter1/6-MultipleTextures/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Multiple Textures",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/6-MultipleTextures/Shaders/shader.vert b/Chapter1/6-MultipleTextures/Shaders/shader.vert
index 4fb2871..4934eaa 100644
--- a/Chapter1/6-MultipleTextures/Shaders/shader.vert
+++ b/Chapter1/6-MultipleTextures/Shaders/shader.vert
@@ -1,6 +1,6 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
@@ -10,5 +10,5 @@ void main(void)
{
texCoord = aTexCoord;
- gl_Position = vec4(aPosition, 1.0);
+ gl_Position = vec4(aPos, 1.0);
}
\ No newline at end of file
diff --git a/Chapter1/6-MultipleTextures/Window.cs b/Chapter1/6-MultipleTextures/Window.cs
index 6b19d83..4e902ee 100644
--- a/Chapter1/6-MultipleTextures/Window.cs
+++ b/Chapter1/6-MultipleTextures/Window.cs
@@ -1,8 +1,10 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -29,11 +31,11 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
- private Texture _texture;
+ private int _texture;
- private Texture _texture2;
+ private int _texture2;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -58,31 +60,36 @@ protected override void OnLoad()
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
// shader.frag has been modified yet again, take a look at it as well.
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
- var vertexLocation = _shader.GetAttribLocation("aPosition");
+ var vertexLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
- var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
+ var texCoordLocation = 1; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
- _texture = Texture.LoadFromFile("Resources/container.png");
+ _texture = LoadTextureFromFile("Resources/container.png");
// Texture units are explained in Texture.cs, at the Use function.
// First texture goes in texture unit 0.
- _texture.Use(TextureUnit.Texture0);
+
+ // First we use GL.ActiveTexture to tell OpenGL which texture unit we would like following commands to use.
+ GL.ActiveTexture(TextureUnit.Texture0);
+ // Then we bind our first texture to the 0th texture unit as a 2D texture.
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
// This is helpful because System.Drawing reads the pixels differently than OpenGL expects.
- _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
+ _texture2 = LoadTextureFromFile("Resources/awesomeface.png");
// Then, the second goes in texture unit 1.
- _texture2.Use(TextureUnit.Texture1);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
- // Next, we must setup the samplers in the shaders to use the right textures.
+ // Next, we must setup the samplers in the shaders to use the right texture units.
// The int we send to the uniform indicates which texture unit the sampler should use.
- _shader.SetInt("texture0", 0);
- _shader.SetInt("texture1", 1);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -93,9 +100,12 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vertexArrayObject);
- _texture.Use(TextureUnit.Texture0);
- _texture2.Use(TextureUnit.Texture1);
- _shader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
+
+ GL.UseProgram(_shader);
GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
@@ -114,11 +124,82 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
{
- base.OnResize(e);
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ return handle;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/7-Transformations/7-Transformations.csproj b/Chapter1/7-Transformations/7-Transformations.csproj
index b5e0922..c8b95b6 100644
--- a/Chapter1/7-Transformations/7-Transformations.csproj
+++ b/Chapter1/7-Transformations/7-Transformations.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/7-Transformations/Program.cs b/Chapter1/7-Transformations/Program.cs
index 4008987..e5aac3c 100644
--- a/Chapter1/7-Transformations/Program.cs
+++ b/Chapter1/7-Transformations/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Transformations",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/7-Transformations/Shaders/shader.vert b/Chapter1/7-Transformations/Shaders/shader.vert
index bb064b4..77ba190 100644
--- a/Chapter1/7-Transformations/Shaders/shader.vert
+++ b/Chapter1/7-Transformations/Shaders/shader.vert
@@ -1,6 +1,6 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
@@ -14,5 +14,5 @@ void main(void)
texCoord = aTexCoord;
// Then all you have to do is multiply the vertices by the transformation matrix, and you'll see your transformation in the scene!
- gl_Position = vec4(aPosition, 1.0) * transform;
+ gl_Position = vec4(aPos, 1.0) * transform;
}
\ No newline at end of file
diff --git a/Chapter1/7-Transformations/Window.cs b/Chapter1/7-Transformations/Window.cs
index 2507132..d017c87 100644
--- a/Chapter1/7-Transformations/Window.cs
+++ b/Chapter1/7-Transformations/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -39,11 +41,11 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
- private Texture _texture;
+ private int _texture;
- private Texture _texture2;
+ private int _texture2;
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
@@ -68,25 +70,27 @@ protected override void OnLoad()
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
// shader.vert has been modified, take a look at it as well.
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
- var vertexLocation = _shader.GetAttribLocation("aPosition");
+ var vertexLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
- var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
+ var texCoordLocation = 1; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
- _texture = Texture.LoadFromFile("Resources/container.png");
- _texture.Use(TextureUnit.Texture0);
+ _texture = LoadTextureFromFile("Resources/container.png");
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
- _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
- _texture2.Use(TextureUnit.Texture1);
+ _texture2 = LoadTextureFromFile("Resources/awesomeface.png");
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
- _shader.SetInt("texture0", 0);
- _shader.SetInt("texture1", 1);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1);
}
protected override void OnRenderFrame(FrameEventArgs e)
@@ -123,13 +127,16 @@ protected override void OnRenderFrame(FrameEventArgs e)
// The next tutorial will be about how to set one up so we can use more human-readable numbers.
transform = transform * Matrix4.CreateTranslation(0.1f, 0.1f, 0.0f);
- _texture.Use(TextureUnit.Texture0);
- _texture2.Use(TextureUnit.Texture1);
- _shader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
+
+ GL.UseProgram(_shader);
// Now that the matrix is finished, pass it to the vertex shader.
// Go over to shader.vert to see how we finally apply this to the vertices.
- _shader.SetMatrix4("transform", transform);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "transform"), true, ref transform);
// And that's it for now! In the next tutorial, we'll see how to setup a full coordinates system.
@@ -150,11 +157,82 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
{
- base.OnResize(e);
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ return handle;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj b/Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj
index b5e0922..c8b95b6 100644
--- a/Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj
+++ b/Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
diff --git a/Chapter1/8-CoordinatesSystems/Program.cs b/Chapter1/8-CoordinatesSystems/Program.cs
index d26f898..67618ff 100644
--- a/Chapter1/8-CoordinatesSystems/Program.cs
+++ b/Chapter1/8-CoordinatesSystems/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Coordinates Systems",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/8-CoordinatesSystems/Shaders/shader.vert b/Chapter1/8-CoordinatesSystems/Shaders/shader.vert
index 6457b69..a1dd2e5 100644
--- a/Chapter1/8-CoordinatesSystems/Shaders/shader.vert
+++ b/Chapter1/8-CoordinatesSystems/Shaders/shader.vert
@@ -1,6 +1,6 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
@@ -14,5 +14,5 @@ void main(void)
{
texCoord = aTexCoord;
- gl_Position = vec4(aPosition, 1.0) * model * view * projection;
+ gl_Position = vec4(aPos, 1.0) * model * view * projection;
}
diff --git a/Chapter1/8-CoordinatesSystems/Window.cs b/Chapter1/8-CoordinatesSystems/Window.cs
index 3282234..853c397 100644
--- a/Chapter1/8-CoordinatesSystems/Window.cs
+++ b/Chapter1/8-CoordinatesSystems/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -33,11 +35,11 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
- private Texture _texture;
+ private int _texture;
- private Texture _texture2;
+ private int _texture2;
// We create a double to hold how long has passed since the program was opened.
private double _time;
@@ -78,25 +80,27 @@ protected override void OnLoad()
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
// shader.vert has been modified. Take a look at it after the explanation in OnRenderFrame.
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
- var vertexLocation = _shader.GetAttribLocation("aPosition");
+ var vertexLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
- var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
+ var texCoordLocation = 1; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
- _texture = Texture.LoadFromFile("Resources/container.png");
- _texture.Use(TextureUnit.Texture0);
+ _texture = LoadTextureFromFile("Resources/container.png");
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
- _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
- _texture2.Use(TextureUnit.Texture1);
+ _texture2 = LoadTextureFromFile("Resources/awesomeface.png");
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
- _shader.SetInt("texture0", 0);
- _shader.SetInt("texture1", 1);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1);
// For the view, we don't do too much here. Next tutorial will be all about a Camera class that will make it much easier to manipulate the view.
// For now, we move it backwards three units on the Z axis.
@@ -124,12 +128,15 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vertexArrayObject);
- _texture.Use(TextureUnit.Texture0);
- _texture2.Use(TextureUnit.Texture1);
- _shader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
+
+ GL.UseProgram(_shader);
// Finally, we have the model matrix. This determines the position of the model.
- var model = Matrix4.Identity * Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));
+ var model = Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));
// Then, we pass all of these matrices to the vertex shader.
// You could also multiply them here and then pass, which is faster, but having the separate matrices available is used for some advanced effects.
@@ -140,9 +147,9 @@ protected override void OnRenderFrame(FrameEventArgs e)
// If you pass the individual matrices to the shader and multiply there, you have to do in the order "model * view * projection".
// You can think like this: first apply the modelToWorld (aka model) matrix, then apply the worldToView (aka view) matrix,
// and finally apply the viewToProjectedSpace (aka projection) matrix.
- _shader.SetMatrix4("model", model);
- _shader.SetMatrix4("view", _view);
- _shader.SetMatrix4("projection", _projection);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "view"), true, ref _view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "projection"), true, ref _projection);
GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
@@ -161,11 +168,82 @@ protected override void OnUpdateFrame(FrameEventArgs e)
}
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
+ {
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
+ }
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
{
- base.OnResize(e);
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ return handle;
}
}
}
\ No newline at end of file
diff --git a/Chapter1/9-Camera/9-Camera.csproj b/Chapter1/9-Camera/9-Camera.csproj
index b5e0922..c8b95b6 100644
--- a/Chapter1/9-Camera/9-Camera.csproj
+++ b/Chapter1/9-Camera/9-Camera.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
diff --git a/Common/Camera.cs b/Chapter1/9-Camera/Camera.cs
similarity index 99%
rename from Common/Camera.cs
rename to Chapter1/9-Camera/Camera.cs
index b705c6b..15b64b8 100644
--- a/Common/Camera.cs
+++ b/Chapter1/9-Camera/Camera.cs
@@ -1,7 +1,7 @@
using OpenTK.Mathematics;
using System;
-namespace LearnOpenTK.Common
+namespace LearnOpenTK
{
// This is the camera class as it could be set up after the tutorials on the website.
// It is important to note there are a few ways you could have set up this camera.
diff --git a/Chapter1/9-Camera/Program.cs b/Chapter1/9-Camera/Program.cs
index 907d3d3..4526663 100644
--- a/Chapter1/9-Camera/Program.cs
+++ b/Chapter1/9-Camera/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Camera",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter1/9-Camera/Shaders/shader.vert b/Chapter1/9-Camera/Shaders/shader.vert
index 6457b69..a1dd2e5 100644
--- a/Chapter1/9-Camera/Shaders/shader.vert
+++ b/Chapter1/9-Camera/Shaders/shader.vert
@@ -1,6 +1,6 @@
#version 330 core
-layout(location = 0) in vec3 aPosition;
+layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
@@ -14,5 +14,5 @@ void main(void)
{
texCoord = aTexCoord;
- gl_Position = vec4(aPosition, 1.0) * model * view * projection;
+ gl_Position = vec4(aPos, 1.0) * model * view * projection;
}
diff --git a/Chapter1/9-Camera/Window.cs b/Chapter1/9-Camera/Window.cs
index f9ccc1f..c8fdded 100644
--- a/Chapter1/9-Camera/Window.cs
+++ b/Chapter1/9-Camera/Window.cs
@@ -1,10 +1,11 @@
-using System;
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -40,11 +41,11 @@ public class Window : GameWindow
private int _vertexArrayObject;
- private Shader _shader;
+ private int _shader;
- private Texture _texture;
+ private int _texture;
- private Texture _texture2;
+ private int _texture2;
// The view and projection matrices have been removed as we don't need them here anymore.
// They can now be found in the new camera class.
@@ -84,25 +85,27 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
- _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
- _shader.Use();
+ _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+ GL.UseProgram(_shader);
- var vertexLocation = _shader.GetAttribLocation("aPosition");
+ var vertexLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
- var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
+ var texCoordLocation = 1; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
- _texture = Texture.LoadFromFile("Resources/container.png");
- _texture.Use(TextureUnit.Texture0);
+ _texture = LoadTextureFromFile("Resources/container.png");
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
- _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
- _texture2.Use(TextureUnit.Texture1);
+ _texture2 = LoadTextureFromFile("Resources/awesomeface.png");
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
- _shader.SetInt("texture0", 0);
- _shader.SetInt("texture1", 1);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1);
// We initialize the camera so that it is 3 units back from where the rectangle is.
// We also give it the proper aspect ratio.
@@ -122,14 +125,20 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vertexArrayObject);
- _texture.Use(TextureUnit.Texture0);
- _texture2.Use(TextureUnit.Texture1);
- _shader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _texture);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _texture2);
+
+ GL.UseProgram(_shader);
- var model = Matrix4.Identity * Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));
- _shader.SetMatrix4("model", model);
- _shader.SetMatrix4("view", _camera.GetViewMatrix());
- _shader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
+
+ var model = Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_shader, "projection"), true, ref projection);
GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
@@ -211,13 +220,85 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
// We need to update the aspect ratio once the window has been resized.
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Chapter2/1-Colors/1-Colors.csproj b/Chapter2/1-Colors/1-Colors.csproj
index ec7f0ed..dd06013 100644
--- a/Chapter2/1-Colors/1-Colors.csproj
+++ b/Chapter2/1-Colors/1-Colors.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/1-Colors/Camera.cs b/Chapter2/1-Colors/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/1-Colors/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/1-Colors/OpenTK.dll.config b/Chapter2/1-Colors/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/1-Colors/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/1-Colors/Program.cs b/Chapter2/1-Colors/Program.cs
index e14478c..1cf8360 100644
--- a/Chapter2/1-Colors/Program.cs
+++ b/Chapter2/1-Colors/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Colors",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/1-Colors/Window.cs b/Chapter2/1-Colors/Window.cs
index 69715a4..8b44af1 100644
--- a/Chapter2/1-Colors/Window.cs
+++ b/Chapter2/1-Colors/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
+using OpenTK.Graphics.GL;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -77,9 +79,9 @@ public class Window : GameWindow
// We also need two shaders, one for the lamp and one for our lighting material.
// The lighting shader is where most of this chapter will take place as this is where a lot of the lighting "magic" happens.
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
private Camera _camera;
@@ -107,15 +109,16 @@ protected override void OnLoad()
// Load the two different shaders, they use the same vertex shader program. However they have two different fragment shaders.
// This is because the lamp only uses a basic shader to turn it white, it wouldn't make sense to have the lamp lit in other colors.
// The lighting shaders uses the lighting.frag shader which is what a large part of this chapter will be about
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
{
// Initialize the vao for the model
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var vertexLocation = _lightingShader.GetAttribLocation("aPos");
+ // Specified in the shader.vert file, aPos has location 0.
+ var vertexLocation = 0;
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
}
@@ -125,8 +128,8 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- // Set the vertex attributes (only position data for our lamp)
- var vertexLocation = _lampShader.GetAttribLocation("aPos");
+ // Specified in the shader.vert file, aPos has location 0.
+ var vertexLocation = 0;
GL.EnableVertexAttribArray(vertexLocation);
GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
}
@@ -145,29 +148,34 @@ protected override void OnRenderFrame(FrameEventArgs e)
// Draw the model/cube with the lighting shader
GL.BindVertexArray(_vaoModel);
- _lightingShader.Use();
+ GL.UseProgram(_lightingShader);
+
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
// Matrix4.Identity is used as the matrix, since we just want to draw it at 0, 0, 0
- _lightingShader.SetMatrix4("model", Matrix4.Identity);
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ Matrix4 model = Matrix4.Identity;
- _lightingShader.SetVector3("objectColor", new Vector3(1.0f, 0.5f, 0.31f));
- _lightingShader.SetVector3("lightColor", new Vector3(1.0f, 1.0f, 1.0f));
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
+
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "objectColor"), new Vector3(1.0f, 0.5f, 0.31f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "lightColor"), new Vector3(1.0f, 1.0f, 1.0f));
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
// Draw the lamp, this is mostly the same as for the model cube
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f); // We scale the lamp cube down a bit to make it less dominant
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);
+ // We scale the lamp cube down a bit to make it less dominant
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -243,12 +251,58 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
- GL.Viewport(0, 0, Size.X, Size.Y);
+ GL.Viewport(0, 0, e.Width, e.Height);
+
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
}
}
diff --git a/Chapter2/2-BasicLighting/2-BasicLighting.csproj b/Chapter2/2-BasicLighting/2-BasicLighting.csproj
index ec7f0ed..dd06013 100644
--- a/Chapter2/2-BasicLighting/2-BasicLighting.csproj
+++ b/Chapter2/2-BasicLighting/2-BasicLighting.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/2-BasicLighting/Camera.cs b/Chapter2/2-BasicLighting/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/2-BasicLighting/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/2-BasicLighting/OpenTK.dll.config b/Chapter2/2-BasicLighting/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/2-BasicLighting/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/2-BasicLighting/Program.cs b/Chapter2/2-BasicLighting/Program.cs
index d1c61fe..d54fdfc 100644
--- a/Chapter2/2-BasicLighting/Program.cs
+++ b/Chapter2/2-BasicLighting/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Basic lighting",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/2-BasicLighting/Window.cs b/Chapter2/2-BasicLighting/Window.cs
index d80d979..24f5883 100644
--- a/Chapter2/2-BasicLighting/Window.cs
+++ b/Chapter2/2-BasicLighting/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
+using OpenTK.Graphics.GL;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -69,9 +71,9 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
private Camera _camera;
@@ -96,20 +98,21 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ // Specified in the shader.vert file, aPos has location 0.
+ var positionLocation = 0;
GL.EnableVertexAttribArray(positionLocation);
// Remember to change the stride as we now have 6 floats per vertex
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
- // We now need to define the layout of the normal so the shader can use it
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ // Specified in the shader.vert file, aNormal has location 1.
+ var normalLocation = 1;
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
}
@@ -118,7 +121,8 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ // Specified in the shader.vert file, aPos has location 0.
+ var positionLocation = 0;
GL.EnableVertexAttribArray(positionLocation);
// Also change the stride here as we now have 6 floats per vertex. Now we don't define the normal for the lamp VAO
// this is because it isn't used, it might seem like a waste to use the same VBO if they dont have the same data
@@ -140,29 +144,33 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _lightingShader.Use();
+ GL.UseProgram(_lightingShader);
+
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
+
+ Matrix4 model = Matrix4.Identity;
- _lightingShader.SetMatrix4("model", Matrix4.Identity);
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
- _lightingShader.SetVector3("objectColor", new Vector3(1.0f, 0.5f, 0.31f));
- _lightingShader.SetVector3("lightColor", new Vector3(1.0f, 1.0f, 1.0f));
- _lightingShader.SetVector3("lightPos", _lightPos);
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "objectColor"), new Vector3(1.0f, 0.5f, 0.31f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "lightColor"), new Vector3(1.0f, 1.0f, 1.0f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "lightPos"), _lightPos);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -238,12 +246,58 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
}
}
diff --git a/Chapter2/3-Materials/3-Materials.csproj b/Chapter2/3-Materials/3-Materials.csproj
index ec7f0ed..dd06013 100644
--- a/Chapter2/3-Materials/3-Materials.csproj
+++ b/Chapter2/3-Materials/3-Materials.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -11,10 +11,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/3-Materials/Camera.cs b/Chapter2/3-Materials/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/3-Materials/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/3-Materials/OpenTK.dll.config b/Chapter2/3-Materials/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/3-Materials/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/3-Materials/Program.cs b/Chapter2/3-Materials/Program.cs
index 3208c78..e4d91de 100644
--- a/Chapter2/3-Materials/Program.cs
+++ b/Chapter2/3-Materials/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Materials",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/3-Materials/Window.cs b/Chapter2/3-Materials/Window.cs
index 987174f..d5dc10b 100644
--- a/Chapter2/3-Materials/Window.cs
+++ b/Chapter2/3-Materials/Window.cs
@@ -1,11 +1,11 @@
-using System;
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using System;
using System.Diagnostics;
+using System.IO;
namespace LearnOpenTK
{
@@ -69,9 +69,9 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
private Camera _camera;
@@ -96,18 +96,18 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // The location of aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
}
@@ -116,7 +116,7 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
}
@@ -134,20 +134,24 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _lightingShader.Use();
+ GL.UseProgram(_lightingShader);
- _lightingShader.SetMatrix4("model", Matrix4.Identity);
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
+ Matrix4 model = Matrix4.Identity;
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
+
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
// Here we set the material values of the cube, the material struct is just a container so to access
// the underlying values we simply type "material.value" to get the location of the uniform
- _lightingShader.SetVector3("material.ambient", new Vector3(1.0f, 0.5f, 0.31f));
- _lightingShader.SetVector3("material.diffuse", new Vector3(1.0f, 0.5f, 0.31f));
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "material.ambient"), new Vector3(1.0f, 0.5f, 0.31f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "material.diffuse"), new Vector3(1.0f, 0.5f, 0.31f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "material.specular"), new Vector3(1.0f, 0.5f, 0.5f));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
// This is where we change the lights color over time using the sin function
Vector3 lightColor;
@@ -160,24 +164,22 @@ protected override void OnRenderFrame(FrameEventArgs e)
Vector3 ambientColor = lightColor * new Vector3(0.2f);
Vector3 diffuseColor = lightColor * new Vector3(0.5f);
- _lightingShader.SetVector3("light.position", _lightPos);
- _lightingShader.SetVector3("light.ambient", ambientColor);
- _lightingShader.SetVector3("light.diffuse", diffuseColor);
- _lightingShader.SetVector3("light.specular", new Vector3(1.0f, 1.0f, 1.0f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.position"), _lightPos);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.ambient"), ambientColor);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.diffuse"), diffuseColor);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.specular"), new Vector3(1.0f, 1.0f, 1.0f));
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.Identity;
- lampMatrix *= Matrix4.CreateScale(0.2f);
- lampMatrix *= Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -253,12 +255,58 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
}
}
diff --git a/Chapter2/4-LightingMaps/4-LightingMaps.csproj b/Chapter2/4-LightingMaps/4-LightingMaps.csproj
index 9263ce0..081d661 100644
--- a/Chapter2/4-LightingMaps/4-LightingMaps.csproj
+++ b/Chapter2/4-LightingMaps/4-LightingMaps.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -17,10 +17,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/4-LightingMaps/Camera.cs b/Chapter2/4-LightingMaps/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/4-LightingMaps/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/4-LightingMaps/OpenTK.dll.config b/Chapter2/4-LightingMaps/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/4-LightingMaps/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/4-LightingMaps/Program.cs b/Chapter2/4-LightingMaps/Program.cs
index 92bf3c8..d67858a 100644
--- a/Chapter2/4-LightingMaps/Program.cs
+++ b/Chapter2/4-LightingMaps/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Lighting maps",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/4-LightingMaps/Shaders/lighting.frag b/Chapter2/4-LightingMaps/Shaders/lighting.frag
index c7c21f0..e3f5be0 100644
--- a/Chapter2/4-LightingMaps/Shaders/lighting.frag
+++ b/Chapter2/4-LightingMaps/Shaders/lighting.frag
@@ -28,26 +28,26 @@ in vec3 FragPos;
// Now we need the texture coordinates, however we only need one set even though we have 2 textures,
// as every fragment should have the same texture position no matter what texture we are using.
-in vec2 TexCoords;
+in vec2 TexCoord;
void main()
{
// Each of the 3 different components now use a texture for the material values instead of the object wide color they had before.
// Note: The ambient and the diffuse share the same texture.
// ambient
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
// Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
// Specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
diff --git a/Chapter2/4-LightingMaps/Shaders/shader.vert b/Chapter2/4-LightingMaps/Shaders/shader.vert
index af12da3..4fa340c 100644
--- a/Chapter2/4-LightingMaps/Shaders/shader.vert
+++ b/Chapter2/4-LightingMaps/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
+layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
@@ -9,12 +9,12 @@ uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos;
-out vec2 TexCoords;
+out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0) * model * view * projection;
FragPos = vec3(vec4(aPos, 1.0) * model);
Normal = aNormal * mat3(transpose(inverse(model)));
- TexCoords = aTexCoords;
+ TexCoord = aTexCoord;
}
\ No newline at end of file
diff --git a/Chapter2/4-LightingMaps/Window.cs b/Chapter2/4-LightingMaps/Window.cs
index b3479b6..2f8d452 100644
--- a/Chapter2/4-LightingMaps/Window.cs
+++ b/Chapter2/4-LightingMaps/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -67,16 +69,16 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
// The texture containing information for the diffuse map, this would more commonly
// just be called the color/texture of the object.
- private Texture _diffuseMap;
+ private int _diffuseMap;
// The specular map is a black/white representation of how specular each part of the texture is.
- private Texture _specularMap;
+ private int _specularMap;
private Camera _camera;
@@ -101,25 +103,25 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
// All of the vertex attributes have been updated to now have a stride of 8 float sizes.
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // The location of aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));
// The texture coords have now been added too, remember we only have 2 coordinates as the texture is 2d,
// so the size parameter should only be 2 for the texture coordinates.
- var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
+ var texCoordLocation = 2; // The location of aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
}
@@ -130,15 +132,15 @@ protected override void OnLoad()
// The lamp shader should have its stride updated aswell, however we dont actually
// use the texture coords for the lamp, so we dont need to add any extra attributes.
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // The location of aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
}
// Our two textures are loaded in from memory, you should head over and
// check them out and compare them to the results.
- _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
- _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");
+ _diffuseMap = LoadTextureFromFile("Resources/container2.png");
+ _specularMap = LoadTextureFromFile("Resources/container2_specular.png");
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
@@ -155,40 +157,45 @@ protected override void OnRenderFrame(FrameEventArgs e)
// The two textures need to be used, in this case we use the diffuse map as our 0th texture
// and the specular map as our 1st texture.
- _diffuseMap.Use(TextureUnit.Texture0);
- _specularMap.Use(TextureUnit.Texture1);
- _lightingShader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _diffuseMap);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _specularMap);
- _lightingShader.SetMatrix4("model", Matrix4.Identity);
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UseProgram(_lightingShader);
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
- // Here we specify to the shaders what textures they should refer to when we want to get the positions.
- _lightingShader.SetInt("material.diffuse", 0);
- _lightingShader.SetInt("material.specular", 1);
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ Matrix4 model = Matrix4.Identity;
- _lightingShader.SetVector3("light.position", _lightPos);
- _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
- _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
- _lightingShader.SetVector3("light.specular", new Vector3(1.0f));
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
+
+ // Here we specify to the shaders what textures they should refer to when we want to get the positions.
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.diffuse"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.specular"), 1);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
+
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.position"), _lightPos);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.ambient"), new Vector3(0.2f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.diffuse"), new Vector3(0.5f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.specular"), new Vector3(1.0f));
+
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.Identity;
- lampMatrix *= Matrix4.CreateScale(0.2f);
- lampMatrix *= Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -264,12 +271,84 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj b/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj
index 182a118..7705f8b 100644
--- a/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj
+++ b/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-DirectionalLights/Camera.cs b/Chapter2/5-LightCasters-DirectionalLights/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/5-LightCasters-DirectionalLights/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config b/Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/5-LightCasters-DirectionalLights/Program.cs b/Chapter2/5-LightCasters-DirectionalLights/Program.cs
index 15c17bd..7762351 100644
--- a/Chapter2/5-LightCasters-DirectionalLights/Program.cs
+++ b/Chapter2/5-LightCasters-DirectionalLights/Program.cs
@@ -9,7 +9,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new OpenTK.Mathematics.Vector2i(800, 600),
+ ClientSize = new OpenTK.Mathematics.Vector2i(800, 600),
Title = "LearnOpenTK - Light caster - directional",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag b/Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag
index d2cb8bf..2480cab 100644
--- a/Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag
+++ b/Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag
@@ -21,25 +21,25 @@ uniform vec3 viewPos;
out vec4 FragColor;
in vec3 Normal;
-in vec2 TexCoords;
+in vec2 TexCoord;
void main()
{
// ambient
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(-light.direction);//We still normalize the light direction since we techically dont know,
//wether it was normalized for us or not.
float diff = max(dot(norm, lightDir), 0.0);
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
// specular
vec3 viewDir = normalize(viewPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
diff --git a/Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert b/Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert
index 5604e7b..9a0c35f 100644
--- a/Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert
+++ b/Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert
@@ -1,18 +1,18 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
+layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 Normal;
-out vec2 TexCoords;
+out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0) * model * view * projection;
Normal = aNormal * mat3(transpose(inverse(model)));
- TexCoords = aTexCoords;
+ TexCoord = aTexCoord;
}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-DirectionalLights/Window.cs b/Chapter2/5-LightCasters-DirectionalLights/Window.cs
index 9a75fc0..7a71878 100644
--- a/Chapter2/5-LightCasters-DirectionalLights/Window.cs
+++ b/Chapter2/5-LightCasters-DirectionalLights/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -82,13 +84,13 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
- private Texture _diffuseMap;
+ private int _diffuseMap;
- private Texture _specularMap;
+ private int _specularMap;
private Camera _camera;
@@ -113,22 +115,22 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
-
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));
- var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
+ var texCoordLocation = 2; // aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
}
@@ -139,13 +141,13 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
}
- _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
- _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");
+ _diffuseMap = LoadTextureFromFile("Resources/container2.png");
+ _specularMap = LoadTextureFromFile("Resources/container2_specular.png");
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
@@ -160,25 +162,31 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _diffuseMap.Use(TextureUnit.Texture0);
- _specularMap.Use(TextureUnit.Texture1);
- _lightingShader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _diffuseMap);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _specularMap);
+
+ GL.UseProgram(_lightingShader);
+
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
- _lightingShader.SetInt("material.diffuse", 0);
- _lightingShader.SetInt("material.specular", 1);
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ // Here we specify to the shaders what textures they should refer to when we want to get the positions.
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.diffuse"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.specular"), 1);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
// Directional light needs a direction, in this example we just use (-0.2, -1.0, -0.3f) as the lights direction
- _lightingShader.SetVector3("light.direction", new Vector3(-0.2f, -1.0f, -0.3f));
- _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
- _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
- _lightingShader.SetVector3("light.specular", new Vector3(1.0f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.direction"), new Vector3(-0.2f, -1.0f, -0.3f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.ambient"), new Vector3(0.2f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.diffuse"), new Vector3(0.5f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.specular"), new Vector3(1.0f));
// We want to draw all the cubes at their respective positions
for (int i = 0; i < _cubePositions.Length; i++)
@@ -188,8 +196,8 @@ protected override void OnRenderFrame(FrameEventArgs e)
// We then calculate the angle and rotate the model around an axis
float angle = 20.0f * i;
model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
- // Remember to set the model at last so it can be used by opentk
- _lightingShader.SetMatrix4("model", model);
+ // Remember to set the model at last so it can be used by opengl
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
// At last we draw all our cubes
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -197,14 +205,13 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -280,12 +287,84 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj b/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj
index 182a118..7705f8b 100644
--- a/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj
+++ b/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-PointLights/Camera.cs b/Chapter2/5-LightCasters-PointLights/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/5-LightCasters-PointLights/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-PointLights/OpenTK.dll.config b/Chapter2/5-LightCasters-PointLights/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/5-LightCasters-PointLights/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/5-LightCasters-PointLights/Program.cs b/Chapter2/5-LightCasters-PointLights/Program.cs
index c7f68ef..211bcab 100644
--- a/Chapter2/5-LightCasters-PointLights/Program.cs
+++ b/Chapter2/5-LightCasters-PointLights/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Light casters - point lights",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag b/Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag
index a32b305..e7975a2 100644
--- a/Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag
+++ b/Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag
@@ -28,25 +28,25 @@ out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
-in vec2 TexCoords;
+in vec2 TexCoord;
void main()
{
//ambient
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
//diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
//specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
//attenuation
//The attenuation is the term we use when talking about how dim the light gets over distance
diff --git a/Chapter2/5-LightCasters-PointLights/Shaders/shader.vert b/Chapter2/5-LightCasters-PointLights/Shaders/shader.vert
index af12da3..4fa340c 100644
--- a/Chapter2/5-LightCasters-PointLights/Shaders/shader.vert
+++ b/Chapter2/5-LightCasters-PointLights/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
+layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
@@ -9,12 +9,12 @@ uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos;
-out vec2 TexCoords;
+out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0) * model * view * projection;
FragPos = vec3(vec4(aPos, 1.0) * model);
Normal = aNormal * mat3(transpose(inverse(model)));
- TexCoords = aTexCoords;
+ TexCoord = aTexCoord;
}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-PointLights/Window.cs b/Chapter2/5-LightCasters-PointLights/Window.cs
index a40b7ea..295d5ad 100644
--- a/Chapter2/5-LightCasters-PointLights/Window.cs
+++ b/Chapter2/5-LightCasters-PointLights/Window.cs
@@ -1,9 +1,11 @@
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -83,13 +85,13 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
- private Texture _diffuseMap;
+ private int _diffuseMap;
- private Texture _specularMap;
+ private int _specularMap;
private Camera _camera;
@@ -114,22 +116,22 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
-
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));
- var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
+ var texCoordLocation = 2; // aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
}
@@ -138,13 +140,13 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
}
- _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
- _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");
+ _diffuseMap = LoadTextureFromFile("Resources/container2.png");
+ _specularMap = LoadTextureFromFile("Resources/container2_specular.png");
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
@@ -159,27 +161,34 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _diffuseMap.Use(TextureUnit.Texture0);
- _specularMap.Use(TextureUnit.Texture1);
- _lightingShader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _diffuseMap);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _specularMap);
+
+ GL.UseProgram(_lightingShader);
+
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
- _lightingShader.SetInt("material.diffuse", 0);
- _lightingShader.SetInt("material.specular", 1);
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ // Here we specify to the shaders what textures they should refer to when we want to get the positions.
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.diffuse"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.specular"), 1);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
- _lightingShader.SetVector3("light.position", _lightPos);
- _lightingShader.SetFloat("light.constant", 1.0f);
- _lightingShader.SetFloat("light.linear", 0.09f);
- _lightingShader.SetFloat("light.quadratic", 0.032f);
- _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
- _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
- _lightingShader.SetVector3("light.specular", new Vector3(1.0f));
+ // Directional light needs a direction, in this example we just use (-0.2, -1.0, -0.3f) as the lights direction
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.position"), _lightPos);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.constant"), 1.0f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.linear"), 0.09f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.quadratic"), 0.032f);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.ambient"), new Vector3(0.2f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.diffuse"), new Vector3(0.5f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.specular"), new Vector3(1.0f));
// We want to draw all the cubes at their respective positions
for (int i = 0; i < _cubePositions.Length; i++)
@@ -191,7 +200,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
float angle = 20.0f * i;
model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
// Remember to set the model at last so it can be used by opentk
- _lightingShader.SetMatrix4("model", model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
// At last we draw all our cubes
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -199,14 +208,13 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -282,12 +290,84 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Chapter2/5-LightCasters-Spotlight/5-LightCasters-Spotlight.csproj b/Chapter2/5-LightCasters-Spotlight/5-LightCasters-Spotlight.csproj
index 182a118..7705f8b 100644
--- a/Chapter2/5-LightCasters-Spotlight/5-LightCasters-Spotlight.csproj
+++ b/Chapter2/5-LightCasters-Spotlight/5-LightCasters-Spotlight.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-Spotlight/Camera.cs b/Chapter2/5-LightCasters-Spotlight/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/5-LightCasters-Spotlight/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-Spotlight/OpenTK.dll.config b/Chapter2/5-LightCasters-Spotlight/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/5-LightCasters-Spotlight/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/5-LightCasters-Spotlight/Program.cs b/Chapter2/5-LightCasters-Spotlight/Program.cs
index d8ba86c..21cfdfc 100644
--- a/Chapter2/5-LightCasters-Spotlight/Program.cs
+++ b/Chapter2/5-LightCasters-Spotlight/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Light casters - spotlight",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/5-LightCasters-Spotlight/Shaders/lighting.frag b/Chapter2/5-LightCasters-Spotlight/Shaders/lighting.frag
index 78abfaf..a421a32 100644
--- a/Chapter2/5-LightCasters-Spotlight/Shaders/lighting.frag
+++ b/Chapter2/5-LightCasters-Spotlight/Shaders/lighting.frag
@@ -29,24 +29,24 @@ out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
-in vec2 TexCoords;
+in vec2 TexCoord;
void main()
{
//ambient
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
//diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
//specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
//attenuation
float distance = length(light.position - FragPos);
diff --git a/Chapter2/5-LightCasters-Spotlight/Shaders/shader.vert b/Chapter2/5-LightCasters-Spotlight/Shaders/shader.vert
index af12da3..4fa340c 100644
--- a/Chapter2/5-LightCasters-Spotlight/Shaders/shader.vert
+++ b/Chapter2/5-LightCasters-Spotlight/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
+layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
@@ -9,12 +9,12 @@ uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos;
-out vec2 TexCoords;
+out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0) * model * view * projection;
FragPos = vec3(vec4(aPos, 1.0) * model);
Normal = aNormal * mat3(transpose(inverse(model)));
- TexCoords = aTexCoords;
+ TexCoord = aTexCoord;
}
\ No newline at end of file
diff --git a/Chapter2/5-LightCasters-Spotlight/Window.cs b/Chapter2/5-LightCasters-Spotlight/Window.cs
index 792373c..9782fb9 100644
--- a/Chapter2/5-LightCasters-Spotlight/Window.cs
+++ b/Chapter2/5-LightCasters-Spotlight/Window.cs
@@ -1,10 +1,11 @@
-using System;
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -85,13 +86,13 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
- private Texture _diffuseMap;
+ private int _diffuseMap;
- private Texture _specularMap;
+ private int _specularMap;
private Camera _camera;
@@ -116,22 +117,22 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
-
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));
- var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
+ var texCoordLocation = 2; // aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
}
@@ -140,13 +141,13 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
}
- _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
- _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");
+ _diffuseMap = LoadTextureFromFile("Resources/container2.png");
+ _specularMap = LoadTextureFromFile("Resources/container2_specular.png");
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
@@ -161,30 +162,37 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _diffuseMap.Use(TextureUnit.Texture0);
- _specularMap.Use(TextureUnit.Texture1);
- _lightingShader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _diffuseMap);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _specularMap);
+
+ GL.UseProgram(_lightingShader);
+
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
- _lightingShader.SetInt("material.diffuse", 0);
- _lightingShader.SetInt("material.specular", 1);
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ // Here we specify to the shaders what textures they should refer to when we want to get the positions.
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.diffuse"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.specular"), 1);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
- _lightingShader.SetVector3("light.position", _camera.Position);
- _lightingShader.SetVector3("light.direction", _camera.Front);
- _lightingShader.SetFloat("light.cutOff", MathF.Cos(MathHelper.DegreesToRadians(12.5f)));
- _lightingShader.SetFloat("light.outerCutOff", MathF.Cos(MathHelper.DegreesToRadians(17.5f)));
- _lightingShader.SetFloat("light.constant", 1.0f);
- _lightingShader.SetFloat("light.linear", 0.09f);
- _lightingShader.SetFloat("light.quadratic", 0.032f);
- _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
- _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
- _lightingShader.SetVector3("light.specular", new Vector3(1.0f));
+ // Directional light needs a direction, in this example we just use (-0.2, -1.0, -0.3f) as the lights direction
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.position"), _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.direction"), _camera.Front);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.cutOff"), MathF.Cos(MathHelper.DegreesToRadians(12.5f)));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.outerCutOff"), MathF.Cos(MathHelper.DegreesToRadians(17.5f)));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.constant"), 1.0f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.linear"), 0.09f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "light.quadratic"), 0.032f);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.ambient"), new Vector3(0.2f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.diffuse"), new Vector3(0.5f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "light.specular"), new Vector3(1.0f));
// We want to draw all the cubes at their respective positions
for (int i = 0; i < _cubePositions.Length; i++)
@@ -195,7 +203,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
float angle = 20.0f * i;
model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
// Remember to set the model at last so it can be used by opentk
- _lightingShader.SetMatrix4("model", model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
// At last we draw all our cubes
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -203,14 +211,13 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos);
- _lampShader.SetMatrix4("model", lampMatrix);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
@@ -286,12 +293,84 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Chapter2/6-MultipleLights/6-MultipleLights.csproj b/Chapter2/6-MultipleLights/6-MultipleLights.csproj
index 182a118..7705f8b 100644
--- a/Chapter2/6-MultipleLights/6-MultipleLights.csproj
+++ b/Chapter2/6-MultipleLights/6-MultipleLights.csproj
@@ -3,7 +3,7 @@
WinExe
LearnOpenTK
LearnOpenTK
- netcoreapp3.1
+ net8.0
@@ -12,10 +12,7 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Chapter2/6-MultipleLights/Camera.cs b/Chapter2/6-MultipleLights/Camera.cs
new file mode 100644
index 0000000..15b64b8
--- /dev/null
+++ b/Chapter2/6-MultipleLights/Camera.cs
@@ -0,0 +1,119 @@
+using OpenTK.Mathematics;
+using System;
+
+namespace LearnOpenTK
+{
+ // This is the camera class as it could be set up after the tutorials on the website.
+ // It is important to note there are a few ways you could have set up this camera.
+ // For example, you could have also managed the player input inside the camera class,
+ // and a lot of the properties could have been made into functions.
+
+ // TL;DR: This is just one of many ways in which we could have set up the camera.
+ // Check out the web version if you don't know why we are doing a specific thing or want to know more about the code.
+ public class Camera
+ {
+ // Those vectors are directions pointing outwards from the camera to define how it rotated.
+ private Vector3 _front = -Vector3.UnitZ;
+
+ private Vector3 _up = Vector3.UnitY;
+
+ private Vector3 _right = Vector3.UnitX;
+
+ // Rotation around the X axis (radians)
+ private float _pitch;
+
+ // Rotation around the Y axis (radians)
+ private float _yaw = -MathHelper.PiOver2; // Without this, you would be started rotated 90 degrees right.
+
+ // The field of view of the camera (radians)
+ private float _fov = MathHelper.PiOver2;
+
+ public Camera(Vector3 position, float aspectRatio)
+ {
+ Position = position;
+ AspectRatio = aspectRatio;
+ }
+
+ // The position of the camera
+ public Vector3 Position { get; set; }
+
+ // This is simply the aspect ratio of the viewport, used for the projection matrix.
+ public float AspectRatio { private get; set; }
+
+ public Vector3 Front => _front;
+
+ public Vector3 Up => _up;
+
+ public Vector3 Right => _right;
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Pitch
+ {
+ get => MathHelper.RadiansToDegrees(_pitch);
+ set
+ {
+ // We clamp the pitch value between -89 and 89 to prevent the camera from going upside down, and a bunch
+ // of weird "bugs" when you are using euler angles for rotation.
+ // If you want to read more about this you can try researching a topic called gimbal lock
+ var angle = MathHelper.Clamp(value, -89f, 89f);
+ _pitch = MathHelper.DegreesToRadians(angle);
+ UpdateVectors();
+ }
+ }
+
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Yaw
+ {
+ get => MathHelper.RadiansToDegrees(_yaw);
+ set
+ {
+ _yaw = MathHelper.DegreesToRadians(value);
+ UpdateVectors();
+ }
+ }
+
+ // The field of view (FOV) is the vertical angle of the camera view.
+ // This has been discussed more in depth in a previous tutorial,
+ // but in this tutorial, you have also learned how we can use this to simulate a zoom feature.
+ // We convert from degrees to radians as soon as the property is set to improve performance.
+ public float Fov
+ {
+ get => MathHelper.RadiansToDegrees(_fov);
+ set
+ {
+ var angle = MathHelper.Clamp(value, 1f, 90f);
+ _fov = MathHelper.DegreesToRadians(angle);
+ }
+ }
+
+ // Get the view matrix using the amazing LookAt function described more in depth on the web tutorials
+ public Matrix4 GetViewMatrix()
+ {
+ return Matrix4.LookAt(Position, Position + _front, _up);
+ }
+
+ // Get the projection matrix using the same method we have used up until this point
+ public Matrix4 GetProjectionMatrix()
+ {
+ return Matrix4.CreatePerspectiveFieldOfView(_fov, AspectRatio, 0.01f, 100f);
+ }
+
+ // This function is going to update the direction vertices using some of the math learned in the web tutorials.
+ private void UpdateVectors()
+ {
+ // First, the front matrix is calculated using some basic trigonometry.
+ _front.X = MathF.Cos(_pitch) * MathF.Cos(_yaw);
+ _front.Y = MathF.Sin(_pitch);
+ _front.Z = MathF.Cos(_pitch) * MathF.Sin(_yaw);
+
+ // We need to make sure the vectors are all normalized, as otherwise we would get some funky results.
+ _front = Vector3.Normalize(_front);
+
+ // Calculate both the right and the up vector using cross product.
+ // Note that we are calculating the right from the global up; this behaviour might
+ // not be what you need for all cameras so keep this in mind if you do not want a FPS camera.
+ _right = Vector3.Normalize(Vector3.Cross(_front, Vector3.UnitY));
+ _up = Vector3.Normalize(Vector3.Cross(_right, _front));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter2/6-MultipleLights/OpenTK.dll.config b/Chapter2/6-MultipleLights/OpenTK.dll.config
deleted file mode 100644
index 7098d39..0000000
--- a/Chapter2/6-MultipleLights/OpenTK.dll.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Chapter2/6-MultipleLights/Program.cs b/Chapter2/6-MultipleLights/Program.cs
index 6c488e7..691277b 100644
--- a/Chapter2/6-MultipleLights/Program.cs
+++ b/Chapter2/6-MultipleLights/Program.cs
@@ -10,7 +10,7 @@ private static void Main()
{
var nativeWindowSettings = new NativeWindowSettings()
{
- Size = new Vector2i(800, 600),
+ ClientSize = new Vector2i(800, 600),
Title = "LearnOpenTK - Multiple lights",
// This is needed to run on macos
Flags = ContextFlags.ForwardCompatible,
diff --git a/Chapter2/6-MultipleLights/Shaders/lighting.frag b/Chapter2/6-MultipleLights/Shaders/lighting.frag
index b68142b..5746618 100644
--- a/Chapter2/6-MultipleLights/Shaders/lighting.frag
+++ b/Chapter2/6-MultipleLights/Shaders/lighting.frag
@@ -54,7 +54,7 @@ out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
-in vec2 TexCoords;
+in vec2 TexCoord;
//Here we have some function prototypes, these are the signatures the gpu will use to know how the
//parameters of each light calculation is layed out.
@@ -89,9 +89,9 @@ vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//combine results
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
return (ambient + diffuse + specular);
}
@@ -108,9 +108,9 @@ vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
float attenuation = 1.0 / (light.constant + light.linear * distance +
light.quadratic * (distance * distance));
//combine results
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
@@ -138,9 +138,9 @@ vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
//combine results
- vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
- vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
- vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
+ vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoord));
+ vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord));
+ vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord));
ambient *= attenuation;
diffuse *= attenuation * intensity;
specular *= attenuation * intensity;
diff --git a/Chapter2/6-MultipleLights/Shaders/shader.vert b/Chapter2/6-MultipleLights/Shaders/shader.vert
index af12da3..4fa340c 100644
--- a/Chapter2/6-MultipleLights/Shaders/shader.vert
+++ b/Chapter2/6-MultipleLights/Shaders/shader.vert
@@ -1,7 +1,7 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
-layout (location = 2) in vec2 aTexCoords;
+layout (location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
@@ -9,12 +9,12 @@ uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos;
-out vec2 TexCoords;
+out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0) * model * view * projection;
FragPos = vec3(vec4(aPos, 1.0) * model);
Normal = aNormal * mat3(transpose(inverse(model)));
- TexCoords = aTexCoords;
+ TexCoord = aTexCoord;
}
\ No newline at end of file
diff --git a/Chapter2/6-MultipleLights/Window.cs b/Chapter2/6-MultipleLights/Window.cs
index 4b64d2c..03cc0dd 100644
--- a/Chapter2/6-MultipleLights/Window.cs
+++ b/Chapter2/6-MultipleLights/Window.cs
@@ -1,10 +1,11 @@
-using System;
-using LearnOpenTK.Common;
-using OpenTK.Graphics.OpenGL4;
+using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
-using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using StbImageSharp;
+using System;
+using System.IO;
namespace LearnOpenTK
{
@@ -87,13 +88,13 @@ public class Window : GameWindow
private int _vaoLamp;
- private Shader _lampShader;
+ private int _lampShader;
- private Shader _lightingShader;
+ private int _lightingShader;
- private Texture _diffuseMap;
+ private int _diffuseMap;
- private Texture _specularMap;
+ private int _specularMap;
private Camera _camera;
@@ -118,22 +119,22 @@ protected override void OnLoad()
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
- _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
- _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
-
+ _lightingShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/lighting.frag"));
+ _lampShader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag"));
+
{
_vaoModel = GL.GenVertexArray();
GL.BindVertexArray(_vaoModel);
- var positionLocation = _lightingShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
- var normalLocation = _lightingShader.GetAttribLocation("aNormal");
+ var normalLocation = 1; // aNormal
GL.EnableVertexAttribArray(normalLocation);
GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));
- var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
+ var texCoordLocation = 2; // aTexCoord
GL.EnableVertexAttribArray(texCoordLocation);
GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
}
@@ -142,13 +143,13 @@ protected override void OnLoad()
_vaoLamp = GL.GenVertexArray();
GL.BindVertexArray(_vaoLamp);
- var positionLocation = _lampShader.GetAttribLocation("aPos");
+ var positionLocation = 0; // aPos
GL.EnableVertexAttribArray(positionLocation);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
}
- _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
- _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");
+ _diffuseMap = LoadTextureFromFile("Resources/container2.png");
+ _specularMap = LoadTextureFromFile("Resources/container2_specular.png");
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
@@ -163,19 +164,23 @@ protected override void OnRenderFrame(FrameEventArgs e)
GL.BindVertexArray(_vaoModel);
- _diffuseMap.Use(TextureUnit.Texture0);
- _specularMap.Use(TextureUnit.Texture1);
- _lightingShader.Use();
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, _diffuseMap);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, _specularMap);
+ GL.UseProgram(_lightingShader);
- _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ Matrix4 projection = _camera.GetProjectionMatrix();
+ Matrix4 view = _camera.GetViewMatrix();
- _lightingShader.SetVector3("viewPos", _camera.Position);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection);
- _lightingShader.SetInt("material.diffuse", 0);
- _lightingShader.SetInt("material.specular", 1);
- _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
- _lightingShader.SetFloat("material.shininess", 32.0f);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position);
+
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.diffuse"), 0);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.specular"), 1);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "material.shininess"), 32.0f);
/*
Here we set all the uniforms for the 5/6 types of lights we have. We have to set them manually and index
@@ -184,58 +189,57 @@ the proper PointLight struct in the array to set each uniform variable. This can
by using 'Uniform buffer objects', but that is something we'll discuss in the 'Advanced GLSL' tutorial.
*/
// Directional light
- _lightingShader.SetVector3("dirLight.direction", new Vector3(-0.2f, -1.0f, -0.3f));
- _lightingShader.SetVector3("dirLight.ambient", new Vector3(0.05f, 0.05f, 0.05f));
- _lightingShader.SetVector3("dirLight.diffuse", new Vector3(0.4f, 0.4f, 0.4f));
- _lightingShader.SetVector3("dirLight.specular", new Vector3(0.5f, 0.5f, 0.5f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "dirLight.direction"), new Vector3(-0.2f, -1.0f, -0.3f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "dirLight.ambient"), new Vector3(0.05f, 0.05f, 0.05f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "dirLight.diffuse"), new Vector3(0.4f, 0.4f, 0.4f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "dirLight.specular"), new Vector3(0.5f, 0.5f, 0.5f));
// Point lights
for (int i = 0; i < _pointLightPositions.Length; i++)
{
- _lightingShader.SetVector3($"pointLights[{i}].position", _pointLightPositions[i]);
- _lightingShader.SetVector3($"pointLights[{i}].ambient", new Vector3(0.05f, 0.05f, 0.05f));
- _lightingShader.SetVector3($"pointLights[{i}].diffuse", new Vector3(0.8f, 0.8f, 0.8f));
- _lightingShader.SetVector3($"pointLights[{i}].specular", new Vector3(1.0f, 1.0f, 1.0f));
- _lightingShader.SetFloat($"pointLights[{i}].constant", 1.0f);
- _lightingShader.SetFloat($"pointLights[{i}].linear", 0.09f);
- _lightingShader.SetFloat($"pointLights[{i}].quadratic", 0.032f);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].position"), _pointLightPositions[i]);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].ambient"), new Vector3(0.05f, 0.05f, 0.05f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].diffuse"), new Vector3(0.8f, 0.8f, 0.8f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].specular"), new Vector3(1.0f, 1.0f, 1.0f));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].constant"), 1.0f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].linear"), 0.09f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, $"pointLights[{i}].quadratic"), 0.032f);
}
// Spot light
- _lightingShader.SetVector3("spotLight.position", _camera.Position);
- _lightingShader.SetVector3("spotLight.direction", _camera.Front);
- _lightingShader.SetVector3("spotLight.ambient", new Vector3(0.0f, 0.0f, 0.0f));
- _lightingShader.SetVector3("spotLight.diffuse", new Vector3(1.0f, 1.0f, 1.0f));
- _lightingShader.SetVector3("spotLight.specular", new Vector3(1.0f, 1.0f, 1.0f));
- _lightingShader.SetFloat("spotLight.constant", 1.0f);
- _lightingShader.SetFloat("spotLight.linear", 0.09f);
- _lightingShader.SetFloat("spotLight.quadratic", 0.032f);
- _lightingShader.SetFloat("spotLight.cutOff", MathF.Cos(MathHelper.DegreesToRadians(12.5f)));
- _lightingShader.SetFloat("spotLight.outerCutOff", MathF.Cos(MathHelper.DegreesToRadians(17.5f)));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "spotLight.position"), _camera.Position);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "spotLight.direction"), _camera.Front);
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "spotLight.ambient"), new Vector3(0.0f, 0.0f, 0.0f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "spotLight.diffuse"), new Vector3(1.0f, 1.0f, 1.0f));
+ GL.Uniform3(GL.GetUniformLocation(_lightingShader, "spotLight.specular"), new Vector3(1.0f, 1.0f, 1.0f));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "spotLight.constant"), 1.0f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "spotLight.linear"), 0.09f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "spotLight.quadratic"), 0.032f);
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "spotLight.cutOff"), MathF.Cos(MathHelper.DegreesToRadians(12.5f)));
+ GL.Uniform1(GL.GetUniformLocation(_lightingShader, "spotLight.outerCutOff"), MathF.Cos(MathHelper.DegreesToRadians(17.5f)));
for (int i = 0; i < _cubePositions.Length; i++)
{
Matrix4 model = Matrix4.CreateTranslation(_cubePositions[i]);
float angle = 20.0f * i;
model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
- _lightingShader.SetMatrix4("model", model);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
}
GL.BindVertexArray(_vaoLamp);
- _lampShader.Use();
+ GL.UseProgram(_lampShader);
- _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
- _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "view"), true, ref view);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "projection"), true, ref projection);
// We use a loop to draw all the lights at the proper position
for (int i = 0; i < _pointLightPositions.Length; i++)
{
- Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
- lampMatrix = lampMatrix * Matrix4.CreateTranslation(_pointLightPositions[i]);
+ Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_pointLightPositions[i]);
- _lampShader.SetMatrix4("model", lampMatrix);
+ GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix);
GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
}
@@ -312,12 +316,84 @@ protected override void OnMouseWheel(MouseWheelEventArgs e)
_camera.Fov -= e.OffsetY;
}
- protected override void OnResize(ResizeEventArgs e)
+ protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
{
- base.OnResize(e);
+ base.OnFramebufferResize(e);
+
+ GL.Viewport(0, 0, e.Width, e.Height);
- GL.Viewport(0, 0, Size.X, Size.Y);
_camera.AspectRatio = Size.X / (float)Size.Y;
}
+
+ private static int CompileProgram(string vertexSource, string fragmentSource)
+ {
+ int vertexShader = CompileShader(ShaderType.VertexShader, vertexSource);
+ int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentSource);
+
+ int handle = GL.CreateProgram();
+
+ GL.AttachShader(handle, vertexShader);
+ GL.AttachShader(handle, fragmentShader);
+
+ GL.LinkProgram(handle);
+
+ GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ string infoLog = GL.GetProgramInfoLog(handle);
+ throw new Exception($"Error occurred whilst linking Program({handle}):\n{infoLog}");
+ }
+
+ GL.DetachShader(handle, vertexShader);
+ GL.DetachShader(handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ return handle;
+ }
+
+ private static int CompileShader(ShaderType type, string source)
+ {
+ int shader = GL.CreateShader(type);
+
+ GL.ShaderSource(shader, source);
+
+ GL.CompileShader(shader);
+
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}");
+ }
+
+ return shader;
+ }
+
+ public static int LoadTextureFromFile(string path)
+ {
+ int handle = GL.GenTexture();
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, handle);
+
+ StbImage.stbi_set_flip_vertically_on_load(1);
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
+ }
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+
+ return handle;
+ }
}
}
diff --git a/Common/Common.csproj b/Common/Common.csproj
deleted file mode 100644
index 14df6c8..0000000
--- a/Common/Common.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- LearnOpenTK.Common
- LearnOpenTK.Common
- netcoreapp3.1
-
-
-
-
-
-
-
diff --git a/Common/Shader.cs b/Common/Shader.cs
deleted file mode 100644
index b1e6b8b..0000000
--- a/Common/Shader.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-using System;
-using System.IO;
-using System.Text;
-using System.Collections.Generic;
-using OpenTK.Graphics.OpenGL4;
-using OpenTK.Mathematics;
-
-namespace LearnOpenTK.Common
-{
- // A simple class meant to help create shaders.
- public class Shader
- {
- public readonly int Handle;
-
- private readonly Dictionary _uniformLocations;
-
- // This is how you create a simple shader.
- // Shaders are written in GLSL, which is a language very similar to C in its semantics.
- // The GLSL source is compiled *at runtime*, so it can optimize itself for the graphics card it's currently being used on.
- // A commented example of GLSL can be found in shader.vert.
- public Shader(string vertPath, string fragPath)
- {
- // There are several different types of shaders, but the only two you need for basic rendering are the vertex and fragment shaders.
- // The vertex shader is responsible for moving around vertices, and uploading that data to the fragment shader.
- // The vertex shader won't be too important here, but they'll be more important later.
- // The fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel.
- // The fragment shader is what we'll be using the most here.
-
- // Load vertex shader and compile
- var shaderSource = File.ReadAllText(vertPath);
-
- // GL.CreateShader will create an empty shader (obviously). The ShaderType enum denotes which type of shader will be created.
- var vertexShader = GL.CreateShader(ShaderType.VertexShader);
-
- // Now, bind the GLSL source code
- GL.ShaderSource(vertexShader, shaderSource);
-
- // And then compile
- CompileShader(vertexShader);
-
- // We do the same for the fragment shader.
- shaderSource = File.ReadAllText(fragPath);
- var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
- GL.ShaderSource(fragmentShader, shaderSource);
- CompileShader(fragmentShader);
-
- // These two shaders must then be merged into a shader program, which can then be used by OpenGL.
- // To do this, create a program...
- Handle = GL.CreateProgram();
-
- // Attach both shaders...
- GL.AttachShader(Handle, vertexShader);
- GL.AttachShader(Handle, fragmentShader);
-
- // And then link them together.
- LinkProgram(Handle);
-
- // When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
- // Detach them, and then delete them.
- GL.DetachShader(Handle, vertexShader);
- GL.DetachShader(Handle, fragmentShader);
- GL.DeleteShader(fragmentShader);
- GL.DeleteShader(vertexShader);
-
- // The shader is now ready to go, but first, we're going to cache all the shader uniform locations.
- // Querying this from the shader is very slow, so we do it once on initialization and reuse those values
- // later.
-
- // First, we have to get the number of active uniforms in the shader.
- GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);
-
- // Next, allocate the dictionary to hold the locations.
- _uniformLocations = new Dictionary();
-
- // Loop over all the uniforms,
- for (var i = 0; i < numberOfUniforms; i++)
- {
- // get the name of this uniform,
- var key = GL.GetActiveUniform(Handle, i, out _, out _);
-
- // get the location,
- var location = GL.GetUniformLocation(Handle, key);
-
- // and then add it to the dictionary.
- _uniformLocations.Add(key, location);
- }
- }
-
- private static void CompileShader(int shader)
- {
- // Try to compile the shader
- GL.CompileShader(shader);
-
- // Check for compilation errors
- GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
- if (code != (int)All.True)
- {
- // We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
- var infoLog = GL.GetShaderInfoLog(shader);
- throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
- }
- }
-
- private static void LinkProgram(int program)
- {
- // We link the program
- GL.LinkProgram(program);
-
- // Check for linking errors
- GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
- if (code != (int)All.True)
- {
- // We can use `GL.GetProgramInfoLog(program)` to get information about the error.
- throw new Exception($"Error occurred whilst linking Program({program})");
- }
- }
-
- // A wrapper function that enables the shader program.
- public void Use()
- {
- GL.UseProgram(Handle);
- }
-
- // The shader sources provided with this project use hardcoded layout(location)-s. If you want to do it dynamically,
- // you can omit the layout(location=X) lines in the vertex shader, and use this in VertexAttribPointer instead of the hardcoded values.
- public int GetAttribLocation(string attribName)
- {
- return GL.GetAttribLocation(Handle, attribName);
- }
-
- // Uniform setters
- // Uniforms are variables that can be set by user code, instead of reading them from the VBO.
- // You use VBOs for vertex-related data, and uniforms for almost everything else.
-
- // Setting a uniform is almost always the exact same, so I'll explain it here once, instead of in every method:
- // 1. Bind the program you want to set the uniform on
- // 2. Get a handle to the location of the uniform with GL.GetUniformLocation.
- // 3. Use the appropriate GL.Uniform* function to set the uniform.
-
- ///
- /// Set a uniform int on this shader.
- ///
- /// The name of the uniform
- /// The data to set
- public void SetInt(string name, int data)
- {
- GL.UseProgram(Handle);
- GL.Uniform1(_uniformLocations[name], data);
- }
-
- ///
- /// Set a uniform float on this shader.
- ///
- /// The name of the uniform
- /// The data to set
- public void SetFloat(string name, float data)
- {
- GL.UseProgram(Handle);
- GL.Uniform1(_uniformLocations[name], data);
- }
-
- ///
- /// Set a uniform Matrix4 on this shader
- ///
- /// The name of the uniform
- /// The data to set
- ///
- ///
- /// The matrix is transposed before being sent to the shader.
- ///
- ///
- public void SetMatrix4(string name, Matrix4 data)
- {
- GL.UseProgram(Handle);
- GL.UniformMatrix4(_uniformLocations[name], true, ref data);
- }
-
- ///
- /// Set a uniform Vector3 on this shader.
- ///
- /// The name of the uniform
- /// The data to set
- public void SetVector3(string name, Vector3 data)
- {
- GL.UseProgram(Handle);
- GL.Uniform3(_uniformLocations[name], data);
- }
- }
-}
diff --git a/Common/Texture.cs b/Common/Texture.cs
deleted file mode 100755
index b9bde62..0000000
--- a/Common/Texture.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using OpenTK.Graphics.OpenGL4;
-using System.Drawing;
-using System.Drawing.Imaging;
-using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat;
-using StbImageSharp;
-using System.IO;
-
-namespace LearnOpenTK.Common
-{
- // A helper class, much like Shader, meant to simplify loading textures.
- public class Texture
- {
- public readonly int Handle;
-
- public static Texture LoadFromFile(string path)
- {
- // Generate handle
- int handle = GL.GenTexture();
-
- // Bind the handle
- GL.ActiveTexture(TextureUnit.Texture0);
- GL.BindTexture(TextureTarget.Texture2D, handle);
-
- // For this example, we're going to use .NET's built-in System.Drawing library to load textures.
-
- // OpenGL has it's texture origin in the lower left corner instead of the top left corner,
- // so we tell StbImageSharp to flip the image when loading.
- StbImage.stbi_set_flip_vertically_on_load(1);
-
- // Here we open a stream to the file and pass it to StbImageSharp to load.
- using (Stream stream = File.OpenRead(path))
- {
- ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
-
- // Now that our pixels are prepared, it's time to generate a texture. We do this with GL.TexImage2D.
- // Arguments:
- // The type of texture we're generating. There are various different types of textures, but the only one we need right now is Texture2D.
- // Level of detail. We can use this to start from a smaller mipmap (if we want), but we don't need to do that, so leave it at 0.
- // Target format of the pixels. This is the format OpenGL will store our image with.
- // Width of the image
- // Height of the image.
- // Border of the image. This must always be 0; it's a legacy parameter that Khronos never got rid of.
- // The format of the pixels, explained above. Since we loaded the pixels as RGBA earlier, we need to use PixelFormat.Rgba.
- // Data type of the pixels.
- // And finally, the actual pixels.
- GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
- }
-
- // Now that our texture is loaded, we can set a few settings to affect how the image appears on rendering.
-
- // First, we set the min and mag filter. These are used for when the texture is scaled down and up, respectively.
- // Here, we use Linear for both. This means that OpenGL will try to blend pixels, meaning that textures scaled too far will look blurred.
- // You could also use (amongst other options) Nearest, which just grabs the nearest pixel, which makes the texture look pixelated if scaled too far.
- // NOTE: The default settings for both of these are LinearMipmap. If you leave these as default but don't generate mipmaps,
- // your image will fail to render at all (usually resulting in pure black instead).
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
-
- // Now, set the wrapping mode. S is for the X axis, and T is for the Y axis.
- // We set this to Repeat so that textures will repeat when wrapped. Not demonstrated here since the texture coordinates exactly match
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
-
- // Next, generate mipmaps.
- // Mipmaps are smaller copies of the texture, scaled down. Each mipmap level is half the size of the previous one
- // Generated mipmaps go all the way down to just one pixel.
- // OpenGL will automatically switch between mipmaps when an object gets sufficiently far away.
- // This prevents moiré effects, as well as saving on texture bandwidth.
- // Here you can see and read about the morié effect https://en.wikipedia.org/wiki/Moir%C3%A9_pattern
- // Here is an example of mips in action https://en.wikipedia.org/wiki/File:Mipmap_Aliasing_Comparison.png
- GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
-
- return new Texture(handle);
- }
-
- public Texture(int glHandle)
- {
- Handle = glHandle;
- }
-
- // Activate texture
- // Multiple textures can be bound, if your shader needs more than just one.
- // If you want to do that, use GL.ActiveTexture to set which slot GL.BindTexture binds to.
- // The OpenGL standard requires that there be at least 16, but there can be more depending on your graphics card.
- public void Use(TextureUnit unit)
- {
- GL.ActiveTexture(unit);
- GL.BindTexture(TextureTarget.Texture2D, Handle);
- }
- }
-}
diff --git a/LearnOpenTK.sln b/LearnOpenTK.sln
index c22f114..ba4771b 100644
--- a/LearnOpenTK.sln
+++ b/LearnOpenTK.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30309.148
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36408.4 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1-CreatingAWindow", "Chapter1\1-CreatingAWindow\1-CreatingAWindow.csproj", "{39A0FE24-A920-4C13-9BB9-18483FECD55D}"
EndProject
@@ -19,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "8-CoordinatesSystems", "Cha
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "9-Camera", "Chapter1\9-Camera\9-Camera.csproj", "{F2E87EBD-BA5C-4A22-820F-99B492B39BCB}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{2CEFE30B-F99D-4E62-8D33-2366A6F75F7F}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chapter1", "Chapter1", "{F7676E7B-4F14-48D9-8985-3FA73BF633BD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chapter2", "Chapter2", "{F66727D5-726B-41DF-8318-6E12733B278F}"
@@ -85,10 +83,6 @@ Global
{F2E87EBD-BA5C-4A22-820F-99B492B39BCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2E87EBD-BA5C-4A22-820F-99B492B39BCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2E87EBD-BA5C-4A22-820F-99B492B39BCB}.Release|Any CPU.Build.0 = Release|Any CPU
- {2CEFE30B-F99D-4E62-8D33-2366A6F75F7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2CEFE30B-F99D-4E62-8D33-2366A6F75F7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2CEFE30B-F99D-4E62-8D33-2366A6F75F7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2CEFE30B-F99D-4E62-8D33-2366A6F75F7F}.Release|Any CPU.Build.0 = Release|Any CPU
{F363CEB6-1E2C-4114-AA8C-85452472F82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F363CEB6-1E2C-4114-AA8C-85452472F82B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F363CEB6-1E2C-4114-AA8C-85452472F82B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -161,7 +155,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2A8747DC-91DB-4ECB-AB4A-5504F9D715D9}
EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {2A8747DC-91DB-4ECB-AB4A-5504F9D715D9}
- EndGlobalSection
EndGlobal