void Triangle(Vector3 t0, Vector3 t1, Vector3 t2, TGAImage image, TGAColor color, int[] zbuffer)
        {
            if (t0.Y == t1.Y && t0.Y == t2.Y)
            {
                return;                               // i dont care about degenerate triangles
            }
            if (t0.Y > t1.Y)
            {
                Swap(ref t0, ref t1);
            }

            if (t0.Y > t2.Y)
            {
                Swap(ref t0, ref t2);
            }

            if (t1.Y > t2.Y)
            {
                Swap(ref t1, ref t2);
            }
            int total_height = (int)(t2.Y - t0.Y);

            for (int i = 0; i < total_height; i++)
            {
                bool    second_half    = i > t1.Y - t0.Y || t1.Y == t0.Y;
                int     segment_height = (int)(second_half ? t2.Y - t1.Y : t1.Y - t0.Y);
                float   alpha          = (float)i / total_height;
                float   beta           = (float)(i - (second_half ? t1.Y - t0.Y : 0)) / segment_height; // be careful: with above conditions no division by zero here
                Vector3 A = t0 + (t2 - t0) * alpha;
                Vector3 B = second_half ? t1 + (t2 - t1) * beta : t0 + (t1 - t0) * beta;
                if (A.X > B.X)
                {
                    Swap(ref A, ref B);
                }
                for (int j = (int)A.X; j <= B.X; j++)
                {
                    float   phi = B.X == A.X ? 1.0f : (float)(j - A.X) / (float)(B.X - A.X);
                    Vector3 P   = A + (B - A) * phi;
                    P.X = j; P.Y = t0.Y + i; // a hack to fill holes (due to int cast precision problems)
                    int idx = (int)(j + (t0.Y + i) * width);
                    if (zbuffer[idx] < P.Z)
                    {
                        zbuffer[idx] = (int)P.Z;
                        image[(int)P.X, (int)P.Y] = color;   // attention, due to int casts t0.Y+i != A.Y
                    }
                }
            }
        }
