├── README.md
├── infogr-raytracer
├── assets
│ ├── font.png
│ └── container.png
├── Light.cs
├── packages.config
├── shaders
│ ├── shader.frag
│ ├── raytrace-shader.vert
│ ├── shader.vert
│ └── raytrace-shader.frag
├── Scene.cs
├── Program.cs
├── IGameObject.cs
├── Ray.cs
├── Properties
│ └── AssemblyInfo.cs
├── OpenTK.dll.config
├── Camera.cs
├── Circle.cs
├── Texture.cs
├── ShaderGame.cs
├── Shader.cs
├── Window.cs
├── Rectangle.cs
├── infogr-raytracer.csproj
├── Game.cs
└── Surface.cs
├── instructions
└── INFOGR_P1_2020_Raytracing.pdf
├── Tests
├── Tests.csproj
├── RectangleTest.cs
└── CircleTest.cs
├── infogr-raytracer.sln
└── .gitignore
/README.md:
--------------------------------------------------------------------------------
1 | # infogr-raytracer
2 | INFOGR ray tracing practicum
3 |
--------------------------------------------------------------------------------
/infogr-raytracer/assets/font.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-raytracer/master/infogr-raytracer/assets/font.png
--------------------------------------------------------------------------------
/infogr-raytracer/assets/container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-raytracer/master/infogr-raytracer/assets/container.png
--------------------------------------------------------------------------------
/instructions/INFOGR_P1_2020_Raytracing.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoR/infogr-raytracer/master/instructions/INFOGR_P1_2020_Raytracing.pdf
--------------------------------------------------------------------------------
/infogr-raytracer/Light.cs:
--------------------------------------------------------------------------------
1 | using OpenTK;
2 | using OpenTK.Graphics;
3 |
4 | namespace infogr_raytracer
5 | {
6 | public struct Light
7 | {
8 | public Vector2 Position;
9 | public Vector3 Color;
10 | }
11 | }
--------------------------------------------------------------------------------
/infogr-raytracer/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/infogr-raytracer/shaders/shader.frag:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | out vec4 outputColor;
4 |
5 | in vec2 texCoord;
6 |
7 | uniform sampler2D texture0;
8 |
9 | void main()
10 | {
11 | outputColor = texture(texture0, texCoord);
12 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Scene.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using OpenTK;
3 |
4 | namespace infogr_raytracer
5 | {
6 | public class Scene
7 | {
8 | public List Lights;
9 | public List GameObjects;
10 | }
11 | }
--------------------------------------------------------------------------------
/infogr-raytracer/shaders/raytrace-shader.vert:
--------------------------------------------------------------------------------
1 | #version 330 core
2 | layout (location = 0) in vec3 vertexPosition;
3 |
4 | out vec2 screenPosition;
5 |
6 | void main()
7 | {
8 | gl_Position = vec4(vertexPosition, 1.0);
9 |
10 | screenPosition = vertexPosition.xy;
11 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Program.cs:
--------------------------------------------------------------------------------
1 | namespace infogr_raytracer
2 | {
3 | internal class Program
4 | {
5 | public static void Main(string[] args)
6 | {
7 | using (Window window = new Window(800, 800, "INFOGR Raytracer"))
8 | {
9 | window.Run(60.0);
10 | }
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/infogr-raytracer/shaders/shader.vert:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | layout (location = 0) in vec3 aPosition;
4 | layout (location = 1) in vec2 aTexCoord;
5 |
6 | out vec2 texCoord;
7 |
8 | void main(void)
9 | {
10 | // pass the input texture coordinate to the fragment shader
11 | texCoord = aTexCoord;
12 |
13 | gl_Position = vec4(aPosition, 1.0);
14 | }
--------------------------------------------------------------------------------
/infogr-raytracer/IGameObject.cs:
--------------------------------------------------------------------------------
1 | using OpenTK;
2 |
3 | namespace infogr_raytracer
4 | {
5 | public interface IGameObject
6 | {
7 | Vector2 Position { get; }
8 | void Move(Vector2 position);
9 |
10 | void MoveBy(Vector2 translation);
11 |
12 | bool Intersects(Ray ray);
13 |
14 | // TODO:
15 | // Vector3 Trace(Ray ray);
16 | }
17 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Ray.cs:
--------------------------------------------------------------------------------
1 | using OpenTK;
2 |
3 | namespace infogr_raytracer
4 | {
5 | public struct Ray
6 | {
7 | private Vector2 _direction;
8 |
9 | ///
10 | /// The origin of our ray.
11 | ///
12 | public Vector2 Origin;
13 |
14 | ///
15 | /// The direction component of our ray. Is normalized when set.
16 | ///
17 | public Vector2 Direction
18 | {
19 | get => _direction;
20 | set => _direction = value.Normalized();
21 | }
22 |
23 | ///
24 | /// The magnitude of this ray
25 | ///
26 | public float Magnitude;
27 | }
28 | }
--------------------------------------------------------------------------------
/Tests/Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/infogr-raytracer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "infogr-raytracer", "infogr-raytracer\infogr-raytracer.csproj", "{077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{366D4B29-2926-462C-B78C-129501E5519E}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {366D4B29-2926-462C-B78C-129501E5519E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {366D4B29-2926-462C-B78C-129501E5519E}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {366D4B29-2926-462C-B78C-129501E5519E}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {366D4B29-2926-462C-B78C-129501E5519E}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/infogr-raytracer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("infogr_raytracer")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("infogr_raytracer")]
12 | [assembly: AssemblyCopyright("Copyright © 2020")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/infogr-raytracer/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/infogr-raytracer/Camera.cs:
--------------------------------------------------------------------------------
1 | using OpenTK;
2 |
3 | namespace infogr_raytracer
4 | {
5 | public struct Camera
6 | {
7 | ///
8 | /// The Camera's position (in world space)
9 | ///
10 | public Vector2 Position;
11 |
12 | private Vector2 _viewPort;
13 | private Vector2 _screenResolution;
14 | private float _screenSizeWorldSizeRatio;
15 |
16 | ///
17 | /// Convert a screen space coordinate to a world space coordinate
18 | /// Takes the current position into account
19 | ///
20 | /// The X component of the screen space coordinate
21 | /// The Y component of the screen space coordinate
22 | /// The screen space coordinate
23 | public Vector2 WorldSpace(int screenSpaceX, int screenSpaceY)
24 | {
25 | // float ratio = ((float) ViewPort.X / (float) ScreenResolution.X);
26 | return new Vector2(
27 | (float) screenSpaceX * _screenSizeWorldSizeRatio,
28 | (float) _viewPort.Y - screenSpaceY * _screenSizeWorldSizeRatio
29 | ) + Position;
30 | }
31 |
32 | ///
33 | /// Handle screen resizing by adjusting the screen resolution, viewport and world size ratio
34 | ///
35 | /// The new screen width
36 | /// The new screen height
37 | public void Resize(int screenWidth, int screenHeight)
38 | {
39 | float pixelsPerWorldSpaceUnit = 100;
40 | _screenResolution = new Vector2(screenWidth, screenHeight);
41 | _viewPort = new Vector2((float) screenWidth / pixelsPerWorldSpaceUnit,
42 | (float) screenHeight / pixelsPerWorldSpaceUnit);
43 |
44 | _screenSizeWorldSizeRatio = ((float) _viewPort.X / (float) _screenResolution.X);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Circle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTK;
3 |
4 | namespace infogr_raytracer
5 | {
6 | public struct Circle: IGameObject
7 | {
8 | public Vector2 Position { get; set; }
9 | public float Radius;
10 |
11 | ///
12 | /// Move a circle to a new position.
13 | ///
14 | /// The circle's new position.
15 | public void Move(Vector2 position)
16 | {
17 | Position = position;
18 | }
19 |
20 | ///
21 | /// Translate a circle.
22 | ///
23 | /// The translation by which the circle's position should be altered
24 | public void MoveBy(Vector2 translation)
25 | {
26 | Move(translation + Position);
27 | }
28 |
29 | ///
30 | /// Return if this circle intersects a ray.
31 | /// Uses the ABC formula.
32 | ///
33 | /// The ray for which the intersection should be checked.
34 | /// If the circle intersects the ray.
35 | public bool Intersects(Ray ray)
36 | {
37 | Vector2 positionToRayOrigin = ray.Origin - Position;
38 |
39 | // Apply ABC formula
40 | float a = Vector2.Dot(ray.Direction, ray.Direction);
41 | float b = Vector2.Dot(ray.Direction, positionToRayOrigin);
42 | float c = Vector2.Dot(positionToRayOrigin, positionToRayOrigin) - Radius * Radius;
43 | float d = b * b - a * c;
44 |
45 | // Early exit if there are no intersections
46 | if (d < 0) return false;
47 |
48 | // Calculate intersections based on d
49 | float sqrtD = (float) Math.Sqrt(d);
50 | float distance = (-b - sqrtD) / a;
51 | if (distance < 0) distance = (-b + sqrtD) / a;
52 |
53 | // Check if our result is within the ray's length
54 | return (distance > 0 && distance < ray.Magnitude);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Texture.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Drawing.Imaging;
4 | using OpenTK.Graphics.OpenGL;
5 | using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;
6 |
7 | namespace infogr_raytracer
8 | {
9 | public class Texture: IDisposable
10 | {
11 | public readonly int Handle;
12 |
13 | public Texture(string path)
14 | {
15 | Handle = GL.GenTexture();
16 |
17 | Use();
18 |
19 | using (var image = new Bitmap(path))
20 | {
21 | // Get pixels from the loaded bitmap
22 | var data = image.LockBits(
23 | new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
24 | ImageLockMode.ReadOnly,
25 | System.Drawing.Imaging.PixelFormat.Format32bppArgb);
26 |
27 | // Generate a texture based on the Bitmap data
28 | GL.TexImage2D(TextureTarget.Texture2D,
29 | 0,
30 | PixelInternalFormat.Rgba,
31 | image.Width,
32 | image.Height,
33 | 0,
34 | PixelFormat.Bgra,
35 | PixelType.UnsignedByte,
36 | data.Scan0);
37 |
38 | // Set the min and mag filter
39 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
40 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
41 |
42 | // Set the wrapping mode
43 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
44 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
45 |
46 | // Generate Mipmap
47 | GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
48 | }
49 |
50 | }
51 |
52 | ///
53 | /// Activate texture
54 | ///
55 | ///
56 | public void Use(TextureUnit unit = TextureUnit.Texture0)
57 | {
58 | GL.ActiveTexture(unit);
59 | GL.BindTexture(TextureTarget.Texture2D, Handle);
60 | }
61 |
62 |
63 | // Make our texture disposable
64 | private bool _disposedValue = false;
65 |
66 | protected virtual void Dispose(bool disposing)
67 | {
68 | if (!_disposedValue)
69 | {
70 | GL.DeleteTexture(Handle);
71 | _disposedValue = true;
72 | }
73 | }
74 |
75 | ~Texture()
76 | {
77 | GL.DeleteProgram(Handle);
78 | }
79 |
80 |
81 | public void Dispose()
82 | {
83 | Dispose(true);
84 | GC.SuppressFinalize(this);
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/infogr-raytracer/ShaderGame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using OpenTK.Graphics.OpenGL;
4 |
5 | namespace infogr_raytracer
6 | {
7 | public class ShaderGame
8 | {
9 | private readonly float[] _vertices =
10 | {
11 | 1.0f, 1.0f, 0.0f,
12 | 1.0f, -1.0f, 0.0f,
13 | -1.0f, -1.0f, 0.0f,
14 | -1.0f, 1.0f, 0.0f
15 | };
16 |
17 | private readonly uint[] _indices =
18 | {
19 | 0, 1, 3,
20 | 1, 2, 3
21 | };
22 |
23 | private int _vertexBufferObject;
24 | private int _vertexArrayObject;
25 | private int _elementBufferObject;
26 |
27 | private Shader _shader;
28 |
29 | ///
30 | /// Called when the Game is loaded.
31 | /// Should be used for one-time setup.
32 | ///
33 | public void OnLoad()
34 | {
35 | VaoSetup();
36 |
37 | }
38 |
39 | private void VaoSetup()
40 | {
41 | _vertexBufferObject = GL.GenBuffer();
42 | GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
43 | GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
44 |
45 | _elementBufferObject = GL.GenBuffer();
46 | GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
47 | GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
48 |
49 | _vertexArrayObject = GL.GenVertexArray();
50 | GL.BindVertexArray(_vertexArrayObject);
51 | GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
52 | GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
53 |
54 |
55 | _shader = new Shader("shaders/raytrace-shader.vert", "shaders/raytrace-shader.frag");
56 | _shader.Use();
57 |
58 | int vertexLocation = _shader.GetAttribLocation("vertexPosition");
59 | GL.EnableVertexAttribArray(vertexLocation);
60 | GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
61 | }
62 |
63 | ///
64 | /// Called when the frame is rendered.
65 | ///
66 | public void OnRenderFrame()
67 | {
68 | GL.BindVertexArray(_vertexArrayObject);
69 |
70 | var stopwatch = new Stopwatch();
71 | stopwatch.Start();
72 |
73 | _shader.SetFloat("time", (float) Math.Sin(DateTime.Now.Ticks / 1_000_000.0) / 2.0f);
74 |
75 | GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
76 |
77 | stopwatch.Stop();
78 |
79 | var fps = stopwatch.ElapsedMilliseconds == 0 ?
80 | "Infinite" :
81 | $"{1_000 / stopwatch.ElapsedMilliseconds}";
82 | // Console.WriteLine($"FPS: {fps}");
83 | }
84 |
85 | public void OnUnload()
86 | {
87 | _shader.Dispose();
88 | }
89 |
90 | public void OnResize(int width, int height)
91 | {
92 | _shader.SetInt("SCREEN_WIDTH", width);
93 | _shader.SetInt("SCREEN_HEIGHT", height);
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/Tests/RectangleTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NUnit.Framework;
4 | using infogr_raytracer;
5 | using OpenTK;
6 |
7 | namespace Tests
8 | {
9 | public class RectangleTest
10 | {
11 | public struct RectanglePositionTestCase
12 | {
13 | public Vector2 Position;
14 | public float Width;
15 | public float Height;
16 | public float Angle;
17 | public string Description;
18 |
19 | public override string ToString()
20 | {
21 | return Description;
22 | }
23 | }
24 |
25 | private static List rectanglePositionTestCases =
26 | new List()
27 | {
28 | new RectanglePositionTestCase()
29 | {
30 | Position = new Vector2(0, 0),
31 | Width = 4,
32 | Height = 4,
33 | Angle = 0,
34 | Description = "Square rectangle at origin of size 4, 4"
35 | },
36 | new RectanglePositionTestCase()
37 | {
38 | Position = new Vector2(0, 0),
39 | Width = 2,
40 | Height = 4,
41 | Angle = 0,
42 | Description = "Rectangle at origin of size 2, 4"
43 | },
44 | new RectanglePositionTestCase()
45 | {
46 | Position = new Vector2(2, 3),
47 | Width = 2,
48 | Height = 4,
49 | Angle = 0,
50 | Description = "Rectangle at (2, 3) of size 2, 4"
51 | },
52 | new RectanglePositionTestCase()
53 | {
54 | Position = new Vector2(2.042f, 3.036f),
55 | Width = 3.2f,
56 | Height = 4.2f,
57 | Angle = 0,
58 | Description = "Rectangle at (2.042, 3.036) of size 3.2, 4.2"
59 | },
60 | new RectanglePositionTestCase()
61 | {
62 | Position = new Vector2(5.43421f, -4.321f),
63 | Width = 3.21f,
64 | Height = 4.21f,
65 | Angle = 0,
66 | Description = "Rectangle at (5.43421, -4.321) of size 3.21, 4.321"
67 | },
68 | new RectanglePositionTestCase()
69 | {
70 | Position = new Vector2(5.43421f, -4.321f),
71 | Width = -5.21f,
72 | Height = 4.21f,
73 | Angle = 0,
74 | Description = "Rectangle with negative Width"
75 | },
76 | new RectanglePositionTestCase()
77 | {
78 | Position = new Vector2(5.43421f, -4.321f),
79 | Width = 5.21f,
80 | Height = -7.21f,
81 | Angle = 0,
82 | Description = "Rectangle with negative Height"
83 | }
84 | };
85 |
86 | [Test]
87 | [TestCaseSource("rectanglePositionTestCases")]
88 | public void CalculatesPosition(RectanglePositionTestCase testCase)
89 | {
90 | Rectangle rectangle = new Rectangle(testCase.Position, testCase.Width, testCase.Height);
91 | Assert.That(testCase.Position.X, Is.EqualTo(rectangle.Position.X).Within(0.001f));
92 | Assert.That(testCase.Position.Y, Is.EqualTo(rectangle.Position.Y).Within(0.001f));
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Shader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using OpenTK.Graphics.OpenGL;
5 |
6 | namespace infogr_raytracer
7 | {
8 | public class Shader: IDisposable
9 | {
10 | public int Handle;
11 |
12 | public Shader(string vertexPath, string fragmentPath)
13 | {
14 | // Get the Vertex Shader Source from the file located at vertexPath
15 | // and the Fragment Shader Source from the file located at fragmentPath
16 | string vertexShaderSource;
17 | using (StreamReader reader = new StreamReader(vertexPath, Encoding.UTF8))
18 | vertexShaderSource = reader.ReadToEnd();
19 |
20 | string fragmentShaderSource;
21 | using (StreamReader reader = new StreamReader(fragmentPath, Encoding.UTF8))
22 | fragmentShaderSource = reader.ReadToEnd();
23 |
24 |
25 | // Create OpenGL shaders and bind the source code to the shaders
26 | int vertexShader = GL.CreateShader(ShaderType.VertexShader);
27 | GL.ShaderSource(vertexShader, vertexShaderSource);
28 |
29 | int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
30 | GL.ShaderSource(fragmentShader, fragmentShaderSource);
31 |
32 |
33 | // Compile the shaders and check for errors
34 | GL.CompileShader(vertexShader);
35 | string infoLogVert = GL.GetShaderInfoLog(vertexShader);
36 | if (infoLogVert != String.Empty)
37 | Console.WriteLine(infoLogVert);
38 |
39 | GL.CompileShader(fragmentShader);
40 | string infoLogFrag = GL.GetShaderInfoLog(fragmentShader);
41 | if (infoLogFrag != String.Empty)
42 | Console.WriteLine(infoLogFrag);
43 |
44 |
45 | // Link our compiled shaders together into a program that can run on the GPU.
46 | Handle = GL.CreateProgram();
47 | GL.AttachShader(Handle, vertexShader);
48 | GL.AttachShader(Handle, fragmentShader);
49 |
50 | GL.LinkProgram(Handle); // Handle is now a usable shader program
51 |
52 |
53 | // Cleanup. Vertex and fragment shaders are useless now that they've been linked. We detach and delete them.
54 | GL.DetachShader(Handle, vertexShader);
55 | GL.DetachShader(Handle, fragmentShader);
56 | GL.DeleteShader(fragmentShader);
57 | GL.DeleteShader(vertexShader);
58 | }
59 |
60 | public void Use()
61 | {
62 | GL.UseProgram(Handle);
63 | }
64 |
65 | public void SetUniform4(string name, float v0, float v1, float v2, float v3)
66 | {
67 | Use();
68 | GL.Uniform4(GL.GetUniformLocation(Handle, name), v0, v1, v2, v3);
69 | }
70 |
71 | public void SetFloat(string name, float value)
72 | {
73 | Use();
74 | GL.Uniform1(GL.GetUniformLocation(Handle, name), value);
75 | }
76 |
77 | public void SetInt(string name, int value)
78 | {
79 | Use();
80 | GL.Uniform1(GL.GetUniformLocation(Handle, name), value);
81 | }
82 |
83 | public int GetAttribLocation(string attribName)
84 | {
85 | return GL.GetAttribLocation(Handle, attribName);
86 | }
87 |
88 |
89 | // Make our shader disposable
90 | private bool _disposedValue = false;
91 |
92 | protected virtual void Dispose(bool disposing)
93 | {
94 | if (!_disposedValue)
95 | {
96 | GL.DeleteProgram(Handle);
97 |
98 | _disposedValue = true;
99 | }
100 | }
101 |
102 | ~Shader()
103 | {
104 | GL.DeleteProgram(Handle);
105 | }
106 |
107 |
108 | public void Dispose()
109 | {
110 | Dispose(true);
111 | GC.SuppressFinalize(this);
112 | }
113 |
114 | }
115 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Window.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTK;
3 | using OpenTK.Graphics;
4 | using OpenTK.Graphics.OpenGL;
5 |
6 | namespace infogr_raytracer
7 | {
8 | public class Window: GameWindow
9 | {
10 | // TODO: Figure out a proper way to set feature flag
11 | const bool SHADER_VERSION = true;
12 |
13 | private Game _game;
14 | private ShaderGame _shaderGame;
15 |
16 | ///
17 | /// Constructs a new Window with the specified attributes.
18 | ///
19 | /// The width of the Window's window
20 | /// The height of the Window's window
21 | /// The title of the Window's window
22 | public Window(int width, int height, string title) : base(width, height, GraphicsMode.Default, title,
23 | GameWindowFlags.Default, DisplayDevice.Default,
24 | 3, 3, GraphicsContextFlags.ForwardCompatible)
25 | {
26 | }
27 |
28 | ///
29 | /// Print OpenGL and Open GL Shading Language versions
30 | ///
31 | private static void PrintVersionInfo()
32 | {
33 | Console.WriteLine($"OpenGL Version: {GL.GetString(StringName.Version)}");
34 | Console.WriteLine($"OpenGL Shading Language Version: {GL.GetString(StringName.ShadingLanguageVersion)}");
35 | Console.WriteLine();
36 | }
37 |
38 | ///
39 | /// Called after an OpenGL context has been established, but before entering the main loop.
40 | ///
41 | /// Not used.
42 | protected override void OnLoad(EventArgs e)
43 | {
44 | PrintVersionInfo();
45 |
46 | if (SHADER_VERSION)
47 | {
48 | _shaderGame = new ShaderGame();
49 | _shaderGame.OnLoad();
50 | }
51 | else
52 | {
53 | _game = new Game { Screen = new Surface(Width, Height) };
54 | _game.OnLoad();
55 | }
56 |
57 | base.OnLoad(e);
58 | }
59 |
60 | ///
61 | /// Called when the frame is rendered.
62 | ///
63 | protected override void OnRenderFrame(FrameEventArgs e)
64 | {
65 | // Clear the screen
66 | GL.Clear(ClearBufferMask.ColorBufferBit);
67 |
68 | if (SHADER_VERSION)
69 | {
70 | _shaderGame.OnRenderFrame();
71 | }
72 | else
73 | {
74 | // Let our Game draw stuff to it's Screen
75 | _game.OnRenderFrame();
76 | _game.Screen.Draw();
77 | }
78 |
79 | // Show the new frame
80 | Context.SwapBuffers();
81 | base.OnRenderFrame(e);
82 | }
83 |
84 | ///
85 | /// Called when this window is resized.
86 | /// Updates viewport to match window size.
87 | ///
88 | /// Not used.
89 | protected override void OnResize(EventArgs e)
90 | {
91 | GL.Viewport(0, 0, Width, Height);
92 |
93 | // TODO: Implement for SHADER_VERSION
94 | if (SHADER_VERSION)
95 | {
96 | _shaderGame.OnResize(Width, Height);
97 | }
98 | if (!SHADER_VERSION)
99 | {
100 | _game.Screen.Unload();
101 | _game.Screen = new Surface(Width, Height);
102 | _game.OnResize();
103 | }
104 |
105 | base.OnResize(e);
106 | }
107 |
108 | ///
109 | /// Called after GameWindow.Exit was called, but before destroying the OpenGL context.
110 | /// Unloads our screen.
111 | ///
112 | /// Not used.
113 | protected override void OnUnload(EventArgs e)
114 | {
115 | // TODO: Implement for SHADER_VERSION
116 | if (SHADER_VERSION)
117 | {
118 | _shaderGame.OnUnload();
119 | }
120 | else
121 | {
122 | _game.Screen.Unload();
123 | }
124 | base.OnUnload(e);
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Rectangle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTK;
3 |
4 | namespace infogr_raytracer
5 | {
6 | public struct Rectangle: IGameObject
7 | {
8 | // Clockwise order, starting with top-left corner, with respect to the angle
9 | private Vector2[] _corners;
10 |
11 | public Rectangle(Vector2 position, float width, float height, float angle = 0)
12 | {
13 | // Determine x, y coordinates as if the rectangle were at the origin
14 | _corners = new[]
15 | {
16 | new Vector2(- width * 0.5f, + height * 0.5f),
17 | new Vector2(+ width * 0.5f, + height * 0.5f),
18 | new Vector2(+ width * 0.5f, - height * 0.5f),
19 | new Vector2(- width * 0.5f, - height * 0.5f),
20 | };
21 |
22 | // Rotate the points based on angle
23 | if (angle != 0)
24 | {
25 | for (int i = 0; i < _corners.Length; i++)
26 | _corners[i] = new Vector2(
27 | _corners[i].X * (float) Math.Cos(angle) - _corners[i].Y * (float) Math.Sin(angle),
28 | _corners[i].X * (float) Math.Sin(angle) + _corners[i].Y * (float) Math.Cos(angle));
29 | }
30 |
31 | // Translate the points to their position
32 | for (int i = 0; i < _corners.Length; i++)
33 | _corners[i] += position;
34 |
35 | }
36 |
37 | public Vector2 Position
38 | {
39 | get => (_corners[0] + _corners[2]) / 2;
40 | }
41 |
42 | public void Move(Vector2 position)
43 | {
44 | Vector2 translation = position - Position;
45 | for (int i = 0; i < _corners.Length; i++)
46 | _corners[i] += translation;
47 | }
48 |
49 | ///
50 | /// Translate a rectangle.
51 | ///
52 | /// The translation by which the rectangle's position should be altered
53 | public void MoveBy(Vector2 translation)
54 | {
55 | Move(translation + Position);
56 | }
57 |
58 | ///
59 | /// Return if this rectangle intersects a ray.
60 | ///
61 | /// The ray for which the intersection should be checked
62 | /// If the rectangle intersects the ray.
63 | public bool Intersects(Ray ray)
64 | {
65 | return _lineIntersects(ray, _corners[0], _corners[1]) ||
66 | _lineIntersects(ray, _corners[1], _corners[2]) ||
67 | _lineIntersects(ray, _corners[2], _corners[3]) ||
68 | _lineIntersects(ray, _corners[3], _corners[0]);
69 | }
70 |
71 |
72 | ///
73 | /// Determine if our ray crosses the line from line start to line end.
74 | ///
75 | /// The ray that needs to be checked.
76 | /// The start of the line.
77 | /// The end of the line.
78 | ///
79 | private bool _lineIntersects(Ray ray, Vector2 lineStart, Vector2 lineEnd)
80 | {
81 | // Uses this https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
82 | // algorithm of finding out if two vectors intersect.
83 |
84 | Vector2 rayStart = ray.Origin;
85 | Vector2 rayEnd = ray.Origin + ray.Direction;
86 |
87 | Vector2 rayStoLineS = lineStart - rayStart;
88 | Vector2 r = ray.Direction * ray.Magnitude;
89 | Vector2 s = lineEnd - lineStart;
90 |
91 | float crossR = rayStoLineS.X * r.Y - rayStoLineS.Y * r.X;
92 | float crossS = rayStoLineS.X * s.Y - rayStoLineS.Y * s.X;
93 | float rCrossS = r.X * s.Y - r.Y * s.X;
94 |
95 | if (crossR == 0f) // Lines are collinear
96 | return ((lineStart.X - rayStart.X < 0f) != (lineStart.X - rayEnd.X < 0f)) ||
97 | ((lineStart.Y - rayStart.Y < 0f) != (lineStart.Y - rayEnd.Y < 0f));
98 |
99 | if (rCrossS == 0f) // Lines are parallel
100 | return false;
101 |
102 | float t = crossS / rCrossS;
103 | float u = crossR / rCrossS;
104 |
105 | return (t >= 0f) && (t <= 1f) && (u >= 0f) && (u <= 1f);
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/infogr-raytracer/infogr-raytracer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {077A96BD-0CB5-4244-9CE2-A4A9A3E4AA19}
9 | Exe
10 | Properties
11 | infogr_raytracer
12 | infogr_raytracer
13 | v4.7.2
14 | 512
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll
38 | True
39 |
40 |
41 | ..\packages\OpenTK.3.2\lib\net20\OpenTK.dll
42 | True
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | Always
75 |
76 |
77 | Always
78 |
79 |
80 |
81 | Always
82 |
83 |
84 | Always
85 |
86 |
87 |
88 |
89 |
90 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.
91 |
92 |
93 |
94 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/infogr-raytracer/shaders/raytrace-shader.frag:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec2 screenPosition;
4 | out vec4 FragColor;
5 |
6 | uniform int SCREEN_WIDTH;
7 | uniform int SCREEN_HEIGHT;
8 |
9 | #define M_PI 3.1415926535897932384626433832795
10 |
11 | uniform float time;
12 |
13 |
14 |
15 | // DATA MODEL
16 | struct Ray
17 | {
18 | vec2 origin;
19 | vec2 direction;
20 | float magnitude;
21 | };
22 |
23 | struct Light
24 | {
25 | vec2 position;
26 | vec3 color;
27 | };
28 |
29 | struct Circle
30 | {
31 | vec2 position;
32 | float radius;
33 | };
34 |
35 | struct Camera
36 | {
37 | vec2 position;
38 | vec2 viewport;
39 | int screenSizeWorldSizeRatio;
40 | } camera;
41 |
42 | struct Rectangle {
43 | vec2[4] corners;
44 | };
45 |
46 |
47 | Rectangle newRectangle(in vec2 position, float width, float height, float angle) {
48 | vec2[4] corners = vec2[4](
49 | vec2(-width * 0.5, height * 0.5),
50 | vec2(width * 0.5, height * 0.5),
51 | vec2(width * 0.5, -height * 0.5),
52 | vec2(-width * 0.5, -height * 0.5)
53 | );
54 |
55 | if (angle != 0) {
56 | for (int i = 0; i < corners.length(); i++) {
57 | vec2 corner = corners[i];
58 | corners[i] = vec2(
59 | corner.x * cos(angle) - corner.y * sin(angle),
60 | corner.xy * sin(angle) + corner.y * cos(angle)
61 | );
62 | }
63 | }
64 |
65 | for (int i = 0; i < corners.length(); i++) {
66 | corners[i] += position;
67 | }
68 |
69 | return Rectangle(corners);
70 | }
71 |
72 |
73 |
74 |
75 | // SCENE DEFINITION
76 | Light[] lights = Light[] (
77 | Light(vec2(0, 0), 4 * vec3(0, 0.75, 0.75)),
78 | Light(vec2(4, 0), 4 * vec3(0, 1, 0)),
79 | Light(vec2(8, 0), 4 * vec3(0.75, 1, 0)),
80 | Light(vec2(0, 4), 4 * vec3(0, 0, 1)),
81 | Light(vec2(4, 4), 4 * vec3(1, 1, 1)),
82 | Light(vec2(8, 4), 4 * vec3(1, 1, 0)),
83 | Light(vec2(0, 8), 4 * vec3(1, 0, 1)),
84 | Light(vec2(4, 8), 4 * vec3(1, 0, 0)),
85 | Light(vec2(8, 8), 4 * vec3(1, 0.75, 0))
86 | );
87 |
88 | Circle[] circles = Circle[] (
89 | Circle(vec2(2, 2), 0.1),
90 | Circle(vec2(3, 2), 0.2),
91 | Circle(vec2(4, 2), 0.3),
92 | Circle(vec2(5, 2), 0.2),
93 | Circle(vec2(6, 2), 0.1)
94 | );
95 |
96 | Rectangle[] rectangles = Rectangle[](
97 | newRectangle(vec2(3,3), 1, 1, 0.4)
98 | );
99 |
100 |
101 |
102 |
103 | // INTERSECTION CHECKS
104 | bool circleIntersects(Circle circle, Ray ray)
105 | {
106 | vec2 posToOrigin = ray.origin - circle.position;
107 | float a = dot(ray.direction, ray.direction);
108 | float b = dot(ray.direction, posToOrigin);
109 | float c = dot(posToOrigin, posToOrigin) - (circle.radius * circle.radius);
110 | float d = (b * b) - (a * c);
111 |
112 | if (d < 0) return false;
113 |
114 | float sqrtD = sqrt(d);
115 | float distance = (-b - sqrtD) / a;
116 | if (distance < 0) distance = (-b + sqrtD) / a;
117 |
118 | return distance > 0 && distance < ray.magnitude;
119 | }
120 |
121 | bool lineIntersects(Ray ray, vec2 lineStart, vec2 lineEnd) {
122 | vec2 rayStart = ray.origin;
123 | vec2 rayEnd = ray.origin + ray.direction;
124 |
125 | vec2 rayStoLineS = lineStart - rayStart;
126 | vec2 r = ray.direction * ray.magnitude;
127 | vec2 s = lineEnd - lineStart;
128 |
129 | float crossR = (rayStoLineS.x * r.y) - (rayStoLineS.y * r.x);
130 | float crossS = (rayStoLineS.x * s.y) - (rayStoLineS.y * s.x);
131 | float rCrossS = r.x * s.y - r.y * s.x;
132 |
133 | if (crossR == 0) {
134 | return ((lineStart.x - rayStart.x < 0) != (lineStart.x - rayEnd.x < 0)) ||
135 | ((lineStart.y - rayStart.y < 0) != (lineStart.y - rayEnd.y < 0));
136 | }
137 |
138 | if (rCrossS == 0) return false;
139 |
140 | float t = crossS / rCrossS;
141 | float u = crossR / rCrossS;
142 |
143 | return (t >= 0) && (t <= 1) && (u >= 0) && (u <= 1);
144 | }
145 |
146 | bool rectangleIntersect(Ray ray, Rectangle rect) {
147 | vec2[4] corners = rect.corners;
148 |
149 | return lineIntersects(ray, corners[0], corners[1]) ||
150 | lineIntersects(ray, corners[1], corners[2]) ||
151 | lineIntersects(ray, corners[2], corners[3]) ||
152 | lineIntersects(ray, corners[3], corners[0]);
153 | }
154 |
155 |
156 |
157 |
158 | // Convert point from screen space to world space
159 | vec2 ToWorldSpace(vec2 screenSpacePoint)
160 | {
161 | float ratio = 100; // screen pixels per world space unit
162 |
163 | return screenSpacePoint * vec2(SCREEN_WIDTH, SCREEN_HEIGHT) / ratio;
164 | }
165 |
166 | vec3 Trace(vec2 worldPoint)
167 | {
168 | vec3 colorAtPixel = vec3(0, 0, 0);
169 |
170 | for (int i = 0; i < lights.length(); i++)
171 | {
172 | vec2 vector2Light = lights[i].position - worldPoint;
173 |
174 | // Don't forget to normalize the ray's direction
175 | Ray ray = Ray(worldPoint, vector2Light, length(vector2Light));
176 | ray.direction = normalize(ray.direction);
177 |
178 | // Check for occlusions between ray
179 | bool occluded = false;
180 | for (int c = 0; c < circles.length(); c++) {
181 | Circle circle = circles[c];
182 | if (circleIntersects(circle, ray)) {
183 | occluded = true;
184 | break;
185 | }
186 | }
187 | if (occluded) continue;
188 |
189 | for (int r = 0; r < rectangles.length(); r++) {
190 | Rectangle rect = rectangles[r];
191 | if (rectangleIntersect(ray, rect)) {
192 | occluded = true;
193 | break;
194 | }
195 | }
196 | if (occluded) continue;
197 |
198 |
199 | float distanceToLight = length(vector2Light);
200 | float intensity = 1.0 / (4 * M_PI * distanceToLight);
201 |
202 | colorAtPixel += lights[i].color * intensity;
203 | }
204 |
205 | return colorAtPixel;
206 | }
207 |
208 |
209 |
210 |
211 | void main()
212 | {
213 |
214 | for (int i = 0; i < circles.length(); i++)
215 | {
216 | circles[i].position += vec2(time, 0);
217 | }
218 |
219 | camera.position = vec2 (4, 4 + time);
220 | vec3 colorAtPixel = Trace(ToWorldSpace(screenPosition) + camera.position);
221 | FragColor = vec4(colorAtPixel, 1);
222 | }
--------------------------------------------------------------------------------
/infogr-raytracer/Game.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using OpenTK;
4 | using OpenTK.Input;
5 |
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace infogr_raytracer
10 | {
11 | public class Game
12 | {
13 | public Surface Screen;
14 |
15 | // private Scene _scene = new Scene()
16 | // {
17 | // Lights = new List()
18 | // {
19 | // new Light() { Color = new Vector3(1, 1, 1), Position = new Vector2(2.0f, 2.0f) },
20 | // new Light() { Color = new Vector3(3, 2, 1), Position = new Vector2(3.0f, 4.0f) },
21 | // new Light() { Color = new Vector3(3, 4, 5), Position = new Vector2(3.0f, 8.0f) },
22 | // new Light() { Color = new Vector3(1, 0, 0), Position = new Vector2(7.0f, 8.0f) },
23 | // new Light() { Color = new Vector3(0, 0, 1), Position = new Vector2(7.5f, 8.0f) }
24 | // },
25 | // GameObjects = new List()
26 | // {
27 | // new Circle() { Position = new Vector2(4.0f,4.5f), Radius = 0.1f },
28 | // new Circle() { Position = new Vector2(6.5f,6.5f), Radius = 1.0f },
29 | // new Circle() { Position = new Vector2(4.0f,4.0f), Radius = 0.3f },
30 | // new Circle() { Position = new Vector2(3.0f,3.0f), Radius = 0.1f }
31 | // }
32 | // };
33 |
34 | private Scene _scene = new Scene()
35 | {
36 | Lights = new List()
37 | {
38 | new Light() { Position = new Vector2(4.0f, 2.0f), Color = 3 * new Vector3(6, 2, 1) },
39 | new Light() { Position = new Vector2(6.0f, 4.0f), Color = 0.5f * new Vector3(1, 2, 3) },
40 | new Light() { Position = new Vector2(4.5f, 5.0f), Color = 2f * new Vector3(1, 2, 3) },
41 | },
42 | GameObjects = new List()
43 | {
44 | new Circle() { Position = new Vector2(4.0f, 0.9999f), Radius = 1.0f },
45 | new Circle() { Position = new Vector2(4.0f, 3.65f), Radius = 0.1f },
46 | new Circle() { Position = new Vector2(3.5f, 3.5f), Radius = 0.2f },
47 | new Rectangle(new Vector2(4.5f, 3.9f), 0.4f, 0.8f, angle: 0.7f)
48 | }
49 | };
50 |
51 |
52 | private Camera _camera = new Camera()
53 | {
54 | Position = new Vector2(0, 0)
55 | };
56 |
57 | ///
58 | /// Called when the Game is loaded.
59 | /// Should be used for one-time setup.
60 | ///
61 | public void OnLoad()
62 | {
63 | }
64 |
65 |
66 | ///
67 | /// Called when the frame is rendered.
68 | ///
69 | public void OnRenderFrame()
70 | {
71 | MoveCamera();
72 | RenderScene();
73 |
74 | _scene.GameObjects[3].MoveBy(new Vector2(-0.1f, 0));
75 | }
76 |
77 | ///
78 | /// Render our scene.
79 | ///
80 | private void RenderScene()
81 | {
82 | Screen.Clear(255);
83 |
84 | var timer = new System.Diagnostics.Stopwatch();
85 |
86 | timer.Start();
87 | Parallel.For(0, Screen.Width * Screen.Height, (i) =>
88 | {
89 | var x = i % Screen.Width;
90 | var y = i / Screen.Width;
91 | Vector3 colorForPixel = Trace(_camera.WorldSpace(x, y));
92 | Screen.Plot(x, y, ToScreenColor(colorForPixel));
93 | });
94 | timer.Stop();
95 |
96 | var fps = 1000f / timer.ElapsedMilliseconds;
97 | Screen.Print("Look on my Works, ye Mighty, and despair!", 10, 10, 0xFFFFFF);
98 | Screen.Print($"FPS: {fps}", 10, 30, 0xFFFFFF);
99 | }
100 |
101 | ///
102 | /// Moves the Camera using the arrow keys.
103 | ///
104 | private void MoveCamera()
105 | {
106 | KeyboardState keyboardState = Keyboard.GetState();
107 |
108 | float speed = 0.2f;
109 |
110 | if (keyboardState[Key.Right]) _camera.Position.X += speed;
111 | if (keyboardState[Key.Left]) _camera.Position.X -= speed;
112 | if (keyboardState[Key.Up]) _camera.Position.Y += speed;
113 | if (keyboardState[Key.Down]) _camera.Position.Y -= speed;
114 | }
115 |
116 |
117 | ///
118 | /// On Window resize call the camera's resize method.
119 | ///
120 | public void OnResize()
121 | {
122 | _camera.Resize(Screen.Width, Screen.Height);
123 | }
124 |
125 | ///
126 | /// Convert a world color (float, [0-1], and [1, ->) for HDR), to a screen color (int, [0-255]).
127 | ///
128 | /// The world color of type float, [0-1], and [1, ->).
129 | /// A screen color of type int, [0-255].
130 | private int ToScreenColor(Vector3 worldColor)
131 | {
132 | return ((int) (Math.Min(worldColor.X * 255, 255)) << 16) +
133 | ((int) (Math.Min(worldColor.Y * 255, 255)) << 8) +
134 | (int) (Math.Min(worldColor.Z * 255, 255));
135 | }
136 |
137 | ///
138 | /// Calculate which color a world space point should have.
139 | ///
140 | /// A world space point for which the color should be determined.
141 | /// The determined color
142 | private Vector3 Trace(Vector2 point)
143 | {
144 | Vector3 colorAtPoint = new Vector3(0, 0, 0);
145 |
146 | foreach (Light light in _scene.Lights)
147 | {
148 | // Construct ray from point to the light source
149 | Vector2 vectorToLight = light.Position - point;
150 | Ray rayToLight = new Ray() { Origin = point, Direction = vectorToLight, Magnitude = vectorToLight.Length};
151 |
152 | // Check if the light is occluded (intersected) by a Game Object, continue if it is occluded
153 | if (_scene.GameObjects.Exists(gameObject => gameObject.Intersects(rayToLight))) continue;
154 |
155 | // If the light from the light source is not occluded by any game object,
156 | // Calculate the light intensity form that non-occluded light source to our point
157 | float distanceToLight = vectorToLight.Length;
158 | float intensity = 1f / (4f * (float) Math.PI * distanceToLight * distanceToLight);
159 |
160 | // Add the color from the light to our final color
161 | colorAtPoint += light.Color * intensity;
162 | }
163 |
164 | return colorAtPoint;
165 | }
166 | }
167 | }
--------------------------------------------------------------------------------
/Tests/CircleTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NUnit.Framework;
4 | using infogr_raytracer;
5 | using OpenTK;
6 |
7 | namespace Tests
8 | {
9 | public class CircleTest
10 | {
11 | public struct CircleTestCase
12 | {
13 | public Ray Ray;
14 | public Circle Circle;
15 | public bool ExpectedResult;
16 | public string Description;
17 |
18 | public override string ToString()
19 | {
20 | return Description;
21 | }
22 | }
23 |
24 | private static List testCases = new List()
25 | {
26 | new CircleTestCase()
27 | {
28 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
29 | Ray = new Ray()
30 | {
31 | Origin = new Vector2(4, 0),
32 | Direction = new Vector2(-1, 0),
33 | Magnitude = 5f,
34 | },
35 | ExpectedResult = true,
36 | Description = "Intersection near origin"
37 | },
38 | new CircleTestCase()
39 | {
40 | Circle = new Circle() { Position = new Vector2(2, 2), Radius = 1f},
41 | Ray = new Ray() { Origin = new Vector2(0, 2), Direction = new Vector2(1, 0), Magnitude = 50f },
42 | ExpectedResult = true,
43 | Description = "Orthogonal from left"
44 | },
45 | new CircleTestCase()
46 | {
47 | Circle = new Circle() { Position = new Vector2(2, 2), Radius = 1f},
48 | Ray = new Ray() { Origin = new Vector2(2, 4), Direction = new Vector2(0, -1), Magnitude = 50f },
49 | ExpectedResult = true,
50 | Description = "Orthogonal from top"
51 | },
52 | new CircleTestCase()
53 | {
54 | Circle = new Circle() { Position = new Vector2(2, 2), Radius = 1f},
55 | Ray = new Ray() { Origin = new Vector2(4, 2), Direction = new Vector2(-1, 0), Magnitude = 50f },
56 | ExpectedResult = true,
57 | Description = "Orthogonal from right"
58 | },
59 | new CircleTestCase()
60 | {
61 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
62 | Ray = new Ray() { Origin = new Vector2(0, -2), Direction = new Vector2(0, 1), Magnitude = 50f },
63 | ExpectedResult = true,
64 | Description = "Orthogonal from bottom"
65 | },
66 | new CircleTestCase()
67 | {
68 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
69 | Ray = new Ray() { Origin = new Vector2(-4, 4), Direction = new Vector2(1, -1), Magnitude = 50f },
70 | ExpectedResult = true,
71 | Description = "Orthogonal from top left"
72 | },
73 | new CircleTestCase()
74 | {
75 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
76 | Ray = new Ray() { Origin = new Vector2(-4, -4), Direction = new Vector2(1, 1), Magnitude = 50f },
77 | ExpectedResult = true,
78 | Description = "Orthogonal from bottom left"
79 | },
80 | new CircleTestCase()
81 | {
82 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
83 | Ray = new Ray() { Origin = new Vector2(4, 4), Direction = new Vector2(-1, -1), Magnitude = 50f },
84 | ExpectedResult = true,
85 | Description = "Orthogonal from top right"
86 | },
87 | new CircleTestCase()
88 | {
89 | Circle = new Circle() { Position = new Vector2(0, 0), Radius = 1f},
90 | Ray = new Ray() { Origin = new Vector2(4, -4), Direction = new Vector2(-1, 1), Magnitude = 50f },
91 | ExpectedResult = true,
92 | Description = "Orthogonal from bottom right"
93 | },
94 | new CircleTestCase()
95 | {
96 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
97 | Ray = new Ray() { Origin = new Vector2(0, 3), Direction = new Vector2(1, 0), Magnitude = 50f },
98 | ExpectedResult = true,
99 | Description = "Touch the circle at the bottom"
100 | },
101 | new CircleTestCase()
102 | {
103 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
104 | Ray = new Ray() { Origin = new Vector2(0, 10), Direction = new Vector2(1, 0), Magnitude = 50f },
105 | ExpectedResult = false,
106 | Description = "Ray that flies above the circle"
107 | },
108 | new CircleTestCase()
109 | {
110 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
111 | Ray = new Ray() { Origin = new Vector2(0, -10), Direction = new Vector2(1, 0), Magnitude = 50f },
112 | ExpectedResult = false,
113 | Description = "Ray that flies beneath the circle"
114 | },
115 | new CircleTestCase()
116 | {
117 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
118 | Ray = new Ray() { Origin = new Vector2(0, 0), Direction = new Vector2(0, 1), Magnitude = 50f },
119 | ExpectedResult = false,
120 | Description = "Ray that flies to the left of the circle"
121 | },
122 | new CircleTestCase()
123 | {
124 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
125 | Ray = new Ray() { Origin = new Vector2(6, 0), Direction = new Vector2(0, 1), Magnitude = 50f },
126 | ExpectedResult = false,
127 | Description = "Ray that flies to the right of the circle"
128 | },
129 | new CircleTestCase()
130 | {
131 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
132 | Ray = new Ray() { Origin = new Vector2(4, 4), Direction = new Vector2(0, 1), Magnitude = 50f },
133 | ExpectedResult = true,
134 | Description = "Ray from the inside the circle"
135 | },
136 | new CircleTestCase()
137 | {
138 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 0.1f},
139 | Ray = new Ray() { Origin = new Vector2(4, 4), Direction = new Vector2(0, 1), Magnitude = 50f },
140 | ExpectedResult = true,
141 | Description = "Ray from the inside of a smaller the circle"
142 | },
143 | new CircleTestCase()
144 | {
145 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 0.001f},
146 | Ray = new Ray() { Origin = new Vector2(4, 4), Direction = new Vector2(0, 1), Magnitude = 50f },
147 | ExpectedResult = true,
148 | Description = "Ray from the inside of a tiny circle"
149 | },
150 | new CircleTestCase()
151 | {
152 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 0.001f},
153 | Ray = new Ray() { Origin = new Vector2(0, 0), Direction = new Vector2(1, 1), Magnitude = 1f },
154 | ExpectedResult = false,
155 | Description = "Ray end before reaching intersection"
156 | },
157 | new CircleTestCase()
158 | {
159 | Circle = new Circle() { Position = new Vector2(4, 4), Radius = 1f},
160 | Ray = new Ray() { Origin = new Vector2(0, 0), Direction = new Vector2(1, 1), Magnitude = 1f },
161 | ExpectedResult = false,
162 | Description = "Ray end before reaching intersection"
163 | }
164 | };
165 |
166 | [Test]
167 | [TestCaseSource("testCases")]
168 | public void TestIntersect(CircleTestCase testCase)
169 | {
170 | Console.WriteLine(testCase.Description);
171 | Assert.AreEqual(testCase.Circle.Intersects(testCase.Ray), testCase.ExpectedResult, testCase.Description);
172 | }
173 | }
174 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | /packages/
4 | riderModule.iml
5 | /_ReSharper.Caches/
6 | .idea
7 |
8 | # =============================================
9 | # Visual Studio & Associated technologies
10 | # =============================================
11 |
12 | ## Ignore Visual Studio temporary files, build results, and
13 | ## files generated by popular Visual Studio add-ons.
14 | ##
15 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
16 |
17 | # User-specific files
18 | *.suo
19 | *.user
20 | *.userosscache
21 | *.sln.docstates
22 |
23 | # User-specific files (MonoDevelop/Xamarin Studio)
24 | *.userprefs
25 |
26 | # Build results
27 | [Dd]ebug/
28 | [Dd]ebugPublic/
29 | [Rr]elease/
30 | [Rr]eleases/
31 | x64/
32 | x86/
33 | bld/
34 | [Bb]in/
35 | [Oo]bj/
36 | [Ll]og/
37 |
38 | # Visual Studio 2015 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | #wwwroot/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUNIT
48 | *.VisualState.xml
49 | TestResult.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 | **/Properties/launchSettings.json
64 |
65 | *_i.c
66 | *_p.c
67 | *_i.h
68 | *.ilk
69 | *.meta
70 | *.obj
71 | *.pch
72 | *.pdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *.log
83 | *.vspscc
84 | *.vssscc
85 | .builds
86 | *.pidb
87 | *.svclog
88 | *.scc
89 |
90 | # Chutzpah Test files
91 | _Chutzpah*
92 |
93 | # Visual C++ cache files
94 | ipch/
95 | *.aps
96 | *.ncb
97 | *.opendb
98 | *.opensdf
99 | *.sdf
100 | *.cachefile
101 | *.VC.db
102 | *.VC.VC.opendb
103 |
104 | # Visual Studio profiler
105 | *.psess
106 | *.vsp
107 | *.vspx
108 | *.sap
109 |
110 | # TFS 2012 Local Workspace
111 | $tf/
112 |
113 | # Guidance Automation Toolkit
114 | *.gpState
115 |
116 | # ReSharper is a .NET coding add-in
117 | _ReSharper*/
118 | *.[Rr]e[Ss]harper
119 | *.DotSettings.user
120 |
121 | # JustCode is a .NET coding add-in
122 | .JustCode
123 |
124 | # TeamCity is a build add-in
125 | _TeamCity*
126 |
127 | # DotCover is a Code Coverage Tool
128 | *.dotCover
129 |
130 | # Visual Studio code coverage results
131 | *.coverage
132 | *.coveragexml
133 |
134 | # NCrunch
135 | _NCrunch_*
136 | .*crunch*.local.xml
137 | nCrunchTemp_*
138 |
139 | # MightyMoose
140 | *.mm.*
141 | AutoTest.Net/
142 |
143 | # Web workbench (sass)
144 | .sass-cache/
145 |
146 | # Installshield output folder
147 | [Ee]xpress/
148 |
149 | # DocProject is a documentation generator add-in
150 | DocProject/buildhelp/
151 | DocProject/Help/*.HxT
152 | DocProject/Help/*.HxC
153 | DocProject/Help/*.hhc
154 | DocProject/Help/*.hhk
155 | DocProject/Help/*.hhp
156 | DocProject/Help/Html2
157 | DocProject/Help/html
158 |
159 | # Click-Once directory
160 | publish/
161 |
162 | # Publish Web Output
163 | *.[Pp]ublish.xml
164 | *.azurePubxml
165 | # but database connection strings (with potential passwords) will be unencrypted
166 | *.pubxml
167 | *.publishproj
168 |
169 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
170 | # checkin your Azure Web App publish settings, but sensitive information contained
171 | # in these scripts will be unencrypted
172 | PublishScripts/
173 |
174 | # NuGet Packages
175 | *.nupkg
176 | # The packages folder can be ignored because of Package Restore
177 | **/packages/*
178 | # except build/, which is used as an MSBuild target.
179 | !**/packages/build/
180 | # Uncomment if necessary however generally it will be regenerated when needed
181 | #!**/packages/repositories.config
182 | # NuGet v3's project.json files produces more ignorable files
183 | *.nuget.props
184 | *.nuget.targets
185 |
186 | # Microsoft Azure Build Output
187 | csx/
188 | *.build.csdef
189 |
190 | # Microsoft Azure Emulator
191 | ecf/
192 | rcf/
193 |
194 | # Windows Store app package directories and files
195 | AppPackages/
196 | BundleArtifacts/
197 | Package.StoreAssociation.xml
198 | _pkginfo.txt
199 | *.appx
200 |
201 | # Visual Studio cache files
202 | # files ending in .cache can be ignored
203 | *.[Cc]ache
204 | # but keep track of directories ending in .cache
205 | !*.[Cc]ache/
206 |
207 | # Others
208 | ClientBin/
209 | ~$*
210 | *~
211 | *.dbmdl
212 | *.dbproj.schemaview
213 | *.jfm
214 | *.pfx
215 | *.publishsettings
216 | orleans.codegen.cs
217 |
218 | # Since there are multiple workflows, uncomment next line to ignore bower_components
219 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
220 | #bower_components/
221 |
222 | # RIA/Silverlight projects
223 | Generated_Code/
224 |
225 | # Backup & report files from converting an old project file
226 | # to a newer Visual Studio version. Backup files are not needed,
227 | # because we have git ;-)
228 | _UpgradeReport_Files/
229 | Backup*/
230 | UpgradeLog*.XML
231 | UpgradeLog*.htm
232 |
233 | # SQL Server files
234 | *.mdf
235 | *.ldf
236 | *.ndf
237 |
238 | # Business Intelligence projects
239 | *.rdl.data
240 | *.bim.layout
241 | *.bim_*.settings
242 |
243 | # Microsoft Fakes
244 | FakesAssemblies/
245 |
246 | # GhostDoc plugin setting file
247 | *.GhostDoc.xml
248 |
249 | # Node.js Tools for Visual Studio
250 | .ntvs_analysis.dat
251 | node_modules/
252 |
253 | # Typescript v1 declaration files
254 | typings/
255 |
256 | # Visual Studio 6 build log
257 | *.plg
258 |
259 | # Visual Studio 6 workspace options file
260 | *.opt
261 |
262 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
263 | *.vbw
264 |
265 | # Visual Studio LightSwitch build output
266 | **/*.HTMLClient/GeneratedArtifacts
267 | **/*.DesktopClient/GeneratedArtifacts
268 | **/*.DesktopClient/ModelManifest.xml
269 | **/*.Server/GeneratedArtifacts
270 | **/*.Server/ModelManifest.xml
271 | _Pvt_Extensions
272 |
273 | # Paket dependency manager
274 | .paket/paket.exe
275 | paket-files/
276 |
277 | # FAKE - F# Make
278 | .fake/
279 |
280 | # CodeRush
281 | .cr/
282 |
283 | # Python Tools for Visual Studio (PTVS)
284 | __pycache__/
285 | *.pyc
286 |
287 | # Cake - Uncomment if you are using it
288 | # tools/**
289 | # !tools/packages.config
290 |
291 | # Tabs Studio
292 | *.tss
293 |
294 | # Telerik's JustMock configuration file
295 | *.jmconfig
296 |
297 | # BizTalk build output
298 | *.btp.cs
299 | *.btm.cs
300 | *.odx.cs
301 | *.xsd.cs
302 |
303 | # =========================
304 | # Windows detritus
305 | # =========================
306 |
307 | # Windows thumbnail cache files
308 | Thumbs.db
309 | ehthumbs.db
310 | ehthumbs_vista.db
311 |
312 | # Dump file
313 | *.stackdump
314 |
315 | # Folder config file
316 | Desktop.ini
317 |
318 | # Recycle Bin used on file shares
319 | $RECYCLE.BIN/
320 |
321 | # Windows Installer files
322 | *.cab
323 | *.msi
324 | *.msm
325 | *.msp
326 |
327 | # Windows shortcuts
328 | *.lnk
329 |
330 | # ===========
331 | # Macintosh
332 | # ===========
333 |
334 | # General
335 | *.DS_Store
336 | .AppleDouble
337 | .LSOverride
338 |
339 | # Icon must end with two \r
340 | Icon
341 |
342 |
343 | # Thumbnails
344 | ._*
345 |
346 | # Files that might appear in the root of a volume
347 | .DocumentRevisions-V100
348 | .fseventsd
349 | .Spotlight-V100
350 | .TemporaryItems
351 | .Trashes
352 | .VolumeIcon.icns
353 | .com.apple.timemachine.donotpresent
354 |
355 | # Directories potentially created on remote AFP share
356 | .AppleDB
357 | .AppleDesktop
358 | Network Trash Folder
359 | Temporary Items
360 | .apdisk
361 |
362 | # ===================================================
363 | # Exclude F# project specific directories and files
364 | # ===================================================
365 |
366 | # NuGet Packages Directory
367 | packages/
368 |
369 | # Generated documentation folder
370 | docs/output/
371 |
372 | # Temp folder used for publishing docs
373 | temp/
374 |
375 | # Test results produced by build
376 | TestResults.xml
377 | output.mlpd
378 | coverage.xml
379 |
380 | # Nuget outputs
381 | nuget/*.nupkg
382 | release.cmd
383 | release.sh
384 | localpackages/
385 | paket-files
386 | *.orig
387 | .paket/paket.exe
388 | docs/content/license.md
389 | docs/content/release-notes.md
390 | .fake
391 | docs/tools/FSharp.Formatting.svclog
392 |
393 | # ===========
394 | # Covers JetBrains IDEs: Rider, IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
395 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
396 | # ===========
397 |
398 | # User-specific stuff:
399 | .idea/**/workspace.xml
400 | .idea/**/tasks.xml
401 | .idea/dictionaries
402 |
403 | # Sensitive or high-churn files:
404 | .idea/**/dataSources/
405 | .idea/**/dataSources.ids
406 | .idea/**/dataSources.xml
407 | .idea/**/dataSources.local.xml
408 | .idea/**/sqlDataSources.xml
409 | .idea/**/dynamic.xml
410 | .idea/**/uiDesigner.xml
411 |
412 | # Gradle:
413 | .idea/**/gradle.xml
414 | .idea/**/libraries
415 |
416 | # CMake
417 | cmake-build-debug/
418 |
419 | # Mongo Explorer plugin:
420 | .idea/**/mongoSettings.xml
421 |
422 | ## File-based project format:
423 | *.iws
424 |
425 | ## Plugin-specific files:
426 |
427 | # IntelliJ
428 | /out/
429 |
430 | # mpeltonen/sbt-idea plugin
431 | .idea_modules/
432 |
433 | # JIRA plugin
434 | atlassian-ide-plugin.xml
435 |
436 | # Cursive Clojure plugin
437 | .idea/replstate.xml
438 |
439 | # Crashlytics plugin (for Android Studio and IntelliJ)
440 | com_crashlytics_export_strings.xml
441 | crashlytics.properties
442 | crashlytics-build.properties
443 | fabric.properties
444 |
445 | # ============
446 | # MonoDevelop
447 | # ============
448 |
449 | #User Specific
450 | *.userprefs
451 | *.usertasks
452 |
453 | #Mono Project Files
454 | *.pidb
455 | *.resources
456 | test-results/
457 |
458 | # ================
459 | # Linux-specific
460 | # ================
461 |
462 | *~
463 |
464 | # temporary files which can be created if a process still has a handle open of a deleted file
465 | .fuse_hidden*
466 |
467 | # KDE directory preferences
468 | .directory
469 |
470 | # Linux trash folder which might appear on any partition or disk
471 | .Trash-*
472 |
473 | # .nfs files are created when an open file is removed but is still being accessed
474 | .nfs*
475 |
476 | # ============
477 | # VS Code
478 | # ============
479 |
480 | .vscode/*
481 | !.vscode/settings.json
482 | !.vscode/tasks.json
483 | !.vscode/launch.json
484 | !.vscode/extensions.json
485 |
486 | # ============
487 | # Emacs
488 | # ============
489 |
490 | # -*- mode: gitignore; -*-
491 | *~
492 | \#*\#
493 | /.emacs.desktop
494 | /.emacs.desktop.lock
495 | *.elc
496 | auto-save-list
497 | tramp
498 | .\#*
499 |
500 | # Org-mode
501 | .org-id-locations
502 | *_archive
503 |
504 | # flymake-mode
505 | *_flymake.*
506 |
507 | # eshell files
508 | /eshell/history
509 | /eshell/lastdir
510 |
511 | # elpa packages
512 | /elpa/
513 |
514 | # reftex files
515 | *.rel
516 |
517 | # AUCTeX auto folder
518 | /auto/
519 |
520 | # cask packages
521 | .cask/
522 | dist/
523 |
524 | # Flycheck
525 | flycheck_*.el
526 |
527 | # server auth directory
528 | /server/
529 |
530 | # projectiles files
531 | .projectile
532 |
533 | # directory configuration
534 | .dir-locals.el
535 |
536 | # ===========
537 | # Vim
538 | # ===========
539 |
540 | # Swap
541 | [._]*.s[a-v][a-z]
542 | [._]*.sw[a-p]
543 | [._]s[a-v][a-z]
544 | [._]sw[a-p]
545 |
546 | # Session
547 | Session.vim
548 |
549 | # Temporary
550 | .netrwhist
551 | *~
552 | # Auto-generated tag files
553 | tags
--------------------------------------------------------------------------------
/infogr-raytracer/Surface.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Drawing.Imaging;
4 | using OpenTK.Graphics.OpenGL;
5 | using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;
6 |
7 | namespace infogr_raytracer
8 | {
9 | public class Surface
10 | {
11 | public readonly int Width;
12 | public readonly int Height;
13 | public int[] Pixels;
14 |
15 | // Font
16 | private static bool _fontReady = false;
17 | private static Surface _font;
18 | private static int[] _fontRedir;
19 |
20 | // OpenGL values for rendering the screen's texture
21 | private readonly float[] _vertices =
22 | {
23 | // Position Texture coordinates
24 | 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // top right
25 | 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // bottom right
26 | -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // bottom left
27 | -1.0f, 1.0f, 0.0f, 0.0f, 0.0f // top left
28 | };
29 |
30 | private readonly uint[] _indices =
31 | {
32 | 0, 1, 3,
33 | 1, 2, 3
34 | };
35 |
36 | private int _vertexBufferObject;
37 | private int _vertexArrayObject;
38 | private int _elementBufferObject;
39 |
40 | private Shader _shader;
41 | private int _textureId;
42 |
43 |
44 | ///
45 | /// Constructs a Surface with a specified width and height
46 | ///
47 | /// The width of the Surface
48 | /// The height of the Surface
49 | public Surface( int w, int h )
50 | {
51 | Width = w;
52 | Height = h;
53 | Pixels = new int[w * h];
54 |
55 | CreateTexture();
56 | }
57 |
58 |
59 | ///
60 | /// Creates the OpenGL VBO, EBO, VAO, Shader, and Texture needed for rendering the Surface.
61 | ///
62 | private void CreateTexture()
63 | {
64 |
65 | _vertexBufferObject = GL.GenBuffer();
66 | GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
67 | GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
68 |
69 | _elementBufferObject = GL.GenBuffer();
70 | GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
71 | GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
72 |
73 | _vertexArrayObject = GL.GenVertexArray();
74 | GL.BindVertexArray(_vertexArrayObject);
75 | GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
76 | GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
77 |
78 | _textureId = GenTexture();
79 | GL.BindTexture(TextureTarget.Texture2D, _textureId);
80 |
81 | _shader = new Shader("shaders/shader.vert", "shaders/shader.frag");
82 | _shader.Use();
83 |
84 | int vertexLocation = _shader.GetAttribLocation("aPosition");
85 | GL.EnableVertexAttribArray(vertexLocation);
86 | GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
87 |
88 | int texCoordLocation = _shader.GetAttribLocation("aTexCoord");
89 | GL.EnableVertexAttribArray(texCoordLocation);
90 | GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
91 | }
92 |
93 | ///
94 | /// Constructs a Surface using a file
95 | ///
96 | /// The filename of the surface
97 | public Surface( string fileName )
98 | {
99 | Bitmap bmp = new Bitmap( fileName );
100 | Width = bmp.Width;
101 | Height = bmp.Height;
102 | Pixels = new int[Width * Height];
103 | BitmapData data = bmp.LockBits( new System.Drawing.Rectangle( 0, 0, Width, Height ), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb );
104 | IntPtr ptr = data.Scan0;
105 | System.Runtime.InteropServices.Marshal.Copy( data.Scan0, Pixels, 0, Width * Height );
106 | bmp.UnlockBits( data );
107 | }
108 |
109 | ///
110 | /// Creates an OpenGL texture
111 | ///
112 | /// An ID of the texture provided by OpenGL
113 | public int GenTexture()
114 | {
115 | int id = GL.GenTexture();
116 | GL.BindTexture( TextureTarget.Texture2D, id );
117 | GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear );
118 | GL.TexParameter( TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear );
119 | GL.TexImage2D( TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, Width, Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, Pixels );
120 | return id;
121 | }
122 |
123 | ///
124 | /// Clears the surface to a specified color
125 | ///
126 | /// The color that the surface should be cleared to
127 | public void Clear( int c )
128 | {
129 | for( int s = Width * Height, p = 0; p < s; p++ ) Pixels[p] = c;
130 | }
131 |
132 | // copy the surface to another surface
133 | ///
134 | /// Copy the surface to another surface
135 | ///
136 | /// The surface that should be copied to
137 | ///
138 | ///
139 | public void CopyTo( Surface target, int x = 0, int y = 0 )
140 | {
141 | int src = 0;
142 | int dst = 0;
143 | int srcwidth = Width;
144 | int srcheight = Height;
145 | int dstwidth = target.Width;
146 | int dstheight = target.Height;
147 | if( (srcwidth + x) > dstwidth ) srcwidth = dstwidth - x;
148 | if( (srcheight + y) > dstheight ) srcheight = dstheight - y;
149 | if( x < 0 )
150 | {
151 | src -= x;
152 | srcwidth += x;
153 | x = 0;
154 | }
155 | if( y < 0 )
156 | {
157 | src -= y * Width;
158 | srcheight += y;
159 | y = 0;
160 | }
161 | if( (srcwidth > 0) && (srcheight > 0) )
162 | {
163 | dst += x + dstwidth * y;
164 | for( int v = 0; v < srcheight; v++ )
165 | {
166 | for( int u = 0; u < srcwidth; u++ ) target.Pixels[dst + u] = Pixels[src + u];
167 | dst += dstwidth;
168 | src += Width;
169 | }
170 | }
171 | }
172 |
173 | ///
174 | /// Draw a rectangle
175 | ///
176 | /// The X component of the top-left coordinate
177 | /// The Y component of the top-left coordinate
178 | /// The X component of the bottom-right coordinate
179 | /// The Y component of the bottom-right coordinate
180 | /// The color that the bar should be drawn in
181 | public void Box( int x1, int y1, int x2, int y2, int c )
182 | {
183 | int dest = y1 * Width;
184 | for( int y = y1; y <= y2; y++, dest += Width )
185 | {
186 | Pixels[dest + x1] = c;
187 | Pixels[dest + x2] = c;
188 | }
189 | int dest1 = y1 * Width;
190 | int dest2 = y2 * Width;
191 | for( int x = x1; x <= x2; x++ )
192 | {
193 | Pixels[dest1 + x] = c;
194 | Pixels[dest2 + x] = c;
195 | }
196 | }
197 |
198 | ///
199 | /// Draw a solid bar
200 | ///
201 | /// The X component of the top-left coordinate
202 | /// The Y component of the top-left coordinate
203 | /// The X component of the bottom-right coordinate
204 | /// The Y component of the bottom-right coordinate
205 | /// The color that the bar should be drawn in
206 | public void Bar( int x1, int y1, int x2, int y2, int c )
207 | {
208 | int dest = y1 * Width;
209 | for( int y = y1; y <= y2; y++, dest += Width ) for( int x = x1; x <= x2; x++ )
210 | {
211 | Pixels[dest + x] = c;
212 | }
213 | }
214 | ///
215 | /// helper function for line clipping
216 | ///
217 | int OUTCODE( int x, int y )
218 | {
219 | int xmin = 0, ymin = 0, xmax = Width - 1, ymax = Height - 1;
220 | return (((x) < xmin) ? 1 : (((x) > xmax) ? 2 : 0)) + (((y) < ymin) ? 4 : (((y) > ymax) ? 8 : 0));
221 | }
222 |
223 | ///
224 | /// Draw a line, clipped to the window
225 | ///
226 | /// The X component of the top-left coordinate
227 | /// The Y component of the top-left coordinate
228 | /// The X component of the bottom-right coordinate
229 | /// The Y component of the bottom-right coordinate
230 | /// The color that the bar should be drawn in
231 | public void Line( int x1, int y1, int x2, int y2, int c )
232 | {
233 | int xmin = 0, ymin = 0, xmax = Width - 1, ymax = Height - 1;
234 | int c0 = OUTCODE( x1, y1 ), c1 = OUTCODE( x2, y2 );
235 | bool accept = false;
236 | while( true )
237 | {
238 | if( c0 == 0 && c1 == 0 ) { accept = true; break; }
239 | else if( (c0 & c1) > 0 ) break;
240 | else
241 | {
242 | int x = 0, y = 0;
243 | int co = (c0 > 0) ? c0 : c1;
244 | if( (co & 8) > 0 ) { x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1); y = ymax; }
245 | else if( (co & 4) > 0 ) { x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1); y = ymin; }
246 | else if( (co & 2) > 0 ) { y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1); x = xmax; }
247 | else if( (co & 1) > 0 ) { y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1); x = xmin; }
248 | if( co == c0 ) { x1 = x; y1 = y; c0 = OUTCODE( x1, y1 ); }
249 | else { x2 = x; y2 = y; c1 = OUTCODE( x2, y2 ); }
250 | }
251 | }
252 | if( !accept ) return;
253 | if( Math.Abs( x2 - x1 ) >= Math.Abs( y2 - y1 ) )
254 | {
255 | if( x2 < x1 ) { int h = x1; x1 = x2; x2 = h; h = y2; y2 = y1; y1 = h; }
256 | int l = x2 - x1;
257 | if( l == 0 ) return;
258 | int dy = ((y2 - y1) * 8192) / l;
259 | y1 *= 8192;
260 | for( int i = 0; i < l; i++ )
261 | {
262 | Pixels[x1++ + (y1 / 8192) * Width] = c;
263 | y1 += dy;
264 | }
265 | }
266 | else
267 | {
268 | if( y2 < y1 ) { int h = x1; x1 = x2; x2 = h; h = y2; y2 = y1; y1 = h; }
269 | int l = y2 - y1;
270 | if( l == 0 ) return;
271 | int dx = ((x2 - x1) * 8192) / l;
272 | x1 *= 8192;
273 | for( int i = 0; i < l; i++ )
274 | {
275 | Pixels[x1 / 8192 + y1++ * Width] = c;
276 | x1 += dx;
277 | }
278 | }
279 | }
280 | // plot a single pixel
281 |
282 | ///
283 | /// Plot a single pixel
284 | ///
285 | /// The X position of the pixel
286 | /// The Y position of the pixel
287 | /// The Color of the pixel
288 | public void Plot( int x, int y, int c )
289 | {
290 | if( (x >= 0) && (y >= 0) && (x < Width) && (y < Height) )
291 | {
292 | Pixels[x + y * Width] = c;
293 | }
294 | }
295 |
296 | ///
297 | /// Print a string with it's top-left coroner at a specified position
298 | ///
299 | /// The text of the string
300 | /// The X component of the top-left corner
301 | /// The Y component of the top-left corner
302 | /// The color of the string
303 | public void Print( string t, int x, int y, int c )
304 | {
305 | if( !_fontReady )
306 | {
307 | _font = new Surface( "./assets/font.png" );
308 | string ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+={}[];:<>,.?/\\ ";
309 | _fontRedir = new int[256];
310 | for( int i = 0; i < 256; i++ ) _fontRedir[i] = 0;
311 | for( int i = 0; i < ch.Length; i++ )
312 | {
313 | int l = (int)ch[i];
314 | _fontRedir[l & 255] = i;
315 | }
316 | _fontReady = true;
317 | }
318 | for( int i = 0; i < t.Length; i++ )
319 | {
320 | int f = _fontRedir[(int)t[i] & 255];
321 | int dest = x + i * 12 + y * Width;
322 | int src = f * 12;
323 | for( int v = 0; v < _font.Height; v++, src += _font.Width, dest += Width ) for( int u = 0; u < 12; u++ )
324 | {
325 | if( (_font.Pixels[src + u] & 0xffffff) != 0 ) Pixels[dest + u] = c;
326 | }
327 | }
328 | }
329 |
330 | public void Draw()
331 | {
332 | // Draw the screen
333 | GL.BindVertexArray(_vertexArrayObject);
334 | GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height,
335 | PixelFormat.Bgra, PixelType.UnsignedByte, Pixels);
336 | GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
337 | }
338 |
339 | ///
340 | /// Unloads all OpenGL buffers, VAOs, and shader programs.
341 | ///
342 | public void Unload()
343 | {
344 | GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
345 | GL.BindVertexArray(0);
346 | GL.UseProgram(0);
347 |
348 | GL.DeleteBuffer(_vertexBufferObject);
349 | GL.DeleteBuffer(_elementBufferObject);
350 | GL.DeleteVertexArray(_vertexArrayObject);
351 |
352 | _shader.Dispose();
353 | }
354 | }
355 |
356 | public class Sprite
357 | {
358 | Surface bitmap;
359 | static public Surface target;
360 | int textureID;
361 |
362 | ///
363 | /// Constructs a sprite form a spcified filename
364 | ///
365 | /// The sprite's filename
366 | public Sprite( string fileName )
367 | {
368 | bitmap = new Surface( fileName );
369 | textureID = bitmap.GenTexture();
370 | }
371 |
372 |
373 | ///
374 | /// Draw a sprite with scaling
375 | ///
376 | /// The X position where the sprite should be drawn
377 | /// The Y position where the sprite should be drawn
378 | /// The scale at which the sprite should be drawn
379 | public void Draw( float x, float y, float scale = 1.0f )
380 | {
381 | GL.BindTexture( TextureTarget.Texture2D, textureID );
382 | GL.Enable( EnableCap.Blend );
383 | GL.BlendFunc( BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha );
384 | GL.Begin( PrimitiveType.Quads );
385 | float u1 = (x * 2 - 0.5f * scale * bitmap.Width) / target.Width - 1;
386 | float v1 = 1 - (y * 2 - 0.5f * scale * bitmap.Height) / target.Height;
387 | float u2 = ((x + 0.5f * scale * bitmap.Width) * 2) / target.Width - 1;
388 | float v2 = 1 - ((y + 0.5f * scale * bitmap.Height) * 2) / target.Height;
389 | GL.TexCoord2( 0.0f, 1.0f ); GL.Vertex2( u1, v2 );
390 | GL.TexCoord2( 1.0f, 1.0f ); GL.Vertex2( u2, v2 );
391 | GL.TexCoord2( 1.0f, 0.0f ); GL.Vertex2( u2, v1 );
392 | GL.TexCoord2( 0.0f, 0.0f ); GL.Vertex2( u1, v1 );
393 | GL.End();
394 | GL.Disable( EnableCap.Blend );
395 | }
396 | }
397 | }
--------------------------------------------------------------------------------