Create Tutorial 2.2 to demonstrate Camera basics (#221)

* start of tutorial2.2

* Rename Tutorial 1.6 to Tutorial 2.1 to align with planned documentation sections

* Add Tutorial2.2 to demonstrate moving the viewport with WASD, looking around with Mouse movement, and Zooming in and out with the Scroll Wheel
This commit is contained in:
RhysWhy 2020-07-09 20:19:31 +01:00 коммит произвёл GitHub
Родитель d1385a9a5f
Коммит f057c16d99
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 566 добавлений и 1 удалений

Просмотреть файл

@ -293,7 +293,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Windowing.Extensio
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FramebufferResized", "src\Lab\FramebufferResized\FramebufferResized.csproj", "{7AF3C6CA-64FE-43F3-BD7B-C57C83AFAB79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tutorial 1.6 - Co-ordinate Systems", "examples\CSharp\Tutorial 1.6 - Co-ordinate Systems\Tutorial 1.6 - Co-ordinate Systems.csproj", "{7874B5F2-2428-4D4E-8FDB-8634573163B8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tutorial 2.1 - Co-ordinate Systems", "examples\CSharp\Tutorial 2.1 - Co-ordinate Systems\Tutorial 2.1 - Co-ordinate Systems.csproj", "{7874B5F2-2428-4D4E-8FDB-8634573163B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tutorial 2.2 - Camera", "examples\CSharp\Tutorial 2.2 - Camera\Tutorial 2.2 - Camera.csproj", "{4FBE561E-7FF9-4D69-91D5-945DE9A20291}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -1832,6 +1834,18 @@ Global
{7874B5F2-2428-4D4E-8FDB-8634573163B8}.Release|x64.Build.0 = Release|Any CPU
{7874B5F2-2428-4D4E-8FDB-8634573163B8}.Release|x86.ActiveCfg = Release|Any CPU
{7874B5F2-2428-4D4E-8FDB-8634573163B8}.Release|x86.Build.0 = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|x64.ActiveCfg = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|x64.Build.0 = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|x86.ActiveCfg = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Debug|x86.Build.0 = Debug|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|Any CPU.Build.0 = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|x64.ActiveCfg = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|x64.Build.0 = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|x86.ActiveCfg = Release|Any CPU
{4FBE561E-7FF9-4D69-91D5-945DE9A20291}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
@ -1969,6 +1983,7 @@ Global
{F5D5B4B4-58B7-48D9-881B-DD6A4001793E} = {3501BAD6-406A-49BC-BE0E-5A49A3AAAE6A}
{7AF3C6CA-64FE-43F3-BD7B-C57C83AFAB79} = {DFA0E841-33E5-4533-AF00-964E21A141B8}
{7874B5F2-2428-4D4E-8FDB-8634573163B8} = {E1F91563-7277-4E9B-A3B7-8D5FD9802A4A}
{4FBE561E-7FF9-4D69-91D5-945DE9A20291} = {E1F91563-7277-4E9B-A3B7-8D5FD9802A4A}
EndGlobalSection
EndGlobalSection
EndGlobal

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 11 KiB

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Просмотреть файл

@ -0,0 +1,36 @@
using Silk.NET.OpenGL;
using System;
namespace Tutorial
{
public class BufferObject<TDataType> : IDisposable
where TDataType : unmanaged
{
private uint _handle;
private BufferTargetARB _bufferType;
private GL _gl;
public unsafe BufferObject(GL gl, Span<TDataType> data, BufferTargetARB bufferType)
{
_gl = gl;
_bufferType = bufferType;
_handle = _gl.GenBuffer();
Bind();
fixed (void* d = data)
{
_gl.BufferData(bufferType, (UIntPtr)(data.Length * sizeof(TDataType)), d, BufferUsageARB.StaticDraw);
}
}
public void Bind()
{
_gl.BindBuffer(_bufferType, _handle);
}
public void Dispose()
{
_gl.DeleteBuffer(_handle);
}
}
}

Просмотреть файл

@ -0,0 +1,242 @@
using Silk.NET.Input;
using Silk.NET.Input.Common;
using Silk.NET.OpenGL;
using Silk.NET.Windowing;
using Silk.NET.Windowing.Common;
using System;
using System.Drawing;
using System.Linq;
using System.Numerics;
namespace Tutorial
{
class Program
{
private static IWindow window;
private static GL Gl;
private static IKeyboard primaryKeyboard;
private const int Width = 800;
private const int Height = 700;
private static BufferObject<float> Vbo;
private static BufferObject<uint> Ebo;
private static VertexArrayObject<float, uint> Vao;
private static Texture Texture;
private static Shader Shader;
//Setup the cameras location, directions, and movement speed
private static Vector3 CameraPosition = new Vector3(0.0f, 0.0f, 3.0f);
private static Vector3 CameraFront = new Vector3(0.0f, 0.0f, -1.0f);
private static Vector3 CameraUp = new Vector3(0.0f, 1.0f, 3.0f);
private static Vector3 CameraDirection = Vector3.Zero;
private static float CameraYaw = -90f;
private static float CameraPitch = 0f;
private static float CameraZoom = 45f;
//Used to track change in mouse movement to allow for moving of the Camera
private static PointF LastMousePosition;
//Track when the window started so we can use the time elapsed to rotate the cube
private static DateTime StartTime;
private static readonly float[] Vertices =
{
//X Y Z U V
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
private static readonly uint[] Indices =
{
0, 1, 3,
1, 2, 3
};
private static void Main(string[] args)
{
var options = WindowOptions.Default;
options.Size = new Size(800, 600);
options.Title = "LearnOpenGL with Silk.NET";
window = Window.Create(options);
window.Load += OnLoad;
window.Update += OnUpdate;
window.Render += OnRender;
window.Closing += OnClose;
window.Run();
}
private static void OnLoad()
{
StartTime = DateTime.UtcNow;
IInputContext input = window.CreateInput();
primaryKeyboard = input.Keyboards.FirstOrDefault();
if (primaryKeyboard != null)
{
primaryKeyboard.KeyDown += KeyDown;
}
for (int i = 0; i < input.Mice.Count; i++)
{
input.Mice[i].MouseMove += OnMouseMove;
input.Mice[i].Scroll += OnMouseWheel;
}
Gl = GL.GetApi(window);
Ebo = new BufferObject<uint>(Gl, Indices, BufferTargetARB.ElementArrayBuffer);
Vbo = new BufferObject<float>(Gl, Vertices, BufferTargetARB.ArrayBuffer);
Vao = new VertexArrayObject<float, uint>(Gl, Vbo, Ebo);
Vao.VertexAttributePointer(0, 3, VertexAttribPointerType.Float, 5, 0);
Vao.VertexAttributePointer(1, 2, VertexAttribPointerType.Float, 5, 3);
Shader = new Shader(Gl, "shader.vert", "shader.frag");
Texture = new Texture(Gl, "silk.png");
}
private static unsafe void OnUpdate(double deltaTime)
{
var moveSpeed = 2.5f * (float)deltaTime;
if (primaryKeyboard.IsKeyPressed(Key.W))
{
//Move forwards
CameraPosition += moveSpeed * CameraFront;
}
if (primaryKeyboard.IsKeyPressed(Key.S))
{
//Move backwards
CameraPosition -= moveSpeed * CameraFront;
}
if (primaryKeyboard.IsKeyPressed(Key.A))
{
//Move left
CameraPosition -= Vector3.Normalize(Vector3.Cross(CameraFront, CameraUp)) * moveSpeed;
}
if (primaryKeyboard.IsKeyPressed(Key.D))
{
//Move right
CameraPosition += Vector3.Normalize(Vector3.Cross(CameraFront, CameraUp)) * moveSpeed;
}
}
private static unsafe void OnRender(double deltaTime)
{
Gl.Enable(EnableCap.DepthTest);
Gl.Clear((uint)(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit));
Vao.Bind();
Texture.Bind();
Shader.Use();
Shader.SetUniform("uTexture0", 0);
//Use elapsed time to convert to radians to allow our cube to rotate over time
var difference = (DateTime.UtcNow - StartTime).TotalMilliseconds / 10;
var model = Matrix4x4.CreateRotationY((float)DegreesToRadians(difference)) * Matrix4x4.CreateRotationX((float)DegreesToRadians(difference));
var view = Matrix4x4.CreateLookAt(CameraPosition, CameraPosition + CameraFront, CameraUp);
var projection = Matrix4x4.CreatePerspectiveFieldOfView((float)DegreesToRadians(CameraZoom), Width / Height, 0.1f, 100.0f);
Shader.SetUniform("uModel", model);
Shader.SetUniform("uView", view);
Shader.SetUniform("uProjection", projection);
//We're drawing with just vertices and no indicies, and it takes 36 verticies to have a six-sided textured cube
Gl.DrawArrays(PrimitiveType.Triangles, 0, 36);
}
private static unsafe void OnMouseMove(IMouse mouse, PointF position)
{
var lookSensitivity = 0.02f;
if (LastMousePosition == default) { LastMousePosition = position; }
else
{
var xOffset = (position.X - LastMousePosition.X) * lookSensitivity;
var yOffset = (position.Y - LastMousePosition.Y) * lookSensitivity;
LastMousePosition = position;
CameraYaw += xOffset;
CameraPitch += yOffset;
//We don't want to be able to look behind us by going over our head or under our feet so make sure it stays within these bounds
if (CameraPitch > 89.0f) { CameraPitch = 89.0f; }
if (CameraPitch < -89.0f) { CameraPitch = -89.0f; }
CameraDirection.X = MathF.Cos((float)DegreesToRadians(CameraYaw)) * MathF.Cos((float)DegreesToRadians(CameraPitch));
CameraDirection.Y = MathF.Sin((float)DegreesToRadians(CameraPitch));
CameraDirection.Z = MathF.Sin((float)DegreesToRadians(CameraYaw)) * MathF.Cos((float)DegreesToRadians(CameraPitch));
CameraFront = Vector3.Normalize(CameraDirection);
}
}
private static unsafe void OnMouseWheel(IMouse mouse, ScrollWheel scrollWheel)
{
//We don't want to be able to zoom in too close or too far away so clamp to these values
CameraZoom = Math.Clamp(CameraZoom - scrollWheel.Y, 1.0f, 45f);
}
private static void OnClose()
{
Vbo.Dispose();
Ebo.Dispose();
Vao.Dispose();
Shader.Dispose();
Texture.Dispose();
}
private static void KeyDown(IKeyboard keyboard, Key key, int arg3)
{
if (key == Key.Escape)
{
window.Close();
}
}
private static double DegreesToRadians(double degrees)
{
return (Math.PI / 180f) * degrees;
}
}
}

Просмотреть файл

@ -0,0 +1,93 @@
using System;
using System.IO;
using System.Numerics;
using Silk.NET.OpenGL;
namespace Tutorial
{
public class Shader : IDisposable
{
private uint _handle;
private GL _gl;
public Shader(GL gl, string vertexPath, string fragmentPath)
{
_gl = gl;
uint vertex = LoadShader(ShaderType.VertexShader, vertexPath);
uint fragment = LoadShader(ShaderType.FragmentShader, fragmentPath);
_handle = _gl.CreateProgram();
_gl.AttachShader(_handle, vertex);
_gl.AttachShader(_handle, fragment);
_gl.LinkProgram(_handle);
string infoLog = _gl.GetProgramInfoLog(_handle);
if (!string.IsNullOrWhiteSpace(infoLog))
{
throw new Exception($"Program failed to link with error: {infoLog}");
}
_gl.DetachShader(_handle, vertex);
_gl.DetachShader(_handle, fragment);
_gl.DeleteShader(vertex);
_gl.DeleteShader(fragment);
}
public void Use()
{
_gl.UseProgram(_handle);
}
public void SetUniform(string name, int value)
{
int location = _gl.GetUniformLocation(_handle, name);
if (location == -1)
{
throw new Exception($"{name} uniform not found on shader.");
}
Use();
_gl.Uniform1(location, value);
}
public unsafe void SetUniform(string name, Matrix4x4 value)
{
//A new overload has been created for setting a uniform so we can use the transform in our shader.
int location = _gl.GetUniformLocation(_handle, name);
if (location == -1)
{
throw new Exception($"{name} uniform not found on shader.");
}
Use();
_gl.UniformMatrix4(location, 1, false, (float*)&value);
}
public void SetUniform(string name, float value)
{
int location = _gl.GetUniformLocation(_handle, name);
if (location == -1)
{
throw new Exception($"{name} uniform not found on shader.");
}
Use();
_gl.Uniform1(location, value);
}
public void Dispose()
{
_gl.DeleteProgram(_handle);
}
private uint LoadShader(ShaderType type, string path)
{
string src = File.ReadAllText(path);
uint handle = _gl.CreateShader(type);
_gl.ShaderSource(handle, src);
_gl.CompileShader(handle);
string infoLog = _gl.GetShaderInfoLog(handle);
if (!string.IsNullOrWhiteSpace(infoLog))
{
throw new Exception($"Error compiling shader of type {type}, failed with error {infoLog}");
}
return handle;
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
using Silk.NET.OpenGL;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Runtime.InteropServices;
namespace Tutorial
{
public class Texture : IDisposable
{
private uint _handle;
private GL _gl;
public unsafe Texture(GL gl, string path)
{
Image<Rgba32> img = (Image<Rgba32>)Image.Load(path);
img.Mutate(x => x.Flip(FlipMode.Vertical));
fixed (void* data = &MemoryMarshal.GetReference(img.GetPixelRowSpan(0)))
{
Load(gl, data, (uint)img.Width, (uint)img.Height);
}
img.Dispose();
}
public unsafe Texture(GL gl, Span<byte> data, uint width, uint height)
{
fixed (void* d = &data[0])
{
Load(gl, d, width, height);
}
}
private unsafe void Load(GL gl, void* data, uint width, uint height)
{
_gl = gl;
_handle = _gl.GenTexture();
Bind();
_gl.TexImage2D(TextureTarget.Texture2D, 0, (int)InternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)GLEnum.ClampToEdge);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)GLEnum.ClampToEdge);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)GLEnum.Linear);
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)GLEnum.Linear);
_gl.GenerateMipmap(TextureTarget.Texture2D);
}
public void Bind(TextureUnit textureSlot = TextureUnit.Texture0)
{
_gl.ActiveTexture(textureSlot);
_gl.BindTexture(TextureTarget.Texture2D, _handle);
}
public void Dispose()
{
_gl.DeleteTexture(_handle);
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
using System.Numerics;
namespace Tutorial
{
public class Transform
{
//A transform abstraction.
//For a transform we need to have a position a scale and a rotation,
//depending on what application you are creating the type for these may vary.
//Here we have chosen a vec3 for position, float for scale and quaternion for rotation,
//as that is the most normal to go with.
//Another example could have been vec3, vec3, vec4, so the rotation is an axis angle instead of a quaternion
public Vector3 Position { get; set; } = new Vector3(0, 0, 0);
public float Scale { get; set; } = 1f;
public Quaternion Rotation { get; set; } = Quaternion.Identity;
//Note: The order here does matter.
public Matrix4x4 ViewMatrix => Matrix4x4.Identity * Matrix4x4.CreateFromQuaternion(Rotation) * Matrix4x4.CreateScale(Scale) * Matrix4x4.CreateTranslation(Position);
}
}

Просмотреть файл

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<RootNamespace>Tutorial</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Silk.NET" Version="1.4.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-rc0003" />
</ItemGroup>
<ItemGroup>
<None Update="Shader.frag">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shader.vert">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="silk.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,39 @@
using Silk.NET.OpenGL;
using System;
namespace Tutorial
{
public class VertexArrayObject<TVertexType, TIndexType> : IDisposable
where TVertexType : unmanaged
where TIndexType : unmanaged
{
private uint _handle;
private GL _gl;
public VertexArrayObject(GL gl, BufferObject<TVertexType> vbo, BufferObject<TIndexType> ebo)
{
_gl = gl;
_handle = _gl.GenVertexArray();
Bind();
vbo.Bind();
ebo.Bind();
}
public unsafe void VertexAttributePointer(uint index, int count, VertexAttribPointerType type, uint vertexSize, int offSet)
{
_gl.VertexAttribPointer(index, count, type, false, vertexSize * (uint)sizeof(TVertexType), (void*)(offSet * sizeof(TVertexType)));
_gl.EnableVertexAttribArray(index);
}
public void Bind()
{
_gl.BindVertexArray(_handle);
}
public void Dispose()
{
_gl.DeleteVertexArray(_handle);
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
#version 330 core
in vec2 fUv;
uniform sampler2D uTexture0;
out vec4 FragColor;
void main()
{
FragColor = texture(uTexture0, fUv);
}

Просмотреть файл

@ -0,0 +1,16 @@
#version 330 core
layout (location = 0) in vec3 vPos;
layout (location = 1) in vec2 vUv;
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
out vec2 fUv;
void main()
{
//Multiplying our uniform with the vertex position, the multiplication order here does matter.
gl_Position = uProjection * uView * uModel * vec4(vPos, 1.0);
fUv = vUv;
}

Двоичные данные
examples/CSharp/Tutorial 2.2 - Camera/silk.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB