From f4a00bb843ee5e01dd9911f98b7a59a7343c34c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4ger?= Date: Wed, 20 Sep 2023 15:35:19 +0200 Subject: [PATCH 1/4] un-oop:ed the Shader class --- Chapter1/2-HelloTriangle/Shaders/shader.vert | 8 +- Chapter1/2-HelloTriangle/Window.cs | 4 +- .../Shaders/shader.vert | 4 +- Chapter1/3-ElementBufferObjects/Window.cs | 4 +- .../4-Shaders-InsAndOuts/Shaders/shader.vert | 4 +- Chapter1/4-Shaders-InsAndOuts/Window.cs | 5 +- .../Shaders/shader.vert | 4 +- Chapter1/4-Shaders-MoreAttributes/Window.cs | 4 +- .../4-Shaders-Uniforms/Shaders/shader.vert | 4 +- Chapter1/4-Shaders-Uniforms/Window.cs | 4 +- Chapter1/5-Textures/Shaders/shader.vert | 4 +- Chapter1/5-Textures/Window.cs | 8 +- .../6-MultipleTextures/Shaders/shader.vert | 4 +- Chapter1/6-MultipleTextures/Window.cs | 12 +- .../7-Transformations/Shaders/shader.vert | 4 +- Chapter1/7-Transformations/Window.cs | 14 +- .../8-CoordinatesSystems/Shaders/shader.vert | 4 +- Chapter1/8-CoordinatesSystems/Window.cs | 20 +-- Chapter1/9-Camera/Shaders/shader.vert | 4 +- Chapter1/9-Camera/Window.cs | 23 +-- Chapter2/1-Colors/Window.cs | 37 +++-- Chapter2/2-BasicLighting/Window.cs | 43 ++--- Chapter2/3-Materials/Window.cs | 48 +++--- Chapter2/4-LightingMaps/Shaders/lighting.frag | 8 +- Chapter2/4-LightingMaps/Shaders/shader.vert | 6 +- Chapter2/4-LightingMaps/Window.cs | 53 +++--- .../5-LightCasters-DirectionalLights.csproj | 12 ++ .../Shaders/lighting.frag | 8 +- .../Shaders/shader.vert | 6 +- .../Window.cs | 51 +++--- .../5-LightCasters-PointLights.csproj | 12 ++ .../Shaders/lighting.frag | 8 +- .../Shaders/shader.vert | 6 +- Chapter2/5-LightCasters-PointLights/Window.cs | 55 ++++--- .../Shaders/lighting.frag | 8 +- .../Shaders/shader.vert | 6 +- Chapter2/5-LightCasters-Spotlight/Window.cs | 61 +++---- .../6-MultipleLights/Shaders/lighting.frag | 20 +-- Chapter2/6-MultipleLights/Shaders/shader.vert | 6 +- Chapter2/6-MultipleLights/Window.cs | 81 +++++----- Common/Shader.cs | 151 ++++++------------ 41 files changed, 416 insertions(+), 412 deletions(-) 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..e37c319 100644 --- a/Chapter1/2-HelloTriangle/Window.cs +++ b/Chapter1/2-HelloTriangle/Window.cs @@ -118,7 +118,7 @@ protected override void OnLoad() // 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.Handle); // Setup is now complete! Now we move to the OnRenderFrame function to finally draw the triangle. } @@ -140,7 +140,7 @@ protected override void OnRenderFrame(FrameEventArgs e) // and then calling an OpenGL function to render. // Bind the shader - _shader.Use(); + GL.UseProgram(_shader.Handle); // Bind the VAO GL.BindVertexArray(_vertexArrayObject); 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..92539f3 100644 --- a/Chapter1/3-ElementBufferObjects/Window.cs +++ b/Chapter1/3-ElementBufferObjects/Window.cs @@ -77,7 +77,7 @@ protected override void OnLoad() // 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(); + GL.UseProgram(_shader.Handle); } protected override void OnRenderFrame(FrameEventArgs e) @@ -86,7 +86,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.Clear(ClearBufferMask.ColorBufferBit); - _shader.Use(); + GL.UseProgram(_shader.Handle); // 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. 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..5ca3951 100644 --- a/Chapter1/4-Shaders-InsAndOuts/Window.cs +++ b/Chapter1/4-Shaders-InsAndOuts/Window.cs @@ -57,7 +57,8 @@ protected override void OnLoad() Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}"); _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _shader.Use(); + + GL.UseProgram(_shader.Handle); } protected override void OnRenderFrame(FrameEventArgs e) @@ -66,7 +67,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.Clear(ClearBufferMask.ColorBufferBit); - _shader.Use(); + GL.UseProgram(_shader.Handle); GL.BindVertexArray(_vertexArrayObject); 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..279d094 100644 --- a/Chapter1/4-Shaders-MoreAttributes/Window.cs +++ b/Chapter1/4-Shaders-MoreAttributes/Window.cs @@ -68,7 +68,7 @@ protected override void OnLoad() Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}"); _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _shader.Use(); + GL.UseProgram(_shader.Handle); } 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.Handle); GL.BindVertexArray(_vertexArrayObject); 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..a09f956 100644 --- a/Chapter1/4-Shaders-Uniforms/Window.cs +++ b/Chapter1/4-Shaders-Uniforms/Window.cs @@ -57,7 +57,7 @@ protected override void OnLoad() Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}"); _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _shader.Use(); + GL.UseProgram(_shader.Handle); // We start the stopwatch here as this method is only called once. _timer = new Stopwatch(); @@ -70,7 +70,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.Clear(ClearBufferMask.ColorBufferBit); - _shader.Use(); + GL.UseProgram(_shader.Handle); // 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. 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..71f2df6 100644 --- a/Chapter1/5-Textures/Window.cs +++ b/Chapter1/5-Textures/Window.cs @@ -61,19 +61,19 @@ protected override void OnLoad() // 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(); + GL.UseProgram(_shader.Handle); // 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)); @@ -90,7 +90,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vertexArrayObject); _texture.Use(TextureUnit.Texture0); - _shader.Use(); + GL.UseProgram(_shader.Handle); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); 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..eeac115 100644 --- a/Chapter1/6-MultipleTextures/Window.cs +++ b/Chapter1/6-MultipleTextures/Window.cs @@ -59,13 +59,13 @@ protected override void OnLoad() // 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(); + GL.UseProgram(_shader.Handle); - 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)); @@ -81,8 +81,8 @@ protected override void OnLoad() // Next, we must setup the samplers in the shaders to use the right textures. // 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(_shader.UniformLocations["texture0"], 0); + GL.Uniform1(_shader.UniformLocations["texture1"], 1); } protected override void OnRenderFrame(FrameEventArgs e) @@ -95,7 +95,7 @@ protected override void OnRenderFrame(FrameEventArgs e) _texture.Use(TextureUnit.Texture0); _texture2.Use(TextureUnit.Texture1); - _shader.Use(); + GL.UseProgram(_shader.Handle); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); 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..f65e766 100644 --- a/Chapter1/7-Transformations/Window.cs +++ b/Chapter1/7-Transformations/Window.cs @@ -69,13 +69,13 @@ protected override void OnLoad() // shader.vert has been modified, take a look at it as well. _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _shader.Use(); + GL.UseProgram(_shader.Handle); - 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)); @@ -85,8 +85,8 @@ protected override void OnLoad() _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); _texture2.Use(TextureUnit.Texture1); - _shader.SetInt("texture0", 0); - _shader.SetInt("texture1", 1); + GL.Uniform1(_shader.UniformLocations["texture0"], 0); + GL.Uniform1(_shader.UniformLocations["texture1"], 1); } protected override void OnRenderFrame(FrameEventArgs e) @@ -125,11 +125,11 @@ protected override void OnRenderFrame(FrameEventArgs e) _texture.Use(TextureUnit.Texture0); _texture2.Use(TextureUnit.Texture1); - _shader.Use(); + GL.UseProgram(_shader.Handle); // 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(_shader.UniformLocations["transform"], true, ref transform); // And that's it for now! In the next tutorial, we'll see how to setup a full coordinates system. 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..d45e6cc 100644 --- a/Chapter1/8-CoordinatesSystems/Window.cs +++ b/Chapter1/8-CoordinatesSystems/Window.cs @@ -79,13 +79,13 @@ protected override void OnLoad() // 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(); + GL.UseProgram(_shader.Handle); - 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)); @@ -95,8 +95,8 @@ protected override void OnLoad() _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); _texture2.Use(TextureUnit.Texture1); - _shader.SetInt("texture0", 0); - _shader.SetInt("texture1", 1); + GL.Uniform1(_shader.UniformLocations["texture0"], 0); + GL.Uniform1(_shader.UniformLocations["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. @@ -126,10 +126,10 @@ protected override void OnRenderFrame(FrameEventArgs e) _texture.Use(TextureUnit.Texture0); _texture2.Use(TextureUnit.Texture1); - _shader.Use(); + GL.UseProgram(_shader.Handle); // 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 +140,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(_shader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_shader.UniformLocations["view"], true, ref _view); + GL.UniformMatrix4(_shader.UniformLocations["projection"], true, ref _projection); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); 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..6c8530e 100644 --- a/Chapter1/9-Camera/Window.cs +++ b/Chapter1/9-Camera/Window.cs @@ -85,13 +85,13 @@ protected override void OnLoad() GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw); _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _shader.Use(); + GL.UseProgram(_shader.Handle); - 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)); @@ -101,8 +101,8 @@ protected override void OnLoad() _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); _texture2.Use(TextureUnit.Texture1); - _shader.SetInt("texture0", 0); - _shader.SetInt("texture1", 1); + GL.Uniform1(_shader.UniformLocations["texture0"], 0); + GL.Uniform1(_shader.UniformLocations["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. @@ -124,12 +124,15 @@ protected override void OnRenderFrame(FrameEventArgs e) _texture.Use(TextureUnit.Texture0); _texture2.Use(TextureUnit.Texture1); - _shader.Use(); + GL.UseProgram(_shader.Handle); - 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(_shader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_shader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_shader.UniformLocations["projection"], true, ref projection); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); diff --git a/Chapter2/1-Colors/Window.cs b/Chapter2/1-Colors/Window.cs index 69715a4..97614af 100644 --- a/Chapter2/1-Colors/Window.cs +++ b/Chapter2/1-Colors/Window.cs @@ -4,6 +4,7 @@ using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; +using OpenTK.Graphics.GL; namespace LearnOpenTK { @@ -115,7 +116,8 @@ protected override void OnLoad() _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 +127,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 +147,34 @@ protected override void OnRenderFrame(FrameEventArgs e) // Draw the model/cube with the lighting shader GL.BindVertexArray(_vaoModel); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); + + 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; + + GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["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)); + GL.Uniform3(_lightingShader.UniformLocations["objectColor"], new Vector3(1.0f, 0.5f, 0.31f)); + GL.Uniform3(_lightingShader.UniformLocations["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.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); diff --git a/Chapter2/2-BasicLighting/Window.cs b/Chapter2/2-BasicLighting/Window.cs index d80d979..67babc2 100644 --- a/Chapter2/2-BasicLighting/Window.cs +++ b/Chapter2/2-BasicLighting/Window.cs @@ -4,6 +4,7 @@ using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; +using OpenTK.Graphics.GL; namespace LearnOpenTK { @@ -103,13 +104,14 @@ protected override void OnLoad() _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 +120,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 +143,33 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _lightingShader.SetMatrix4("model", Matrix4.Identity); - _lightingShader.SetMatrix4("view", _camera.GetViewMatrix()); - _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix()); + Matrix4 projection = _camera.GetProjectionMatrix(); + Matrix4 view = _camera.GetViewMatrix(); - _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); + Matrix4 model = Matrix4.Identity; + + GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + + GL.Uniform3(_lightingShader.UniformLocations["objectColor"], new Vector3(1.0f, 0.5f, 0.31f)); + GL.Uniform3(_lightingShader.UniformLocations["lightColor"], new Vector3(1.0f, 1.0f, 1.0f)); + GL.Uniform3(_lightingShader.UniformLocations["lightPos"], _lightPos); + GL.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); diff --git a/Chapter2/3-Materials/Window.cs b/Chapter2/3-Materials/Window.cs index 987174f..797b12b 100644 --- a/Chapter2/3-Materials/Window.cs +++ b/Chapter2/3-Materials/Window.cs @@ -103,11 +103,11 @@ protected override void OnLoad() _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.Handle); - _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(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + + GL.Uniform3(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["material.ambient"], new Vector3(1.0f, 0.5f, 0.31f)); + GL.Uniform3(_lightingShader.UniformLocations["material.diffuse"], new Vector3(1.0f, 0.5f, 0.31f)); + GL.Uniform3(_lightingShader.UniformLocations["material.specular"], new Vector3(1.0f, 0.5f, 0.5f)); + GL.Uniform1(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["light.position"], _lightPos); + GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], ambientColor); + GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], diffuseColor); + GL.Uniform3(_lightingShader.UniformLocations["light.specular"], new Vector3(1.0f, 1.0f, 1.0f)); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 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..83068ec 100644 --- a/Chapter2/4-LightingMaps/Window.cs +++ b/Chapter2/4-LightingMaps/Window.cs @@ -4,6 +4,7 @@ using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; +using OpenTK.Graphics.GL; namespace LearnOpenTK { @@ -109,17 +110,17 @@ protected override void OnLoad() 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,7 +131,7 @@ 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); } @@ -157,38 +158,40 @@ protected override void OnRenderFrame(FrameEventArgs e) // and the specular map as our 1st texture. _diffuseMap.Use(TextureUnit.Texture0); _specularMap.Use(TextureUnit.Texture1); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _lightingShader.SetMatrix4("model", Matrix4.Identity); - _lightingShader.SetMatrix4("view", _camera.GetViewMatrix()); - _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix()); + Matrix4 projection = _camera.GetProjectionMatrix(); + Matrix4 view = _camera.GetViewMatrix(); - _lightingShader.SetVector3("viewPos", _camera.Position); + Matrix4 model = Matrix4.Identity; - // 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); + GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); - _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.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); + // Here we specify to the shaders what textures they should refer to when we want to get the positions. + GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); + GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); + GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); + + GL.Uniform3(_lightingShader.UniformLocations["light.position"], _lightPos); + GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); + GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); + GL.Uniform3(_lightingShader.UniformLocations["light.specular"], new Vector3(1.0f)); + GL.DrawArrays(PrimitiveType.Triangles, 0, 36); GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); diff --git a/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj b/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj index 182a118..20342b8 100644 --- a/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj +++ b/Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj @@ -18,4 +18,16 @@ + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + \ No newline at end of file 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..58c2c22 100644 --- a/Chapter2/5-LightCasters-DirectionalLights/Window.cs +++ b/Chapter2/5-LightCasters-DirectionalLights/Window.cs @@ -4,6 +4,7 @@ using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; +using System.Reflection; namespace LearnOpenTK { @@ -120,15 +121,15 @@ protected override void OnLoad() _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,7 +140,7 @@ 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); } @@ -162,23 +163,26 @@ protected override void OnRenderFrame(FrameEventArgs e) _diffuseMap.Use(TextureUnit.Texture0); _specularMap.Use(TextureUnit.Texture1); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _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(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["viewPos"], _camera.Position); + + // Here we specify to the shaders what textures they should refer to when we want to get the positions. + GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); + GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); + GL.Uniform1(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["light.direction"], new Vector3(-0.2f, -1.0f, -0.3f)); + GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); + GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); + GL.Uniform3(_lightingShader.UniformLocations["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 +192,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(_lightingShader.UniformLocations["model"], true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -197,14 +201,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); diff --git a/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj b/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj index 182a118..20342b8 100644 --- a/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj +++ b/Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj @@ -18,4 +18,16 @@ + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + \ No newline at end of file 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..ed09816 100644 --- a/Chapter2/5-LightCasters-PointLights/Window.cs +++ b/Chapter2/5-LightCasters-PointLights/Window.cs @@ -121,15 +121,15 @@ protected override void OnLoad() _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,7 +138,7 @@ 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); } @@ -161,25 +161,29 @@ protected override void OnRenderFrame(FrameEventArgs e) _diffuseMap.Use(TextureUnit.Texture0); _specularMap.Use(TextureUnit.Texture1); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _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(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["viewPos"], _camera.Position); - _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)); + // Here we specify to the shaders what textures they should refer to when we want to get the positions. + GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); + GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); + GL.Uniform1(_lightingShader.UniformLocations["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 + GL.Uniform3(_lightingShader.UniformLocations["light.position"], _lightPos); + GL.Uniform1(_lightingShader.UniformLocations["light.constant"], 1.0f); + GL.Uniform1(_lightingShader.UniformLocations["light.linear"], 0.09f); + GL.Uniform1(_lightingShader.UniformLocations["light.quadratic"], 0.032f); + GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); + GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); + GL.Uniform3(_lightingShader.UniformLocations["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 +195,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(_lightingShader.UniformLocations["model"], true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -199,14 +203,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 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..29ce501 100644 --- a/Chapter2/5-LightCasters-Spotlight/Window.cs +++ b/Chapter2/5-LightCasters-Spotlight/Window.cs @@ -123,15 +123,15 @@ protected override void OnLoad() _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,7 +140,7 @@ 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); } @@ -163,28 +163,32 @@ protected override void OnRenderFrame(FrameEventArgs e) _diffuseMap.Use(TextureUnit.Texture0); _specularMap.Use(TextureUnit.Texture1); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _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(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["viewPos"], _camera.Position); - _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)); + // Here we specify to the shaders what textures they should refer to when we want to get the positions. + GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); + GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); + GL.Uniform1(_lightingShader.UniformLocations["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 + GL.Uniform3(_lightingShader.UniformLocations["light.position"], _camera.Position); + GL.Uniform3(_lightingShader.UniformLocations["light.direction"], _camera.Front); + GL.Uniform1(_lightingShader.UniformLocations["light.cutOff"], MathF.Cos(MathHelper.DegreesToRadians(12.5f))); + GL.Uniform1(_lightingShader.UniformLocations["light.outerCutOff"], MathF.Cos(MathHelper.DegreesToRadians(17.5f))); + GL.Uniform1(_lightingShader.UniformLocations["light.constant"], 1.0f); + GL.Uniform1(_lightingShader.UniformLocations["light.linear"], 0.09f); + GL.Uniform1(_lightingShader.UniformLocations["light.quadratic"], 0.032f); + GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); + GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); + GL.Uniform3(_lightingShader.UniformLocations["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 +199,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(_lightingShader.UniformLocations["model"], true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -203,14 +207,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - 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(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 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..2101691 100644 --- a/Chapter2/6-MultipleLights/Window.cs +++ b/Chapter2/6-MultipleLights/Window.cs @@ -125,15 +125,15 @@ protected override void OnLoad() _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,7 +142,7 @@ 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); } @@ -165,17 +165,19 @@ protected override void OnRenderFrame(FrameEventArgs e) _diffuseMap.Use(TextureUnit.Texture0); _specularMap.Use(TextureUnit.Texture1); - _lightingShader.Use(); + GL.UseProgram(_lightingShader.Handle); - _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(_lightingShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["viewPos"], _camera.Position); + + GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); + GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); + GL.Uniform1(_lightingShader.UniformLocations["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 +186,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(_lightingShader.UniformLocations["dirLight.direction"], new Vector3(-0.2f, -1.0f, -0.3f)); + GL.Uniform3(_lightingShader.UniformLocations["dirLight.ambient"], new Vector3(0.05f, 0.05f, 0.05f)); + GL.Uniform3(_lightingShader.UniformLocations["dirLight.diffuse"], new Vector3(0.4f, 0.4f, 0.4f)); + GL.Uniform3(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations[$"pointLights[{i}].position"], _pointLightPositions[i]); + GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].ambient"], new Vector3(0.05f, 0.05f, 0.05f)); + GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].diffuse"], new Vector3(0.8f, 0.8f, 0.8f)); + GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].specular"], new Vector3(1.0f, 1.0f, 1.0f)); + GL.Uniform1(_lightingShader.UniformLocations[$"pointLights[{i}].constant"], 1.0f); + GL.Uniform1(_lightingShader.UniformLocations[$"pointLights[{i}].linear"], 0.09f); + GL.Uniform1(_lightingShader.UniformLocations[$"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(_lightingShader.UniformLocations["spotLight.position"], _camera.Position); + GL.Uniform3(_lightingShader.UniformLocations["spotLight.direction"], _camera.Front); + GL.Uniform3(_lightingShader.UniformLocations["spotLight.ambient"], new Vector3(0.0f, 0.0f, 0.0f)); + GL.Uniform3(_lightingShader.UniformLocations["spotLight.diffuse"], new Vector3(1.0f, 1.0f, 1.0f)); + GL.Uniform3(_lightingShader.UniformLocations["spotLight.specular"], new Vector3(1.0f, 1.0f, 1.0f)); + GL.Uniform1(_lightingShader.UniformLocations["spotLight.constant"], 1.0f); + GL.Uniform1(_lightingShader.UniformLocations["spotLight.linear"], 0.09f); + GL.Uniform1(_lightingShader.UniformLocations["spotLight.quadratic"], 0.032f); + GL.Uniform1(_lightingShader.UniformLocations["spotLight.cutOff"], MathF.Cos(MathHelper.DegreesToRadians(12.5f))); + GL.Uniform1(_lightingShader.UniformLocations["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(_lightingShader.UniformLocations["model"], true, ref model); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); } GL.BindVertexArray(_vaoLamp); - _lampShader.Use(); + GL.UseProgram(_lampShader.Handle); - _lampShader.SetMatrix4("view", _camera.GetViewMatrix()); - _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix()); + GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); + GL.UniformMatrix4(_lampShader.UniformLocations["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(_lampShader.UniformLocations["model"], true, ref lampMatrix); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); } diff --git a/Common/Shader.cs b/Common/Shader.cs index b1e6b8b..2e0a66c 100644 --- a/Common/Shader.cs +++ b/Common/Shader.cs @@ -4,21 +4,40 @@ using System.Collections.Generic; using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; +using System.Reflection.Metadata; +using System.ComponentModel; namespace LearnOpenTK.Common { - // A simple class meant to help create shaders. - public class Shader + public static class ShaderUtils { - public readonly int Handle; + // This function creates a dictionary that maps uniform names to their location, + // this is used to speed up uniform location queries. + public static Dictionary CreateUniformLocationsDict(int program) + { + // First, we have to get the number of active uniforms in the shader. + GL.GetProgram(program, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms); + + // Next, allocate the dictionary to hold the locations. + Dictionary uniformLocations = new Dictionary(); - private readonly Dictionary _uniformLocations; + // Loop over all the uniforms, + for (var i = 0; i < numberOfUniforms; i++) + { + // get the name of this uniform, + var key = GL.GetActiveUniform(program, i, out _, out _); + + // get the location, + var location = GL.GetUniformLocation(program, key); - // 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) + // and then add it to the dictionary. + uniformLocations.Add(key, location); + } + + return uniformLocations; + } + + public static int Compile(string vertexPath, string fragmentPath) { // 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. @@ -27,7 +46,7 @@ public Shader(string vertPath, string fragPath) // The fragment shader is what we'll be using the most here. // Load vertex shader and compile - var shaderSource = File.ReadAllText(vertPath); + var shaderSource = File.ReadAllText(vertexPath); // 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); @@ -39,51 +58,30 @@ public Shader(string vertPath, string fragPath) CompileShader(vertexShader); // We do the same for the fragment shader. - shaderSource = File.ReadAllText(fragPath); + shaderSource = File.ReadAllText(fragmentPath); 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(); + int handle = GL.CreateProgram(); // Attach both shaders... - GL.AttachShader(Handle, vertexShader); - GL.AttachShader(Handle, fragmentShader); + GL.AttachShader(handle, vertexShader); + GL.AttachShader(handle, fragmentShader); // And then link them together. - LinkProgram(Handle); + 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.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); - } + return handle; } private static void CompileShader(int shader) @@ -114,76 +112,25 @@ private static void LinkProgram(int program) 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); - } + // A simple class that can be used to associate a uniform location dictionary to a shader program. + public class Shader + { + public readonly int Handle; - /// - /// 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); - } + public readonly Dictionary UniformLocations; - /// - /// 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) + public Shader(string vertexPath, string fragmentPath) { - GL.UseProgram(Handle); - GL.UniformMatrix4(_uniformLocations[name], true, ref data); + Handle = ShaderUtils.Compile(vertexPath, fragmentPath); + UniformLocations = ShaderUtils.CreateUniformLocationsDict(Handle); } - /// - /// Set a uniform Vector3 on this shader. - /// - /// The name of the uniform - /// The data to set - public void SetVector3(string name, Vector3 data) + public Shader(int handle, Dictionary uniformLocations) { - GL.UseProgram(Handle); - GL.Uniform3(_uniformLocations[name], data); + Handle = handle; + UniformLocations = uniformLocations; } } } From d393b32c0f3ac32b2c58561c44bf3eb9a4b5fbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4ger?= Date: Thu, 18 Jan 2024 23:20:35 +0100 Subject: [PATCH 2/4] Un-oop:ed the Shader and Texture classes some more. --- Chapter1/2-HelloTriangle/Window.cs | 2 +- Chapter1/3-ElementBufferObjects/Window.cs | 2 +- Chapter1/4-Shaders-InsAndOuts/Window.cs | 2 +- Chapter1/4-Shaders-MoreAttributes/Window.cs | 2 +- Chapter1/4-Shaders-Uniforms/Window.cs | 2 +- Chapter1/5-Textures/Window.cs | 11 +- Chapter1/6-MultipleTextures/Window.cs | 24 ++-- Chapter1/7-Transformations/Window.cs | 19 +-- Chapter1/8-CoordinatesSystems/Window.cs | 19 +-- Chapter1/9-Camera/Window.cs | 19 +-- Chapter2/1-Colors/Window.cs | 4 +- Chapter2/2-BasicLighting/Window.cs | 4 +- Chapter2/3-Materials/Window.cs | 4 +- Chapter2/4-LightingMaps/Window.cs | 15 ++- .../Window.cs | 15 ++- Chapter2/5-LightCasters-PointLights/Window.cs | 15 ++- Chapter2/5-LightCasters-Spotlight/Window.cs | 15 ++- Chapter2/6-MultipleLights/Window.cs | 14 ++- Common/Shader.cs | 118 +++++++++--------- Common/Texture.cs | 23 +--- 20 files changed, 173 insertions(+), 156 deletions(-) diff --git a/Chapter1/2-HelloTriangle/Window.cs b/Chapter1/2-HelloTriangle/Window.cs index e37c319..1f57f75 100644 --- a/Chapter1/2-HelloTriangle/Window.cs +++ b/Chapter1/2-HelloTriangle/Window.cs @@ -114,7 +114,7 @@ protected override void OnLoad() // 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. // shader.vert and shader.frag contain the actual shader code. - _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); + _shader = Shader.FromFile("Shaders/shader.vert", "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. diff --git a/Chapter1/3-ElementBufferObjects/Window.cs b/Chapter1/3-ElementBufferObjects/Window.cs index 92539f3..2208ca2 100644 --- a/Chapter1/3-ElementBufferObjects/Window.cs +++ b/Chapter1/3-ElementBufferObjects/Window.cs @@ -76,7 +76,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); } diff --git a/Chapter1/4-Shaders-InsAndOuts/Window.cs b/Chapter1/4-Shaders-InsAndOuts/Window.cs index 5ca3951..dce7624 100644 --- a/Chapter1/4-Shaders-InsAndOuts/Window.cs +++ b/Chapter1/4-Shaders-InsAndOuts/Window.cs @@ -56,7 +56,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); } diff --git a/Chapter1/4-Shaders-MoreAttributes/Window.cs b/Chapter1/4-Shaders-MoreAttributes/Window.cs index 279d094..4c5f74f 100644 --- a/Chapter1/4-Shaders-MoreAttributes/Window.cs +++ b/Chapter1/4-Shaders-MoreAttributes/Window.cs @@ -67,7 +67,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); } diff --git a/Chapter1/4-Shaders-Uniforms/Window.cs b/Chapter1/4-Shaders-Uniforms/Window.cs index a09f956..5b146dc 100644 --- a/Chapter1/4-Shaders-Uniforms/Window.cs +++ b/Chapter1/4-Shaders-Uniforms/Window.cs @@ -56,7 +56,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); // We start the stopwatch here as this method is only called once. diff --git a/Chapter1/5-Textures/Window.cs b/Chapter1/5-Textures/Window.cs index 71f2df6..4689e9e 100644 --- a/Chapter1/5-Textures/Window.cs +++ b/Chapter1/5-Textures/Window.cs @@ -35,7 +35,7 @@ public class Window : GameWindow private Shader _shader; // For documentation on this, check Texture.cs. - private Texture _texture; + private int _texture; public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings) : base(gameWindowSettings, nativeWindowSettings) @@ -60,7 +60,7 @@ protected override void OnLoad() 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); // Because there's now 5 floats between the start of the first vertex and the start of the second, @@ -78,7 +78,8 @@ protected override void OnLoad() GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); _texture = Texture.LoadFromFile("Resources/container.png"); - _texture.Use(TextureUnit.Texture0); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); } protected override void OnRenderFrame(FrameEventArgs e) @@ -89,7 +90,9 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vertexArrayObject); - _texture.Use(TextureUnit.Texture0); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); + GL.UseProgram(_shader.Handle); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); diff --git a/Chapter1/6-MultipleTextures/Window.cs b/Chapter1/6-MultipleTextures/Window.cs index eeac115..c02ceb0 100644 --- a/Chapter1/6-MultipleTextures/Window.cs +++ b/Chapter1/6-MultipleTextures/Window.cs @@ -31,9 +31,9 @@ public class Window : GameWindow private Shader _shader; - private Texture _texture; + private int _texture; - private Texture _texture2; + private int _texture2; public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings) : base(gameWindowSettings, nativeWindowSettings) @@ -58,7 +58,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); var vertexLocation = 0; // The location of aPos @@ -72,14 +72,19 @@ protected override void OnLoad() _texture = Texture.LoadFromFile("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"); // 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. GL.Uniform1(_shader.UniformLocations["texture0"], 0); GL.Uniform1(_shader.UniformLocations["texture1"], 1); @@ -93,8 +98,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vertexArrayObject); - _texture.Use(TextureUnit.Texture0); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); + GL.UseProgram(_shader.Handle); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); diff --git a/Chapter1/7-Transformations/Window.cs b/Chapter1/7-Transformations/Window.cs index f65e766..e931bb2 100644 --- a/Chapter1/7-Transformations/Window.cs +++ b/Chapter1/7-Transformations/Window.cs @@ -41,9 +41,9 @@ public class Window : GameWindow private Shader _shader; - private Texture _texture; + private int _texture; - private Texture _texture2; + private int _texture2; public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings) : base(gameWindowSettings, nativeWindowSettings) @@ -68,7 +68,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); var vertexLocation = 0; // The location of aPos @@ -80,10 +80,12 @@ protected override void OnLoad() GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); _texture = Texture.LoadFromFile("Resources/container.png"); - _texture.Use(TextureUnit.Texture0); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); GL.Uniform1(_shader.UniformLocations["texture0"], 0); GL.Uniform1(_shader.UniformLocations["texture1"], 1); @@ -123,8 +125,11 @@ 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); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); + GL.UseProgram(_shader.Handle); // Now that the matrix is finished, pass it to the vertex shader. diff --git a/Chapter1/8-CoordinatesSystems/Window.cs b/Chapter1/8-CoordinatesSystems/Window.cs index d45e6cc..bcd6126 100644 --- a/Chapter1/8-CoordinatesSystems/Window.cs +++ b/Chapter1/8-CoordinatesSystems/Window.cs @@ -35,9 +35,9 @@ public class Window : GameWindow private Shader _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,7 +78,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); var vertexLocation = 0; // The location of aPos @@ -90,10 +90,12 @@ protected override void OnLoad() GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); _texture = Texture.LoadFromFile("Resources/container.png"); - _texture.Use(TextureUnit.Texture0); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); GL.Uniform1(_shader.UniformLocations["texture0"], 0); GL.Uniform1(_shader.UniformLocations["texture1"], 1); @@ -124,8 +126,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vertexArrayObject); - _texture.Use(TextureUnit.Texture0); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); + GL.UseProgram(_shader.Handle); // Finally, we have the model matrix. This determines the position of the model. diff --git a/Chapter1/9-Camera/Window.cs b/Chapter1/9-Camera/Window.cs index 6c8530e..4ed2565 100644 --- a/Chapter1/9-Camera/Window.cs +++ b/Chapter1/9-Camera/Window.cs @@ -42,9 +42,9 @@ public class Window : GameWindow private Shader _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,7 +84,7 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); GL.UseProgram(_shader.Handle); var vertexLocation = 0; // The location of aPos @@ -96,10 +96,12 @@ protected override void OnLoad() GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); _texture = Texture.LoadFromFile("Resources/container.png"); - _texture.Use(TextureUnit.Texture0); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); GL.Uniform1(_shader.UniformLocations["texture0"], 0); GL.Uniform1(_shader.UniformLocations["texture1"], 1); @@ -122,8 +124,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vertexArrayObject); - _texture.Use(TextureUnit.Texture0); - _texture2.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _texture); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _texture2); + GL.UseProgram(_shader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Chapter2/1-Colors/Window.cs b/Chapter2/1-Colors/Window.cs index 97614af..70ef4d0 100644 --- a/Chapter2/1-Colors/Window.cs +++ b/Chapter2/1-Colors/Window.cs @@ -108,8 +108,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { // Initialize the vao for the model diff --git a/Chapter2/2-BasicLighting/Window.cs b/Chapter2/2-BasicLighting/Window.cs index 67babc2..61d50be 100644 --- a/Chapter2/2-BasicLighting/Window.cs +++ b/Chapter2/2-BasicLighting/Window.cs @@ -97,8 +97,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); diff --git a/Chapter2/3-Materials/Window.cs b/Chapter2/3-Materials/Window.cs index 797b12b..516153e 100644 --- a/Chapter2/3-Materials/Window.cs +++ b/Chapter2/3-Materials/Window.cs @@ -96,8 +96,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); diff --git a/Chapter2/4-LightingMaps/Window.cs b/Chapter2/4-LightingMaps/Window.cs index 83068ec..2fb4ec3 100644 --- a/Chapter2/4-LightingMaps/Window.cs +++ b/Chapter2/4-LightingMaps/Window.cs @@ -74,10 +74,10 @@ public class Window : GameWindow // 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; @@ -102,8 +102,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); @@ -156,8 +156,11 @@ 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); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _specularMap); + GL.UseProgram(_lightingShader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Chapter2/5-LightCasters-DirectionalLights/Window.cs b/Chapter2/5-LightCasters-DirectionalLights/Window.cs index 58c2c22..4856520 100644 --- a/Chapter2/5-LightCasters-DirectionalLights/Window.cs +++ b/Chapter2/5-LightCasters-DirectionalLights/Window.cs @@ -87,9 +87,9 @@ public class Window : GameWindow private Shader _lightingShader; - private Texture _diffuseMap; + private int _diffuseMap; - private Texture _specularMap; + private int _specularMap; private Camera _camera; @@ -114,8 +114,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); @@ -161,8 +161,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - _diffuseMap.Use(TextureUnit.Texture0); - _specularMap.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _specularMap); + GL.UseProgram(_lightingShader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Chapter2/5-LightCasters-PointLights/Window.cs b/Chapter2/5-LightCasters-PointLights/Window.cs index ed09816..4948e2d 100644 --- a/Chapter2/5-LightCasters-PointLights/Window.cs +++ b/Chapter2/5-LightCasters-PointLights/Window.cs @@ -87,9 +87,9 @@ public class Window : GameWindow private Shader _lightingShader; - private Texture _diffuseMap; + private int _diffuseMap; - private Texture _specularMap; + private int _specularMap; private Camera _camera; @@ -114,8 +114,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); @@ -159,8 +159,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - _diffuseMap.Use(TextureUnit.Texture0); - _specularMap.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _specularMap); + GL.UseProgram(_lightingShader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Chapter2/5-LightCasters-Spotlight/Window.cs b/Chapter2/5-LightCasters-Spotlight/Window.cs index 29ce501..ec094b2 100644 --- a/Chapter2/5-LightCasters-Spotlight/Window.cs +++ b/Chapter2/5-LightCasters-Spotlight/Window.cs @@ -89,9 +89,9 @@ public class Window : GameWindow private Shader _lightingShader; - private Texture _diffuseMap; + private int _diffuseMap; - private Texture _specularMap; + private int _specularMap; private Camera _camera; @@ -116,8 +116,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); @@ -161,8 +161,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - _diffuseMap.Use(TextureUnit.Texture0); - _specularMap.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _specularMap); + GL.UseProgram(_lightingShader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Chapter2/6-MultipleLights/Window.cs b/Chapter2/6-MultipleLights/Window.cs index 2101691..2052470 100644 --- a/Chapter2/6-MultipleLights/Window.cs +++ b/Chapter2/6-MultipleLights/Window.cs @@ -91,9 +91,9 @@ public class Window : GameWindow private Shader _lightingShader; - private Texture _diffuseMap; + private int _diffuseMap; - private Texture _specularMap; + private int _specularMap; private Camera _camera; @@ -118,8 +118,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); + _lampShader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); { _vaoModel = GL.GenVertexArray(); @@ -163,8 +163,10 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - _diffuseMap.Use(TextureUnit.Texture0); - _specularMap.Use(TextureUnit.Texture1); + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); + GL.ActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, _specularMap); GL.UseProgram(_lightingShader.Handle); Matrix4 projection = _camera.GetProjectionMatrix(); diff --git a/Common/Shader.cs b/Common/Shader.cs index 2e0a66c..a1418f4 100644 --- a/Common/Shader.cs +++ b/Common/Shader.cs @@ -6,62 +6,41 @@ using OpenTK.Mathematics; using System.Reflection.Metadata; using System.ComponentModel; +using OpenTK.Compute.OpenCL; namespace LearnOpenTK.Common { - public static class ShaderUtils + // A simple class that can be used to associate a uniform location dictionary to a shader program. + public class Shader { - // This function creates a dictionary that maps uniform names to their location, - // this is used to speed up uniform location queries. - public static Dictionary CreateUniformLocationsDict(int program) - { - // First, we have to get the number of active uniforms in the shader. - GL.GetProgram(program, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms); - - // Next, allocate the dictionary to hold the locations. - Dictionary 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(program, i, out _, out _); - - // get the location, - var location = GL.GetUniformLocation(program, key); - - // and then add it to the dictionary. - uniformLocations.Add(key, location); - } + public readonly int Handle; - return uniformLocations; - } + public readonly Dictionary UniformLocations; - public static int Compile(string vertexPath, string fragmentPath) + public static Shader FromFile(string vertexPath, string fragmentPath) { // 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(vertexPath); + string vertexSource = File.ReadAllText(vertexPath); + string fragmentSource = File.ReadAllText(fragmentPath); - // 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); + int program = CompileProgram(vertexSource, fragmentSource); - // Now, bind the GLSL source code - GL.ShaderSource(vertexShader, shaderSource); + Dictionary uniformLocations = CreateUniformLocationsDict(program); - // And then compile - CompileShader(vertexShader); + return new Shader(program, uniformLocations); + } + + 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. - shaderSource = File.ReadAllText(fragmentPath); - var fragmentShader = GL.CreateShader(ShaderType.FragmentShader); - GL.ShaderSource(fragmentShader, shaderSource); - CompileShader(fragmentShader); + 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... @@ -72,7 +51,16 @@ public static int Compile(string vertexPath, string fragmentPath) GL.AttachShader(handle, fragmentShader); // And then link them together. - LinkProgram(handle); + 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. @@ -84,8 +72,14 @@ public static int Compile(string vertexPath, string fragmentPath) return handle; } - private static void CompileShader(int shader) + 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); @@ -95,36 +89,36 @@ private static void CompileShader(int shader) { // 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}"); + throw new Exception($"Error occurred whilst compiling Shader({shader}):\n{infoLog}"); } + + return shader; } - private static void LinkProgram(int program) + // This function creates a dictionary that maps uniform names to their location, + // this is used to speed up uniform location queries. + public static Dictionary CreateUniformLocationsDict(int program) { - // We link the program - GL.LinkProgram(program); + // First, we have to get the number of active uniforms in the shader. + GL.GetProgram(program, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms); - // Check for linking errors - GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code); - if (code != (int)All.True) + // Next, allocate the dictionary to hold the locations. + Dictionary uniformLocations = new Dictionary(); + + // Loop over all the uniforms, + for (var i = 0; i < numberOfUniforms; i++) { - // We can use `GL.GetProgramInfoLog(program)` to get information about the error. - throw new Exception($"Error occurred whilst linking Program({program})"); - } - } - } + // get the name of this uniform, + var key = GL.GetActiveUniform(program, i, out _, out _); - // A simple class that can be used to associate a uniform location dictionary to a shader program. - public class Shader - { - public readonly int Handle; + // get the location, + var location = GL.GetUniformLocation(program, key); - public readonly Dictionary UniformLocations; + // and then add it to the dictionary. + uniformLocations.Add(key, location); + } - public Shader(string vertexPath, string fragmentPath) - { - Handle = ShaderUtils.Compile(vertexPath, fragmentPath); - UniformLocations = ShaderUtils.CreateUniformLocationsDict(Handle); + return uniformLocations; } public Shader(int handle, Dictionary uniformLocations) diff --git a/Common/Texture.cs b/Common/Texture.cs index b9bde62..9e6bf0e 100755 --- a/Common/Texture.cs +++ b/Common/Texture.cs @@ -8,11 +8,9 @@ namespace LearnOpenTK.Common { // A helper class, much like Shader, meant to simplify loading textures. - public class Texture + public static class Texture { - public readonly int Handle; - - public static Texture LoadFromFile(string path) + public static int LoadFromFile(string path) { // Generate handle int handle = GL.GenTexture(); @@ -70,22 +68,7 @@ public static Texture LoadFromFile(string path) // 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); + return handle; } } } From 9b7ad9c17aa6bbd777c5c7a11a31632a7ae53c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4ger?= Date: Fri, 22 Aug 2025 00:44:19 +0200 Subject: [PATCH 3/4] Removed the Common project in favor of keeping the code in each chapter standalone. Removed the shader class in favour of the more straight forward int way. Changed to use ClientSize instead of Size in NativeWindowSettings. Updated to OpenTK 4.9.4. Updated StbImageSharp to 2.30.15. Revised a few comments. Removed all OpenTK.dll.config files. --- .../1-CreatingAWindow.csproj | 5 +- Chapter1/1-CreatingAWindow/Program.cs | 2 +- .../2-HelloTriangle/2-HelloTriangle.csproj | 18 +- Chapter1/2-HelloTriangle/Program.cs | 2 +- Chapter1/2-HelloTriangle/Shaders/shader.frag | 1 + Chapter1/2-HelloTriangle/Window.cs | 84 +++++++-- .../3-ElementBufferObjects.csproj | 9 +- Chapter1/3-ElementBufferObjects/Program.cs | 2 +- Chapter1/3-ElementBufferObjects/Window.cs | 63 ++++++- .../4-Shaders-InsAndOuts.csproj | 9 +- Chapter1/4-Shaders-InsAndOuts/Program.cs | 2 +- Chapter1/4-Shaders-InsAndOuts/Window.cs | 62 ++++++- .../4-Shaders-MoreAttributes.csproj | 9 +- Chapter1/4-Shaders-MoreAttributes/Program.cs | 2 +- Chapter1/4-Shaders-MoreAttributes/Window.cs | 61 ++++++- .../4-Shaders-Uniforms.csproj | 9 +- Chapter1/4-Shaders-Uniforms/Program.cs | 2 +- Chapter1/4-Shaders-Uniforms/Window.cs | 68 ++++++-- Chapter1/5-Textures/5-Textures.csproj | 9 +- Chapter1/5-Textures/Program.cs | 2 +- Chapter1/5-Textures/Window.cs | 137 +++++++++++++-- .../6-MultipleTextures.csproj | 9 +- Chapter1/6-MultipleTextures/Program.cs | 2 +- Chapter1/6-MultipleTextures/Window.cs | 97 +++++++++-- .../7-Transformations.csproj | 9 +- Chapter1/7-Transformations/Program.cs | 2 +- Chapter1/7-Transformations/Window.cs | 99 +++++++++-- .../8-CoordinatesSystems.csproj | 9 +- Chapter1/8-CoordinatesSystems/Program.cs | 2 +- Chapter1/8-CoordinatesSystems/Window.cs | 103 +++++++++-- Chapter1/9-Camera/9-Camera.csproj | 9 +- {Common => Chapter1/9-Camera}/Camera.cs | 2 +- Chapter1/9-Camera/Program.cs | 2 +- Chapter1/9-Camera/Window.cs | 105 +++++++++-- Chapter2/1-Colors/1-Colors.csproj | 9 +- Chapter2/1-Colors/Camera.cs | 119 +++++++++++++ Chapter2/1-Colors/OpenTK.dll.config | 25 --- Chapter2/1-Colors/Program.cs | 2 +- Chapter2/1-Colors/Window.cs | 83 +++++++-- .../2-BasicLighting/2-BasicLighting.csproj | 9 +- Chapter2/2-BasicLighting/Camera.cs | 119 +++++++++++++ Chapter2/2-BasicLighting/OpenTK.dll.config | 25 --- Chapter2/2-BasicLighting/Program.cs | 2 +- Chapter2/2-BasicLighting/Window.cs | 87 +++++++--- Chapter2/3-Materials/3-Materials.csproj | 9 +- Chapter2/3-Materials/Camera.cs | 119 +++++++++++++ Chapter2/3-Materials/OpenTK.dll.config | 25 --- Chapter2/3-Materials/Program.cs | 2 +- Chapter2/3-Materials/Window.cs | 96 ++++++++--- Chapter2/4-LightingMaps/4-LightingMaps.csproj | 9 +- Chapter2/4-LightingMaps/Camera.cs | 119 +++++++++++++ Chapter2/4-LightingMaps/OpenTK.dll.config | 25 --- Chapter2/4-LightingMaps/Program.cs | 2 +- Chapter2/4-LightingMaps/Window.cs | 129 +++++++++++--- .../5-LightCasters-DirectionalLights.csproj | 21 +-- .../Camera.cs | 119 +++++++++++++ .../OpenTK.dll.config | 25 --- .../Program.cs | 2 +- .../Window.cs | 129 +++++++++++--- .../5-LightCasters-PointLights.csproj | 21 +-- Chapter2/5-LightCasters-PointLights/Camera.cs | 119 +++++++++++++ .../OpenTK.dll.config | 25 --- .../5-LightCasters-PointLights/Program.cs | 2 +- Chapter2/5-LightCasters-PointLights/Window.cs | 134 ++++++++++---- .../5-LightCasters-Spotlight.csproj | 9 +- Chapter2/5-LightCasters-Spotlight/Camera.cs | 119 +++++++++++++ .../OpenTK.dll.config | 25 --- Chapter2/5-LightCasters-Spotlight/Program.cs | 2 +- Chapter2/5-LightCasters-Spotlight/Window.cs | 141 +++++++++++---- .../6-MultipleLights/6-MultipleLights.csproj | 9 +- Chapter2/6-MultipleLights/Camera.cs | 119 +++++++++++++ Chapter2/6-MultipleLights/OpenTK.dll.config | 25 --- Chapter2/6-MultipleLights/Program.cs | 2 +- Chapter2/6-MultipleLights/Window.cs | 163 +++++++++++++----- Common/Common.csproj | 12 -- Common/Shader.cs | 130 -------------- Common/Texture.cs | 74 -------- LearnOpenTK.sln | 13 +- 78 files changed, 2531 insertions(+), 932 deletions(-) rename {Common => Chapter1/9-Camera}/Camera.cs (99%) create mode 100644 Chapter2/1-Colors/Camera.cs delete mode 100644 Chapter2/1-Colors/OpenTK.dll.config create mode 100644 Chapter2/2-BasicLighting/Camera.cs delete mode 100644 Chapter2/2-BasicLighting/OpenTK.dll.config create mode 100644 Chapter2/3-Materials/Camera.cs delete mode 100644 Chapter2/3-Materials/OpenTK.dll.config create mode 100644 Chapter2/4-LightingMaps/Camera.cs delete mode 100644 Chapter2/4-LightingMaps/OpenTK.dll.config create mode 100644 Chapter2/5-LightCasters-DirectionalLights/Camera.cs delete mode 100644 Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config create mode 100644 Chapter2/5-LightCasters-PointLights/Camera.cs delete mode 100644 Chapter2/5-LightCasters-PointLights/OpenTK.dll.config create mode 100644 Chapter2/5-LightCasters-Spotlight/Camera.cs delete mode 100644 Chapter2/5-LightCasters-Spotlight/OpenTK.dll.config create mode 100644 Chapter2/6-MultipleLights/Camera.cs delete mode 100644 Chapter2/6-MultipleLights/OpenTK.dll.config delete mode 100644 Common/Common.csproj delete mode 100644 Common/Shader.cs delete mode 100755 Common/Texture.cs 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/Window.cs b/Chapter1/2-HelloTriangle/Window.cs index 1f57f75..3bf2a8f 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 = Shader.FromFile("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. - GL.UseProgram(_shader.Handle); + 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 - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); // Bind the VAO GL.BindVertexArray(_vertexArrayObject); @@ -182,7 +183,7 @@ protected override void OnResize(ResizeEventArgs 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, FramebufferSize.X, FramebufferSize.Y); } // 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/Window.cs b/Chapter1/3-ElementBufferObjects/Window.cs index 2208ca2..82d4868 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _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); - GL.UseProgram(_shader.Handle); + 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. @@ -118,7 +119,53 @@ protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/4-Shaders-InsAndOuts/Window.cs index dce7624..89ec69f 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,9 +56,8 @@ protected override void OnLoad() GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount); Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}"); - _shader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - - GL.UseProgram(_shader.Handle); + _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag")); + GL.UseProgram(_shader); } protected override void OnRenderFrame(FrameEventArgs e) @@ -67,7 +66,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.Clear(ClearBufferMask.ColorBufferBit); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); GL.BindVertexArray(_vertexArrayObject); @@ -92,7 +91,52 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/4-Shaders-MoreAttributes/Window.cs index 4c5f74f..66a15c1 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _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); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); GL.BindVertexArray(_vertexArrayObject); @@ -102,7 +102,52 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/4-Shaders-Uniforms/Window.cs index 5b146dc..a5ea997 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _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); - GL.UseProgram(_shader.Handle); + 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). @@ -116,7 +117,52 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/5-Textures/Window.cs index 4689e9e..ba1ade3 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,7 +34,7 @@ public class Window : GameWindow private int _vertexArrayObject; - private Shader _shader; + private int _shader; // For documentation on this, check Texture.cs. private int _texture; @@ -59,10 +61,6 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); - // 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. @@ -77,7 +75,18 @@ protected override void OnLoad() GL.EnableVertexAttribArray(texCoordLocation); GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); - _texture = Texture.LoadFromFile("Resources/container.png"); + // 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); } @@ -93,7 +102,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, _texture); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); @@ -116,7 +125,113 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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) + { + // 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); + } + + // 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/Window.cs b/Chapter1/6-MultipleTextures/Window.cs index c02ceb0..65f05d6 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,7 +31,7 @@ public class Window : GameWindow private int _vertexArrayObject; - private Shader _shader; + private int _shader; private int _texture; @@ -58,8 +60,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag")); + GL.UseProgram(_shader); var vertexLocation = 0; // The location of aPos GL.EnableVertexAttribArray(vertexLocation); @@ -69,7 +71,7 @@ protected override void OnLoad() 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. @@ -79,15 +81,15 @@ protected override void OnLoad() 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. GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); // 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. - GL.Uniform1(_shader.UniformLocations["texture0"], 0); - GL.Uniform1(_shader.UniformLocations["texture1"], 1); + GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0); + GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1); } protected override void OnRenderFrame(FrameEventArgs e) @@ -103,7 +105,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); @@ -126,7 +128,78 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/7-Transformations/Window.cs index e931bb2..002c856 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,7 +41,7 @@ public class Window : GameWindow private int _vertexArrayObject; - private Shader _shader; + private int _shader; private int _texture; @@ -68,8 +70,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag")); + GL.UseProgram(_shader); var vertexLocation = 0; // The location of aPos GL.EnableVertexAttribArray(vertexLocation); @@ -79,16 +81,16 @@ protected override void OnLoad() 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"); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, _texture); - _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); + _texture2 = LoadTextureFromFile("Resources/awesomeface.png"); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.Uniform1(_shader.UniformLocations["texture0"], 0); - GL.Uniform1(_shader.UniformLocations["texture1"], 1); + GL.Uniform1(GL.GetUniformLocation(_shader, "texture0"), 0); + GL.Uniform1(GL.GetUniformLocation(_shader, "texture1"), 1); } protected override void OnRenderFrame(FrameEventArgs e) @@ -130,11 +132,11 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.UseProgram(_shader.Handle); + 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. - GL.UniformMatrix4(_shader.UniformLocations["transform"], true, ref 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. @@ -159,7 +161,78 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/8-CoordinatesSystems/Window.cs index bcd6126..f0763c0 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,7 +35,7 @@ public class Window : GameWindow private int _vertexArrayObject; - private Shader _shader; + private int _shader; private int _texture; @@ -78,8 +80,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag")); + GL.UseProgram(_shader); var vertexLocation = 0; // The location of aPos GL.EnableVertexAttribArray(vertexLocation); @@ -89,16 +91,16 @@ protected override void OnLoad() 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"); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, _texture); - _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); + _texture2 = LoadTextureFromFile("Resources/awesomeface.png"); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.Uniform1(_shader.UniformLocations["texture0"], 0); - GL.Uniform1(_shader.UniformLocations["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. @@ -131,7 +133,7 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); // Finally, we have the model matrix. This determines the position of the model. var model = Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time)); @@ -145,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. - GL.UniformMatrix4(_shader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_shader.UniformLocations["view"], true, ref _view); - GL.UniformMatrix4(_shader.UniformLocations["projection"], true, ref _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); @@ -170,7 +172,78 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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; } } } \ 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/Window.cs b/Chapter1/9-Camera/Window.cs index 4ed2565..a310d6e 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,7 +41,7 @@ public class Window : GameWindow private int _vertexArrayObject; - private Shader _shader; + private int _shader; private int _texture; @@ -84,8 +85,8 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject); GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw); - _shader = Shader.FromFile("Shaders/shader.vert", "Shaders/shader.frag"); - GL.UseProgram(_shader.Handle); + _shader = CompileProgram(File.ReadAllText("Shaders/shader.vert"), File.ReadAllText("Shaders/shader.frag")); + GL.UseProgram(_shader); var vertexLocation = 0; // The location of aPos GL.EnableVertexAttribArray(vertexLocation); @@ -95,16 +96,16 @@ protected override void OnLoad() 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"); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, _texture); - _texture2 = Texture.LoadFromFile("Resources/awesomeface.png"); + _texture2 = LoadTextureFromFile("Resources/awesomeface.png"); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.Uniform1(_shader.UniformLocations["texture0"], 0); - GL.Uniform1(_shader.UniformLocations["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. @@ -129,15 +130,15 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _texture2); - GL.UseProgram(_shader.Handle); + GL.UseProgram(_shader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); var model = Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time)); - GL.UniformMatrix4(_shader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_shader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_shader.UniformLocations["projection"], true, ref 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); @@ -223,9 +224,81 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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 70ef4d0..85ad435 100644 --- a/Chapter2/1-Colors/Window.cs +++ b/Chapter2/1-Colors/Window.cs @@ -1,10 +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.Graphics.GL; +using OpenTK.Windowing.GraphicsLibraryFramework; +using System; +using System.IO; namespace LearnOpenTK { @@ -78,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; @@ -108,8 +109,8 @@ 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 = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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 @@ -147,7 +148,7 @@ protected override void OnRenderFrame(FrameEventArgs e) // Draw the model/cube with the lighting shader GL.BindVertexArray(_vaoModel); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); @@ -155,26 +156,26 @@ protected override void OnRenderFrame(FrameEventArgs e) // Matrix4.Identity is used as the matrix, since we just want to draw it at 0, 0, 0 Matrix4 model = Matrix4.Identity; - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + 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(_lightingShader.UniformLocations["objectColor"], new Vector3(1.0f, 0.5f, 0.31f)); - GL.Uniform3(_lightingShader.UniformLocations["lightColor"], new Vector3(1.0f, 1.0f, 1.0f)); + 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); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); // We scale the lamp cube down a bit to make it less dominant Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -254,8 +255,54 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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/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 61d50be..f9b5323 100644 --- a/Chapter2/2-BasicLighting/Window.cs +++ b/Chapter2/2-BasicLighting/Window.cs @@ -1,10 +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.Graphics.GL; +using OpenTK.Windowing.GraphicsLibraryFramework; +using System; +using System.IO; namespace LearnOpenTK { @@ -70,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; @@ -97,8 +98,8 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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(); @@ -143,33 +144,33 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); Matrix4 model = Matrix4.Identity; - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + 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(_lightingShader.UniformLocations["objectColor"], new Vector3(1.0f, 0.5f, 0.31f)); - GL.Uniform3(_lightingShader.UniformLocations["lightColor"], new Vector3(1.0f, 1.0f, 1.0f)); - GL.Uniform3(_lightingShader.UniformLocations["lightPos"], _lightPos); - GL.Uniform3(_lightingShader.UniformLocations["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); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -249,8 +250,54 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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 516153e..4dea086 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,8 +96,8 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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(); @@ -134,24 +134,24 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoModel); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); Matrix4 model = Matrix4.Identity; - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + 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(_lightingShader.UniformLocations["viewPos"], _camera.Position); + 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 - GL.Uniform3(_lightingShader.UniformLocations["material.ambient"], new Vector3(1.0f, 0.5f, 0.31f)); - GL.Uniform3(_lightingShader.UniformLocations["material.diffuse"], new Vector3(1.0f, 0.5f, 0.31f)); - GL.Uniform3(_lightingShader.UniformLocations["material.specular"], new Vector3(1.0f, 0.5f, 0.5f)); - GL.Uniform1(_lightingShader.UniformLocations["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; @@ -164,22 +164,22 @@ protected override void OnRenderFrame(FrameEventArgs e) Vector3 ambientColor = lightColor * new Vector3(0.2f); Vector3 diffuseColor = lightColor * new Vector3(0.5f); - GL.Uniform3(_lightingShader.UniformLocations["light.position"], _lightPos); - GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], ambientColor); - GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], diffuseColor); - GL.Uniform3(_lightingShader.UniformLocations["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); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -259,8 +259,54 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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/Window.cs b/Chapter2/4-LightingMaps/Window.cs index 2fb4ec3..1010ce7 100644 --- a/Chapter2/4-LightingMaps/Window.cs +++ b/Chapter2/4-LightingMaps/Window.cs @@ -1,10 +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.Graphics.GL; +using OpenTK.Windowing.GraphicsLibraryFramework; +using StbImageSharp; +using System; +using System.IO; namespace LearnOpenTK { @@ -68,9 +69,9 @@ 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. @@ -102,8 +103,8 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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(); @@ -138,8 +139,8 @@ protected override void OnLoad() // 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); @@ -161,40 +162,40 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _specularMap); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); Matrix4 model = Matrix4.Identity; - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + 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(_lightingShader.UniformLocations["viewPos"], _camera.Position); + 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(_lightingShader.UniformLocations["material.diffuse"], 0); - GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); - GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); - - GL.Uniform3(_lightingShader.UniformLocations["light.position"], _lightPos); - GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); - GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); - GL.Uniform3(_lightingShader.UniformLocations["light.specular"], new Vector3(1.0f)); + 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); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -274,8 +275,80 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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 20342b8..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,22 +12,7 @@ - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + \ 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/Window.cs b/Chapter2/5-LightCasters-DirectionalLights/Window.cs index 4856520..6f0869a 100644 --- a/Chapter2/5-LightCasters-DirectionalLights/Window.cs +++ b/Chapter2/5-LightCasters-DirectionalLights/Window.cs @@ -1,10 +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 System.Reflection; +using OpenTK.Windowing.GraphicsLibraryFramework; +using StbImageSharp; +using System; +using System.IO; namespace LearnOpenTK { @@ -83,9 +84,9 @@ public class Window : GameWindow private int _vaoLamp; - private Shader _lampShader; + private int _lampShader; - private Shader _lightingShader; + private int _lightingShader; private int _diffuseMap; @@ -114,9 +115,9 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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); @@ -145,8 +146,8 @@ protected override void OnLoad() 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); @@ -166,26 +167,26 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _specularMap); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection); - GL.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); + 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(_lightingShader.UniformLocations["material.diffuse"], 0); - GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); - GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); + 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 - GL.Uniform3(_lightingShader.UniformLocations["light.direction"], new Vector3(-0.2f, -1.0f, -0.3f)); - GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); - GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); - GL.Uniform3(_lightingShader.UniformLocations["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++) @@ -196,7 +197,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 opengl - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -204,13 +205,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -290,8 +291,80 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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 20342b8..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,22 +12,7 @@ - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + \ 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/Window.cs b/Chapter2/5-LightCasters-PointLights/Window.cs index 4948e2d..9c8635b 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,9 +85,9 @@ public class Window : GameWindow private int _vaoLamp; - private Shader _lampShader; + private int _lampShader; - private Shader _lightingShader; + private int _lightingShader; private int _diffuseMap; @@ -114,9 +116,9 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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); @@ -143,8 +145,8 @@ protected override void OnLoad() 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); @@ -164,29 +166,29 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _specularMap); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection); - GL.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); + 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(_lightingShader.UniformLocations["material.diffuse"], 0); - GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); - GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); + 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 - GL.Uniform3(_lightingShader.UniformLocations["light.position"], _lightPos); - GL.Uniform1(_lightingShader.UniformLocations["light.constant"], 1.0f); - GL.Uniform1(_lightingShader.UniformLocations["light.linear"], 0.09f); - GL.Uniform1(_lightingShader.UniformLocations["light.quadratic"], 0.032f); - GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); - GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); - GL.Uniform3(_lightingShader.UniformLocations["light.specular"], new Vector3(1.0f)); + 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++) @@ -198,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 - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -206,13 +208,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -292,8 +294,80 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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/Window.cs b/Chapter2/5-LightCasters-Spotlight/Window.cs index ec094b2..9f8f168 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,9 +86,9 @@ public class Window : GameWindow private int _vaoLamp; - private Shader _lampShader; + private int _lampShader; - private Shader _lightingShader; + private int _lightingShader; private int _diffuseMap; @@ -116,9 +117,9 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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); @@ -145,8 +146,8 @@ protected override void OnLoad() 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); @@ -166,32 +167,32 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _specularMap); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection); - GL.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); + 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(_lightingShader.UniformLocations["material.diffuse"], 0); - GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); - GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); + 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 - GL.Uniform3(_lightingShader.UniformLocations["light.position"], _camera.Position); - GL.Uniform3(_lightingShader.UniformLocations["light.direction"], _camera.Front); - GL.Uniform1(_lightingShader.UniformLocations["light.cutOff"], MathF.Cos(MathHelper.DegreesToRadians(12.5f))); - GL.Uniform1(_lightingShader.UniformLocations["light.outerCutOff"], MathF.Cos(MathHelper.DegreesToRadians(17.5f))); - GL.Uniform1(_lightingShader.UniformLocations["light.constant"], 1.0f); - GL.Uniform1(_lightingShader.UniformLocations["light.linear"], 0.09f); - GL.Uniform1(_lightingShader.UniformLocations["light.quadratic"], 0.032f); - GL.Uniform3(_lightingShader.UniformLocations["light.ambient"], new Vector3(0.2f)); - GL.Uniform3(_lightingShader.UniformLocations["light.diffuse"], new Vector3(0.5f)); - GL.Uniform3(_lightingShader.UniformLocations["light.specular"], new Vector3(1.0f)); + 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++) @@ -202,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 - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model); // At last we draw all our cubes GL.DrawArrays(PrimitiveType.Triangles, 0, 36); @@ -210,13 +211,13 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindVertexArray(_vaoLamp); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); Matrix4 lampMatrix = Matrix4.CreateScale(0.2f) * Matrix4.CreateTranslation(_lightPos); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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); @@ -296,8 +297,80 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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/Window.cs b/Chapter2/6-MultipleLights/Window.cs index 2052470..11cf362 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,9 +88,9 @@ public class Window : GameWindow private int _vaoLamp; - private Shader _lampShader; + private int _lampShader; - private Shader _lightingShader; + private int _lightingShader; private int _diffuseMap; @@ -118,9 +119,9 @@ protected override void OnLoad() GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject); GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw); - _lightingShader = Shader.FromFile("Shaders/shader.vert", "Shaders/lighting.frag"); - _lampShader = Shader.FromFile("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); @@ -147,8 +148,8 @@ protected override void OnLoad() 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); @@ -167,19 +168,19 @@ protected override void OnRenderFrame(FrameEventArgs e) GL.BindTexture(TextureTarget.Texture2D, _diffuseMap); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, _specularMap); - GL.UseProgram(_lightingShader.Handle); + GL.UseProgram(_lightingShader); Matrix4 projection = _camera.GetProjectionMatrix(); Matrix4 view = _camera.GetViewMatrix(); - GL.UniformMatrix4(_lightingShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lightingShader.UniformLocations["projection"], true, ref projection); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "view"), true, ref view); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "projection"), true, ref projection); - GL.Uniform3(_lightingShader.UniformLocations["viewPos"], _camera.Position); + GL.Uniform3(GL.GetUniformLocation(_lightingShader, "viewPos"), _camera.Position); - GL.Uniform1(_lightingShader.UniformLocations["material.diffuse"], 0); - GL.Uniform1(_lightingShader.UniformLocations["material.specular"], 1); - GL.Uniform1(_lightingShader.UniformLocations["material.shininess"], 32.0f); + 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 @@ -188,57 +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 - GL.Uniform3(_lightingShader.UniformLocations["dirLight.direction"], new Vector3(-0.2f, -1.0f, -0.3f)); - GL.Uniform3(_lightingShader.UniformLocations["dirLight.ambient"], new Vector3(0.05f, 0.05f, 0.05f)); - GL.Uniform3(_lightingShader.UniformLocations["dirLight.diffuse"], new Vector3(0.4f, 0.4f, 0.4f)); - GL.Uniform3(_lightingShader.UniformLocations["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++) { - GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].position"], _pointLightPositions[i]); - GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].ambient"], new Vector3(0.05f, 0.05f, 0.05f)); - GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].diffuse"], new Vector3(0.8f, 0.8f, 0.8f)); - GL.Uniform3(_lightingShader.UniformLocations[$"pointLights[{i}].specular"], new Vector3(1.0f, 1.0f, 1.0f)); - GL.Uniform1(_lightingShader.UniformLocations[$"pointLights[{i}].constant"], 1.0f); - GL.Uniform1(_lightingShader.UniformLocations[$"pointLights[{i}].linear"], 0.09f); - GL.Uniform1(_lightingShader.UniformLocations[$"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 - GL.Uniform3(_lightingShader.UniformLocations["spotLight.position"], _camera.Position); - GL.Uniform3(_lightingShader.UniformLocations["spotLight.direction"], _camera.Front); - GL.Uniform3(_lightingShader.UniformLocations["spotLight.ambient"], new Vector3(0.0f, 0.0f, 0.0f)); - GL.Uniform3(_lightingShader.UniformLocations["spotLight.diffuse"], new Vector3(1.0f, 1.0f, 1.0f)); - GL.Uniform3(_lightingShader.UniformLocations["spotLight.specular"], new Vector3(1.0f, 1.0f, 1.0f)); - GL.Uniform1(_lightingShader.UniformLocations["spotLight.constant"], 1.0f); - GL.Uniform1(_lightingShader.UniformLocations["spotLight.linear"], 0.09f); - GL.Uniform1(_lightingShader.UniformLocations["spotLight.quadratic"], 0.032f); - GL.Uniform1(_lightingShader.UniformLocations["spotLight.cutOff"], MathF.Cos(MathHelper.DegreesToRadians(12.5f))); - GL.Uniform1(_lightingShader.UniformLocations["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); - GL.UniformMatrix4(_lightingShader.UniformLocations["model"], true, ref model); + GL.UniformMatrix4(GL.GetUniformLocation(_lightingShader, "model"), true, ref model); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); } GL.BindVertexArray(_vaoLamp); - GL.UseProgram(_lampShader.Handle); + GL.UseProgram(_lampShader); - GL.UniformMatrix4(_lampShader.UniformLocations["view"], true, ref view); - GL.UniformMatrix4(_lampShader.UniformLocations["projection"], true, ref projection); + 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) * Matrix4.CreateTranslation(_pointLightPositions[i]); - GL.UniformMatrix4(_lampShader.UniformLocations["model"], true, ref lampMatrix); + GL.UniformMatrix4(GL.GetUniformLocation(_lampShader, "model"), true, ref lampMatrix); GL.DrawArrays(PrimitiveType.Triangles, 0, 36); } @@ -319,8 +320,80 @@ protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); - GL.Viewport(0, 0, Size.X, Size.Y); + GL.Viewport(0, 0, FramebufferSize.X, FramebufferSize.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 a1418f4..0000000 --- a/Common/Shader.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Collections.Generic; -using OpenTK.Graphics.OpenGL4; -using OpenTK.Mathematics; -using System.Reflection.Metadata; -using System.ComponentModel; -using OpenTK.Compute.OpenCL; - -namespace LearnOpenTK.Common -{ - // A simple class that can be used to associate a uniform location dictionary to a shader program. - public class Shader - { - public readonly int Handle; - - public readonly Dictionary UniformLocations; - - public static Shader FromFile(string vertexPath, string fragmentPath) - { - // 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 fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel. - - // Load vertex shader and compile - string vertexSource = File.ReadAllText(vertexPath); - string fragmentSource = File.ReadAllText(fragmentPath); - - int program = CompileProgram(vertexSource, fragmentSource); - - Dictionary uniformLocations = CreateUniformLocationsDict(program); - - return new Shader(program, uniformLocations); - } - - 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; - } - - // This function creates a dictionary that maps uniform names to their location, - // this is used to speed up uniform location queries. - public static Dictionary CreateUniformLocationsDict(int program) - { - // First, we have to get the number of active uniforms in the shader. - GL.GetProgram(program, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms); - - // Next, allocate the dictionary to hold the locations. - Dictionary 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(program, i, out _, out _); - - // get the location, - var location = GL.GetUniformLocation(program, key); - - // and then add it to the dictionary. - uniformLocations.Add(key, location); - } - - return uniformLocations; - } - - public Shader(int handle, Dictionary uniformLocations) - { - Handle = handle; - UniformLocations = uniformLocations; - } - } -} diff --git a/Common/Texture.cs b/Common/Texture.cs deleted file mode 100755 index 9e6bf0e..0000000 --- a/Common/Texture.cs +++ /dev/null @@ -1,74 +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 static class Texture - { - public static int 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 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 From 57e775eab0532c32a10805652897168021d9d550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4ger?= Date: Fri, 22 Aug 2025 00:47:51 +0200 Subject: [PATCH 4/4] Fix framebuffer resize event. --- Chapter1/2-HelloTriangle/Window.cs | 6 +++--- Chapter1/3-ElementBufferObjects/Window.cs | 6 +++--- Chapter1/4-Shaders-InsAndOuts/Window.cs | 6 +++--- Chapter1/4-Shaders-MoreAttributes/Window.cs | 6 +++--- Chapter1/4-Shaders-Uniforms/Window.cs | 6 +++--- Chapter1/5-Textures/Window.cs | 6 +++--- Chapter1/6-MultipleTextures/Window.cs | 6 +++--- Chapter1/7-Transformations/Window.cs | 6 +++--- Chapter1/8-CoordinatesSystems/Window.cs | 6 +++--- Chapter1/9-Camera/Window.cs | 6 +++--- Chapter2/1-Colors/Window.cs | 6 +++--- Chapter2/2-BasicLighting/Window.cs | 6 +++--- Chapter2/3-Materials/Window.cs | 6 +++--- Chapter2/4-LightingMaps/Window.cs | 6 +++--- Chapter2/5-LightCasters-DirectionalLights/Window.cs | 6 +++--- Chapter2/5-LightCasters-PointLights/Window.cs | 6 +++--- Chapter2/5-LightCasters-Spotlight/Window.cs | 6 +++--- Chapter2/6-MultipleLights/Window.cs | 6 +++--- 18 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Chapter1/2-HelloTriangle/Window.cs b/Chapter1/2-HelloTriangle/Window.cs index 3bf2a8f..a5312b5 100644 --- a/Chapter1/2-HelloTriangle/Window.cs +++ b/Chapter1/2-HelloTriangle/Window.cs @@ -177,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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } // Now, for cleanup. diff --git a/Chapter1/3-ElementBufferObjects/Window.cs b/Chapter1/3-ElementBufferObjects/Window.cs index 82d4868..d98b245 100644 --- a/Chapter1/3-ElementBufferObjects/Window.cs +++ b/Chapter1/3-ElementBufferObjects/Window.cs @@ -116,11 +116,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/4-Shaders-InsAndOuts/Window.cs b/Chapter1/4-Shaders-InsAndOuts/Window.cs index 89ec69f..e066ea0 100644 --- a/Chapter1/4-Shaders-InsAndOuts/Window.cs +++ b/Chapter1/4-Shaders-InsAndOuts/Window.cs @@ -87,11 +87,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/4-Shaders-MoreAttributes/Window.cs b/Chapter1/4-Shaders-MoreAttributes/Window.cs index 66a15c1..a7c81cc 100644 --- a/Chapter1/4-Shaders-MoreAttributes/Window.cs +++ b/Chapter1/4-Shaders-MoreAttributes/Window.cs @@ -98,11 +98,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/4-Shaders-Uniforms/Window.cs b/Chapter1/4-Shaders-Uniforms/Window.cs index a5ea997..c74bd02 100644 --- a/Chapter1/4-Shaders-Uniforms/Window.cs +++ b/Chapter1/4-Shaders-Uniforms/Window.cs @@ -113,11 +113,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/5-Textures/Window.cs b/Chapter1/5-Textures/Window.cs index ba1ade3..2548dbf 100644 --- a/Chapter1/5-Textures/Window.cs +++ b/Chapter1/5-Textures/Window.cs @@ -121,11 +121,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/6-MultipleTextures/Window.cs b/Chapter1/6-MultipleTextures/Window.cs index 65f05d6..4e902ee 100644 --- a/Chapter1/6-MultipleTextures/Window.cs +++ b/Chapter1/6-MultipleTextures/Window.cs @@ -124,11 +124,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/7-Transformations/Window.cs b/Chapter1/7-Transformations/Window.cs index 002c856..d017c87 100644 --- a/Chapter1/7-Transformations/Window.cs +++ b/Chapter1/7-Transformations/Window.cs @@ -157,11 +157,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/8-CoordinatesSystems/Window.cs b/Chapter1/8-CoordinatesSystems/Window.cs index f0763c0..853c397 100644 --- a/Chapter1/8-CoordinatesSystems/Window.cs +++ b/Chapter1/8-CoordinatesSystems/Window.cs @@ -168,11 +168,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); } private static int CompileProgram(string vertexSource, string fragmentSource) diff --git a/Chapter1/9-Camera/Window.cs b/Chapter1/9-Camera/Window.cs index a310d6e..c8fdded 100644 --- a/Chapter1/9-Camera/Window.cs +++ b/Chapter1/9-Camera/Window.cs @@ -220,11 +220,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); // We need to update the aspect ratio once the window has been resized. _camera.AspectRatio = Size.X / (float)Size.Y; diff --git a/Chapter2/1-Colors/Window.cs b/Chapter2/1-Colors/Window.cs index 85ad435..8b44af1 100644 --- a/Chapter2/1-Colors/Window.cs +++ b/Chapter2/1-Colors/Window.cs @@ -251,11 +251,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/2-BasicLighting/Window.cs b/Chapter2/2-BasicLighting/Window.cs index f9b5323..24f5883 100644 --- a/Chapter2/2-BasicLighting/Window.cs +++ b/Chapter2/2-BasicLighting/Window.cs @@ -246,11 +246,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/3-Materials/Window.cs b/Chapter2/3-Materials/Window.cs index 4dea086..d5dc10b 100644 --- a/Chapter2/3-Materials/Window.cs +++ b/Chapter2/3-Materials/Window.cs @@ -255,11 +255,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/4-LightingMaps/Window.cs b/Chapter2/4-LightingMaps/Window.cs index 1010ce7..2f8d452 100644 --- a/Chapter2/4-LightingMaps/Window.cs +++ b/Chapter2/4-LightingMaps/Window.cs @@ -271,11 +271,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/5-LightCasters-DirectionalLights/Window.cs b/Chapter2/5-LightCasters-DirectionalLights/Window.cs index 6f0869a..7a71878 100644 --- a/Chapter2/5-LightCasters-DirectionalLights/Window.cs +++ b/Chapter2/5-LightCasters-DirectionalLights/Window.cs @@ -287,11 +287,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/5-LightCasters-PointLights/Window.cs b/Chapter2/5-LightCasters-PointLights/Window.cs index 9c8635b..295d5ad 100644 --- a/Chapter2/5-LightCasters-PointLights/Window.cs +++ b/Chapter2/5-LightCasters-PointLights/Window.cs @@ -290,11 +290,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/5-LightCasters-Spotlight/Window.cs b/Chapter2/5-LightCasters-Spotlight/Window.cs index 9f8f168..9782fb9 100644 --- a/Chapter2/5-LightCasters-Spotlight/Window.cs +++ b/Chapter2/5-LightCasters-Spotlight/Window.cs @@ -293,11 +293,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; } diff --git a/Chapter2/6-MultipleLights/Window.cs b/Chapter2/6-MultipleLights/Window.cs index 11cf362..03cc0dd 100644 --- a/Chapter2/6-MultipleLights/Window.cs +++ b/Chapter2/6-MultipleLights/Window.cs @@ -316,11 +316,11 @@ 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, FramebufferSize.X, FramebufferSize.Y); + GL.Viewport(0, 0, e.Width, e.Height); _camera.AspectRatio = Size.X / (float)Size.Y; }