예제 #1
0
        private unsafe void FillTriangle(byte *imageData, Triangle3DElement triangle, Camera camera, List <ILightSource> lights, List <double> obstructions)
        {
            Point[] triangle2D = triangle.GetProjection();

            int minX = int.MaxValue;
            int minY = int.MaxValue;

            int maxX = int.MinValue;
            int maxY = int.MinValue;

            for (int i = 0; i < triangle2D.Length; i++)
            {
                triangle2D[i] = new Point((triangle2D[i].X - camera.TopLeft.X) / camera.Size.Width * this.RenderWidth, (triangle2D[i].Y - camera.TopLeft.Y) / camera.Size.Height * this.RenderHeight);

                minX = Math.Min(minX, (int)triangle2D[i].X);
                minY = Math.Min(minY, (int)triangle2D[i].Y);

                maxX = Math.Max(maxX, (int)Math.Ceiling(triangle2D[i].X));
                maxY = Math.Max(maxY, (int)Math.Ceiling(triangle2D[i].Y));
            }

            minX = Math.Max(minX, 0);
            minY = Math.Max(minY, 0);

            maxX = Math.Min(maxX, this.RenderWidth - 1);
            maxY = Math.Min(maxY, this.RenderHeight - 1);

            int totalPixels = (maxX - minX + 1) * (maxY - minY + 1);

            Parallel.For(0, totalPixels, index =>
            {
                int y = index / (maxX - minX + 1) + minY;
                int x = index % (maxX - minX + 1) + minX;

                if (Intersections2D.PointInTriangle(x, y, triangle2D[0], triangle2D[1], triangle2D[2]))
                {
                    Point3D correspPoint = camera.Deproject(new Point((double)x / this.RenderWidth * camera.Size.Width + camera.TopLeft.X, (double)y / this.RenderHeight * camera.Size.Height + camera.TopLeft.Y), triangle);

                    double zDepth = camera.ZDepth(correspPoint);

                    int prevZIndexBuffer = ZIndexBuffer[y * RenderWidth + x];

                    if (prevZIndexBuffer < triangle.ZIndex || (prevZIndexBuffer == triangle.ZIndex && ZBuffer[y * RenderWidth + x] > zDepth))
                    {
                        byte R = 0;
                        byte G = 0;
                        byte B = 0;
                        byte A = 0;

                        for (int i = 0; i < triangle.Fill.Count; i++)
                        {
                            Colour col = triangle.Fill[i].GetColour(correspPoint, triangle.GetNormalAt(correspPoint), camera, lights, obstructions);

                            if (col.A == 1)
                            {
                                R = (byte)(col.R * 255);
                                G = (byte)(col.G * 255);
                                B = (byte)(col.B * 255);
                                A = (byte)(col.A * 255);
                            }
                            else
                            {
                                BlendFront(ref R, ref G, ref B, ref A, (byte)(col.R * 255), (byte)(col.G * 255), (byte)(col.B * 255), (byte)(col.A * 255));
                            }
                        }

                        if (A == 255)
                        {
                            imageData[y * RenderWidth * 4 + x * 4]     = R;
                            imageData[y * RenderWidth * 4 + x * 4 + 1] = G;
                            imageData[y * RenderWidth * 4 + x * 4 + 2] = B;
                            imageData[y * RenderWidth * 4 + x * 4 + 3] = A;
                        }
                        else
                        {
                            BlendFront(ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3], R, G, B, A);
                        }

                        ZBuffer[y * RenderWidth + x]      = zDepth;
                        ZIndexBuffer[y * RenderWidth + x] = triangle.ZIndex;
                    }
                    else if (imageData[y * RenderWidth * 4 + x * 4 + 3] < 255)
                    {
                        byte R = 0;
                        byte G = 0;
                        byte B = 0;
                        byte A = 0;

                        for (int i = 0; i < triangle.Fill.Count; i++)
                        {
                            Colour col = triangle.Fill[i].GetColour(correspPoint, triangle.GetNormalAt(correspPoint), camera, lights, obstructions);

                            if (col.A == 1)
                            {
                                R = (byte)(col.R * 255);
                                G = (byte)(col.G * 255);
                                B = (byte)(col.B * 255);
                                A = (byte)(col.A * 255);
                            }
                            else
                            {
                                BlendFront(ref R, ref G, ref B, ref A, (byte)(col.R * 255), (byte)(col.G * 255), (byte)(col.B * 255), (byte)(col.A * 255));
                            }
                        }

                        BlendBack(R, G, B, A, ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3]);
                    }
                }
            });
        }