Exemple #2
0
        public static TGAImage Load(string path)
        {
            using var reader = new BinaryReader(File.OpenRead(path));
            var header = ReadHeader(reader);

            var height  = header.Height;
            var width   = header.Width;
            var bytespp = header.BitsPerPixel >> 3;
            var format  = (Format)bytespp;

            if (width <= 0 || height <= 0)
            {
                throw new InvalidProgramException($"bad image size: width={width} height={height}");
            }
            if (format != Format.BGR && format != Format.BGRA && format != Format.Grayscale)
            {
                throw new InvalidProgramException($"unknown format {format}");
            }

            var img = new TGAImage(width, height, format);

            switch (header.DataTypeCode)
            {
            case DataType.UncompressedTrueColorImage:
            case DataType.UncompressedBlackAndWhiteImage:
                reader.Read(img.Buffer, 0, img.Buffer.Length);
                break;

            case DataType.RleTrueColorImage:
            case DataType.RleBlackAndWhiteImage:
                img.LoadRleData(reader);
                break;

            default:
                throw new InvalidProgramException($"Unsupported image format {header.DataTypeCode}");
            }

            if ((header.ImageDescriptor & 0x20) == 0)
            {
                img.VerticalFlip();
            }

            return(img);
        }
        public void Run()
        {
            var     image     = new TGAImage(width, height, Format.BGR);
            var     model     = new Model("Resources/african_head.obj");
            var     zbuffer   = Enumerable.Repeat(int.MinValue, width * height).ToArray();
            Vector3 light_dir = new Vector3(0, 0, -1);
            var     sw        = Stopwatch.StartNew();

            //Triangle(pts, image, new TGAColor(255, 0, 0));
            for (int i = 0; i < model.Faces.Count; i++)
            {
                var       face          = model.Faces[i];
                Vector3[] screen_coords = new Vector3[3];
                Vector3[] world_coords  = new Vector3[3];
                for (int j = 0; j < 3; j++)
                {
                    Vector3 v = model.Vertices[face[j]];
                    screen_coords[j] = new Vector3((v.X + 1.0f) * width / 2.0f, (v.Y + 1.0f) * height / 2.0f, (v.Z + 1.0f) * depth / 2.0f);
                    world_coords[j]  = v;
                }
                Vector3 n         = Vector3.Cross((world_coords[2] - world_coords[0]), (world_coords[1] - world_coords[0])).Normalize();
                float   intensity = Vector3.Dot(n, light_dir);
                if (intensity > 0)
                {
                    Triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, new TGAColor((byte)(intensity * 255), (byte)(intensity * 255), (byte)(intensity * 255), 255), zbuffer);
                }
            }
            TGAImage zbimage = new TGAImage(width, height, Format.Grayscale);

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    zbimage[i, j] = new TGAColor(zbuffer[i + j * width], Format.Grayscale);
                }
            }
            sw.Stop();
            zbimage.VerticalFlip(); // i want to have the origin at the left bottom corner of the image
            zbimage.WriteToFile("zbuffer.tga");

            Console.WriteLine(sw.Elapsed);
            image.VerticalFlip(); // i want to have the origin at the left bottom corner of the image
            image.WriteToFile("output.tga");
        }
        private void Line(int x0, int y0, int x1, int y1, TGAImage image, TGAColor color)
        {
            var steep = false;

            if (MathF.Abs(x0 - x1) < MathF.Abs(y0 - y1))
            {
                (x0, y0) = (y0, x0);
                (x1, y1) = (y1, x1);
                steep    = true;
            }

            if (x0 > x1)
            {
                (x0, x1) = (x1, x0);
                (y0, y1) = (y1, y0);
            }

            var dx      = x1 - x0;
            var dy      = y1 - y0;
            var derror2 = Math.Abs(dy) * 2;
            var error2  = 0;
            var y       = y0;

            for (var x = x0; x <= x1; x++)
            {
                if (steep)
                {
                    image[y, x] = color;
                }
                else
                {
                    image[x, y] = color;
                }

                error2 += derror2;
                if (error2 > dx)
                {
                    y      += y1 > y0 ? 1 : -1;
                    error2 -= dx * 2;
                }
            }
        }
        void Triangle(Vector3[] pts, float[] zbuffer, TGAImage image, TGAColor color)
        {
            var bboxmin = new Vector2(float.MaxValue, float.MaxValue);
            var bboxmax = new Vector2(float.MinValue, float.MinValue);
            var clamp   = new Vertex2(image.Width - 1, image.Height - 1);

            for (var i = 0; i < 3; i++)
            {
                bboxmin.X = Math.Max(0, Math.Min(bboxmin.X, pts[i].X));
                bboxmax.X = Math.Min(clamp.X, Math.Max(bboxmax.X, pts[i].X));
                bboxmin.Y = Math.Max(0, Math.Min(bboxmin.Y, pts[i].Y));
                bboxmax.Y = Math.Min(clamp.Y, Math.Max(bboxmax.Y, pts[i].Y));
            }
            Vector3 P = default;

            for (P.X = bboxmin.X; P.X <= bboxmax.X; P.X++)
            {
                for (P.Y = bboxmin.Y; P.Y <= bboxmax.Y; P.Y++)
                {
                    Vector3 bc_screen = Barycentric(pts[0], pts[1], pts[2], P);
                    if (bc_screen.X < 0 || bc_screen.Y < 0 || bc_screen.Z < 0)
                    {
                        continue;
                    }
                    P.Z  = 0;
                    P.Z += pts[0].Y * bc_screen.X;
                    P.Z += pts[1].Y * bc_screen.Y;
                    P.Z += pts[2].Y * bc_screen.Z;
                    if (zbuffer[(int)(P.X + P.Y * width)] < P.Z)
                    {
                        zbuffer[(int)(P.X + P.Y * width)] = P.Z;
                        image[(int)P.X, (int)P.Y]         = color;
                    }
                }
            }
        }