public void GivenExpectedAnswer_VerifyColor(float r, float g, float b) { var expectedAnswer = Color.FromScRgb(Material.DefaultColorA, r, g, b); var hit = _world.CalculateHit(_rayInstance); var actualAnswer = Lighting.CalculateColorWithPhongReflection(hit, _world.LightSource); // Note: Addition of "shadow rendering" required a reduction of Color precision // for these tests. These tests effected by use of IntersectionDto.OverPosition, // which itself required a reduction in precision to eliminate "acne". // That all said, still looks great to the naked eye! Assert.True(expectedAnswer.AreClose(actualAnswer, true, 4)); }
public void GivenExpectedAnswer_CalculateT1Shadowed_VerifyResult(string in_shadow) { // For Ray from point (_t1), back to the light source: // * Any collisions? // * If so, is hit between the point position and the light? i.e. casts _t1 in shadow var v = _world.LightSource.Position - _t1; var direction = Vector4.Normalize(v); var lightingResult = Lighting.CalculateColorWithPhongReflection( _world, new Model.Ray(_t1, direction)); var expectedAnswer = bool.Parse(in_shadow); var actualAnswer = lightingResult.IsInShadow; Assert.Equal(expectedAnswer, actualAnswer); }
public static void DrawDefaultWorldLarger(string outputBitmapFilePath = null, System.Drawing.Bitmap canvas = null) { var world = CreateDefaultWorldLarger(); var camera = new Camera(110, 110, MathF.PI / 2); // Pull back out a bit more to allow for larger sphere size. // Result kind of cool when you don't - you're seeing the inner circle, // with the silhouette of the outer circle. var from = new Vector4(0F, 0F, -10F, 1F); var to = new Vector4(0F, 0F, 0F, 1F); var up = new Vector4(0F, 1F, 0F, 0F); camera.SetViewTransformation(from, to, up); bool shouldDispose = canvas == null; if (canvas == null) { canvas = new System.Drawing.Bitmap(camera.HorizontalSize, camera.VerticalSize); } for (int y = 0; y < camera.VerticalSize - 1; y++) { for (int x = 0; x < camera.HorizontalSize - 1; x++) { var ray = camera.GetRay(x, y); Color color = Lighting.CalculateColorWithPhongReflection(world, ray); canvas.SetPixel(x, y, color.Simplify(255)); } } if (outputBitmapFilePath != null) { canvas.Save(outputBitmapFilePath); } if (shouldDispose) { canvas.Dispose(); } }
protected override Task Handle(RenderSceneCommand request, CancellationToken cancellationToken) { var shapes = new List <IBasicShape>(); shapes.AddRange(request.Scene.Shapes.Select(ShapeFactory.MapApiToDomain)); var world = new World(shapes, LightFactory.MapApiToDomain(request.Scene.LightSource)); var camera = CameraFactory.MapApiToDomain(request.Scene.Camera); // Render image using var canvas = new System.Drawing.Bitmap(camera.HorizontalSize, camera.VerticalSize); for (int y = 0; y < camera.VerticalSize - 1; y++) { if (cancellationToken.IsCancellationRequested) { break; } for (int x = 0; x < camera.HorizontalSize - 1; x++) { if (cancellationToken.IsCancellationRequested) { break; } var ray = camera.GetRay(x, y); Color color = Lighting.CalculateColorWithPhongReflection(world, ray); canvas.SetPixel(x, y, color.Simplify(255)); } } if (!cancellationToken.IsCancellationRequested) { canvas.Save(request.FilePath); } return(cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : Task.CompletedTask); }
public static void DrawDefaultWorld(string outputBitmapFilePath = null, System.Drawing.Bitmap canvas = null) { var world = CreateDefaultWorld(); var camera = new Camera(11, 11, MathF.PI / 2); var from = new Vector4(0F, 0F, -5F, 1F); var to = new Vector4(0F, 0F, 0F, 1F); var up = new Vector4(0F, 1F, 0F, 0F); camera.SetViewTransformation(from, to, up); bool shouldDispose = canvas == null; if (canvas == null) { canvas = new System.Drawing.Bitmap(camera.HorizontalSize, camera.VerticalSize); } for (int y = 0; y < camera.VerticalSize - 1; y++) { for (int x = 0; x < camera.HorizontalSize - 1; x++) { var ray = camera.GetRay(x, y); Color color = Lighting.CalculateColorWithPhongReflection(world, ray); canvas.SetPixel(x, y, color.Simplify(255)); } } if (outputBitmapFilePath != null) { canvas.Save(outputBitmapFilePath); } if (shouldDispose) { canvas.Dispose(); } }
public void Calculate_Lighting_SetOnResultColor() { _resultantColor = Lighting.CalculateColorWithPhongReflection( _materialInstance, _lightInstance, _pointPosition, _eye, _surfaceNormal, _isInShadow ); }
public static void RenderSphereCentral(string outputBitmapFilePath, bool useAcneEffect = false) { // Define world var floor = Sphere.CreateDefaultInstance(); floor.Transformation = new MatrixTransformationBuilder() .Scale(new Vector3(10F, 0.01F, 10F)); floor.UpdateColor(Color.FromScRgb(Material.DefaultColorA, 1F, 0.9F, 0.9F)) .UpdateSpecular(0F); var left_wall = Sphere.CreateDefaultInstance(); left_wall.Transformation = new MatrixTransformationBuilder() .Scale(new Vector3(10F, 0.01F, 10F)) .RotateX(MathF.PI / 2) .RotateY(-MathF.PI / 4) .Translate(new Vector3(0F, 0F, 5F)) ; left_wall.Material = floor.Material; var right_wall = Sphere.CreateDefaultInstance(); right_wall.Transformation = new MatrixTransformationBuilder() .Scale(new Vector3(10F, 0.01F, 10F)) .RotateX(MathF.PI / 2) .RotateY(MathF.PI / 4) .Translate(new Vector3(0F, 0F, 5F)) ; right_wall.Material = floor.Material; var middle = Sphere.CreateDefaultInstance(); middle.Transformation = new MatrixTransformationBuilder() .Translate(new Vector3(-0.5F, 1F, 0.5F)); middle.UpdateColor(Color.FromScRgb(Material.DefaultColorA, 0.1F, 1F, 0.5F)) .UpdateDiffuse(0.7F) .UpdateSpecular(0.3F); var right = Sphere.CreateDefaultInstance(); right.Transformation = new MatrixTransformationBuilder() .Scale(new Vector3(0.5F, 0.5F, 0.5F)) .Translate(new Vector3(1.5F, 0.5F, -0.5F)) ; right.UpdateColor(Color.FromScRgb(Material.DefaultColorA, 0.5F, 1F, 0.1F)) .UpdateDiffuse(0.7F) .UpdateSpecular(0.3F); var left = Sphere.CreateDefaultInstance(); left.Transformation = new MatrixTransformationBuilder() .Scale(new Vector3(0.33F, 0.33F, 0.33F)) .Translate(new Vector3(-1.5F, 0.33F, -0.75F)) ; left.UpdateColor(Color.FromScRgb(Material.DefaultColorA, 1F, 0.8F, 0.1F)) .UpdateDiffuse(0.7F) .UpdateSpecular(0.3F); var world = new World(new List <IBasicShape> { floor, left_wall, right_wall, left, middle, right }, new Light { Position = new Vector4(-10.0F, 10.0F, -10.0F, 1.0F), Intensity = Color.White }); // Use low res until happy, then crank up. Takes a lot of clock cycles! #pragma warning disable CS0219 // Variable is assigned but its value is never used int high = 1000; int medium = 500; int low = 250; int res = medium; #pragma warning restore CS0219 // Variable is assigned but its value is never used var camera = new Camera(res, res / 2, MathF.PI / 3); camera.SetViewTransformation( from: new Vector4(0F, 1.5F, -5F, 1F), to: new Vector4(0F, 1F, 0F, 1F), up: new Vector4(0F, 1F, 0F, 0F) ); // Render image using var canvas = new System.Drawing.Bitmap(camera.HorizontalSize, camera.VerticalSize); for (int y = 0; y < camera.VerticalSize - 1; y++) { for (int x = 0; x < camera.HorizontalSize - 1; x++) { var ray = camera.GetRay(x, y); Color color = Lighting.CalculateColorWithPhongReflection(world, ray, useAcneEffect); canvas.SetPixel(x, y, color.Simplify(255)); } } canvas.Save(outputBitmapFilePath); }
// NOTE: Using the 2D version as starting place. Couple of additions should turn it into a glorious 3D render! public static void DrawSphere(string outputBitmapFilePath, IMatrixTransformationBuilder transformation) { // Rough and ready. Fully following the pseudocode from the text. // Basically: // For each pixel on the canvas, figure out the ray (direction) from there // back to the origin of the emitting ray (light source). // Then calculate the hit (first intersection), if any. If it's a hit then // that pixel on the canvas gets drawn. // The rest of the code is just pixel conversion e.g. determining what "1" // represents in terms of numbers of pixels and also converting the half above / // half below of the sphere to a non-negative set of pixels for drawing. // inputs: could encapsulate in class to cleanup the code. var ray_origin = new Vector4(0F, 0F, -5F, 1F); var wall_z = 10F; var wall_size = 7.0F; var sphere = Sphere.CreateDefaultInstance(); sphere.UpdateColor(Color.FromScRgb(Material.DefaultColorA, 1.0F, 0.2F, 1.0F)); IBasicShape shape = sphere; shape.Transformation = transformation; var light = new Light { Position = new Vector3(-10F, 10F, -10F).AsPoint(), Intensity = Color.FromScRgb(Material.DefaultColorA, 1.0F, 1.0F, 1.0F) }; #pragma warning disable CS0618 // Type or member is obsolete var xs = new SceneIntersectionCalculator(new List <IBasicShape> { shape }); #pragma warning restore CS0618 // Type or member is obsolete var canvas_pixels = 100; var pixel_size = wall_size / canvas_pixels; var half = wall_size / 2; using var canvas = new System.Drawing.Bitmap(canvas_pixels, canvas_pixels); for (int y = 0; y < canvas_pixels; y++) { var world_y = half - pixel_size * y; for (int x = 0; x < canvas_pixels; x++) { var world_x = -half + pixel_size * x; var position = new Vector4(world_x, world_y, wall_z, 1F); var r = new Domain.Model.Ray( ray_origin, Vector4.Normalize(position - ray_origin)); var hit = xs.CalculateHit(r); if (hit.HasValue) { var color = Lighting.CalculateColorWithPhongReflection(hit, light); // keep the alpha value static, otherwise you get strange results! canvas.SetPixel(x, y, color.Simplify(255)); } } } canvas.Save(outputBitmapFilePath); }