예제 #2
0
        private unsafe void FillTriangleWithShadow(byte *imageData, Triangle3DElement triangle, Camera camera, List <ILightSource> lights, List <Triangle3DElement> shadowers)
        {
            Point[] triangle2D = triangle.GetProjection();

            int minX = int.MaxValue;
            int minY = int.MaxValue;

            int maxX = int.MinValue;
            int maxY = int.MinValue;

            for (int i = 0; i < triangle2D.Length; i++)
            {
                triangle2D[i] = new Point((triangle2D[i].X - camera.TopLeft.X) / camera.Size.Width * this.RenderWidth, (triangle2D[i].Y - camera.TopLeft.Y) / camera.Size.Height * this.RenderHeight);

                minX = Math.Min(minX, (int)triangle2D[i].X);
                minY = Math.Min(minY, (int)triangle2D[i].Y);

                maxX = Math.Max(maxX, (int)Math.Ceiling(triangle2D[i].X));
                maxY = Math.Max(maxY, (int)Math.Ceiling(triangle2D[i].Y));
            }

            minX = Math.Max(minX, 0);
            minY = Math.Max(minY, 0);

            maxX = Math.Min(maxX, this.RenderWidth - 1);
            maxY = Math.Min(maxY, this.RenderHeight - 1);

            List <Triangle3DElement> otherShadowers = new List <Triangle3DElement>(shadowers.Count);

            for (int i = 0; i < shadowers.Count; i++)
            {
                if (shadowers[i] != triangle)
                {
                    otherShadowers.Add(shadowers[i]);
                }
            }

            int totalPixels = (maxX - minX + 1) * (maxY - minY + 1);

            Parallel.For(0, totalPixels, index =>
            {
                int y = index / (maxX - minX + 1) + minY;
                int x = index % (maxX - minX + 1) + minX;

                if (Intersections2D.PointInTriangle(x, y, triangle2D[0], triangle2D[1], triangle2D[2]))
                {
                    Point3D correspPoint = camera.Deproject(new Point((double)x / this.RenderWidth * camera.Size.Width + camera.TopLeft.X, (double)y / this.RenderHeight * camera.Size.Height + camera.TopLeft.Y), triangle);

                    double zDepth = camera.ZDepth(correspPoint);

                    int prevZIndexBuffer = ZIndexBuffer[y * RenderWidth + x];

                    if (prevZIndexBuffer < triangle.ZIndex || (prevZIndexBuffer == triangle.ZIndex && ZBuffer[y * RenderWidth + x] > zDepth))
                    {
                        (byte R, byte G, byte B, byte A) = GetPixelColorWithShadow(triangle, lights, otherShadowers, x, y, correspPoint, camera);

                        if (A == 255)
                        {
                            imageData[y * RenderWidth * 4 + x * 4]     = R;
                            imageData[y * RenderWidth * 4 + x * 4 + 1] = G;
                            imageData[y * RenderWidth * 4 + x * 4 + 2] = B;
                            imageData[y * RenderWidth * 4 + x * 4 + 3] = A;
                        }
                        else
                        {
                            BlendFront(ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3], R, G, B, A);
                        }

                        ZBuffer[y * RenderWidth + x]      = zDepth;
                        ZIndexBuffer[y * RenderWidth + x] = triangle.ZIndex;
                    }
                    else if (imageData[y * RenderWidth * 4 + x * 4 + 3] < 255)
                    {
                        (byte R, byte G, byte B, byte A) = GetPixelColorWithShadow(triangle, lights, otherShadowers, x, y, correspPoint, camera);

                        BlendBack(R, G, B, A, ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3]);
                    }
                }
            });
        }
예제 #3
0
        private (byte R, byte G, byte B, byte A) GetPixelColor(Point point, List <Element3D> elements, Camera camera, List <ILightSource> lights, List <Triangle3DElement> shadowers, List <double> noObstructions, bool reproject)
        {
            List <(Element3D element, double z, Point3D correspPoint)> hits = new List <(Element3D element, double z, Point3D correspPoint)>();

            foreach (Element3D element in elements)
            {
                if (element is Triangle3DElement triangle)
                {
                    Point[] projection;

                    if (reproject)
                    {
                        projection = new Point[] { camera.Project(triangle[0]), camera.Project(triangle[1]), camera.Project(triangle[2]) };
                    }
                    else
                    {
                        projection = triangle.GetProjection();
                    }


                    if (Intersections2D.PointInTriangle(point, projection[0], projection[1], projection[2]))
                    {
                        Point3D correspPoint = camera.Deproject(point, triangle);

                        double z = camera.ZDepth(correspPoint);

                        hits.Add((triangle, z, correspPoint));
                    }
                }
                else if (element is Point3DElement pointElement)
                {
                    Point projection;

                    if (reproject)
                    {
                        projection = camera.Project(pointElement[0]);
                    }
                    else
                    {
                        projection = pointElement.GetProjection()[0];
                    }

                    if ((point.X - projection.X) * (point.X - projection.X) + (point.Y - projection.Y) * (point.Y - projection.Y) <= pointElement.Diameter * pointElement.Diameter * 0.25)
                    {
                        double z = camera.ZDepth(pointElement.Point);

                        hits.Add((pointElement, z, pointElement.Point));
                    }
                }
                else if (element is Line3DElement line)
                {
                    Point[] line2D;

                    if (reproject)
                    {
                        line2D = new Point[] { camera.Project(line[0]), camera.Project(line[1]) };
                    }
                    else
                    {
                        line2D = line.GetProjection();
                    }

                    double lineLengthSq = (line2D[1].X - line2D[0].X) * (line2D[1].X - line2D[0].X) + (line2D[1].Y - line2D[0].Y) * (line2D[1].Y - line2D[0].Y);
                    double lineLength   = Math.Sqrt(lineLengthSq);
                    double addedTerm    = line2D[1].X * line2D[0].Y - line2D[1].Y * line2D[0].X;
                    double dy           = line2D[1].Y - line2D[0].Y;
                    double dx           = line2D[1].X - line2D[0].X;

                    double unitsOn  = line.LineDash.UnitsOn / lineLength;
                    double unitsOff = line.LineDash.UnitsOff / lineLength;
                    double phase    = line.LineDash.Phase / lineLength;

                    double dist = Math.Abs(dy * point.X - dx * point.Y + addedTerm) / lineLength;

                    double thickness = line.Thickness * 0.5;

                    if (dist < thickness)
                    {
                        (double t, Point pointOnLine) = Intersections2D.ProjectOnSegment(point, line2D[0], line2D[1]);

                        bool isIn = false;

                        if (line.LineCap == LineCaps.Butt)
                        {
                            if (t >= 0 && t <= 1)
                            {
                                isIn = true;
                            }
                        }
                        else if (line.LineCap == LineCaps.Square)
                        {
                            if (t >= -thickness / lineLength && t <= 1 + thickness / lineLength)
                            {
                                isIn = true;
                            }
                        }
                        else if (line.LineCap == LineCaps.Round)
                        {
                            if (t >= 0 && t <= 1)
                            {
                                isIn = true;
                            }
                            else if (t < -thickness / lineLength || t > 1 + thickness / lineLength)
                            {
                                isIn = false;
                            }
                            else if (t < 0)
                            {
                                double tipDist = (point.X - line2D[0].X) * (point.X - line2D[0].X) + (point.Y - line2D[0].Y) * (point.Y - line2D[0].Y);

                                if (tipDist <= thickness * thickness)
                                {
                                    isIn = true;
                                }
                            }
                            else if (t > 1)
                            {
                                double tipDist = (point.X - line2D[1].X) * (point.X - line2D[1].X) + (point.Y - line2D[1].Y) * (point.Y - line2D[1].Y);

                                if (tipDist <= thickness * thickness)
                                {
                                    isIn = true;
                                }
                            }
                        }

                        if (isIn && IsDashOn(unitsOn, unitsOff, phase, t))
                        {
                            Point3D correspPoint = camera.Deproject(pointOnLine, line);

                            double z = camera.ZDepth(correspPoint);

                            hits.Add((line, z, correspPoint));
                        }
                    }
                }
            }

            hits.Sort((a, b) =>
            {
                if (a.element.ZIndex == b.element.ZIndex)
                {
                    return(Math.Sign(a.z - b.z));
                }
                else
                {
                    return(Math.Sign(b.element.ZIndex - a.element.ZIndex));
                }
            });

            byte pixelR = 0;
            byte pixelG = 0;
            byte pixelB = 0;
            byte pixelA = 0;

            foreach ((Element3D element, double z, Point3D correspPoint)hit in hits)
            {
                byte R = 0;
                byte G = 0;
                byte B = 0;
                byte A = 0;

                if (hit.element is Triangle3DElement triangle)
                {
                    if (!triangle.ReceivesShadow || shadowers == null)
                    {
                        (R, G, B, A) = GetPixelColor(triangle, hit.correspPoint, camera, lights, noObstructions);
                    }
                    else
                    {
                        (R, G, B, A) = GetPixelColorWithShadow(triangle, lights, shadowers, hit.correspPoint, camera);
                    }
                }
                else if (hit.element is Point3DElement pointElement)
                {
                    R = (byte)(pointElement.Colour.R * 255);
                    G = (byte)(pointElement.Colour.G * 255);
                    B = (byte)(pointElement.Colour.B * 255);
                    A = (byte)(pointElement.Colour.A * 255);
                }
                else if (hit.element is Line3DElement lineElement)
                {
                    R = (byte)(lineElement.Colour.R * 255);
                    G = (byte)(lineElement.Colour.G * 255);
                    B = (byte)(lineElement.Colour.B * 255);
                    A = (byte)(lineElement.Colour.A * 255);
                }

                BlendBack(R, G, B, A, ref pixelR, ref pixelG, ref pixelB, ref pixelA);

                if (pixelA == 255)
                {
                    break;
                }
            }

            return(pixelR, pixelG, pixelB, pixelA);
        }