public void FillTriangle(Vertex v1, Vertex v2, Vertex v3)
        {
            var verts = SortVertex(v1, v2, v3);

            if (MathUtility.IsEqual(verts[1].pos.y, verts[2].pos.y))
            {
                FillBottomFlatTriangle(verts[0], verts[1], verts[2]);
            }
            else if (verts[0].pos.y == verts[1].pos.y)
            {
                FillTopFlatTriangle(verts[0], verts[1], verts[2]);
            }
            else //将三角面切割成平顶平底两个三角面绘制
            {
                float  t  = (verts[1].pos.y - verts[0].pos.y) / (verts[2].pos.y - verts[0].pos.y);
                Vertex v4 = TransformUtility.LerpVertexInScreenSpace(verts[0], verts[2], t);
                v4.color = ColorUtility.Lerp(verts[0], verts[1], verts[2], v4);
                float y = verts[1].pos.y;
                float x = (int)(verts[0].pos.x + (((float)verts[1].pos.y - verts[0].pos.y) / ((float)verts[2].pos.y - verts[0].pos.y)) *
                                (verts[2].pos.x - verts[0].pos.x));
                v4.pos = new Vector(x, y, verts[1].pos.z);
                FillBottomFlatTriangle(verts[0], verts[1], v4);
                FillTopFlatTriangle(verts[1], v4, verts[2]);
                //测试用
                //DrawTriangleLine(v1.pos, v2.pos, v3.pos, Color.Black);
            }
        }
        //v1为最高点
        public void FillBottomFlatTriangle(Vertex v1, Vertex v2, Vertex v3)
        {
            Vertex topVert         = v1;
            Vertex leftBottomVert  = v2.pos.x < v3.pos.x ? v2 : v3;
            Vertex rightBottomVert = v2.pos.x > v3.pos.x ? v2 : v3;

            Vector p1           = v1.pos;
            Vector p2           = leftBottomVert.pos;
            Vector p3           = rightBottomVert.pos;
            float  invSlope1    = (p2.x - p1.x) / (p2.y - p1.y);
            float  invSlope2    = (p3.x - p1.x) / (p3.y - p1.y);
            float  onePerLength = 1 / (p2.y - p1.y);
            float  curX1        = p1.x;
            float  curX2        = p1.x;

            Color top         = v1.color;
            Color leftBottom  = leftBottomVert.color;
            Color rightBottom = rightBottomVert.color;

            //屏幕原点在左上,所以是Y++不是--
            for (int scanLineY = (int)p1.y; scanLineY <= p2.y; scanLineY++)
            {
                float  t     = (p2.y - scanLineY) * onePerLength;
                Vertex right = TransformUtility.LerpVertexInScreenSpace(rightBottomVert, topVert, t);
                Vertex left  = TransformUtility.LerpVertexInScreenSpace(leftBottomVert, topVert, t);

                if (Application.RenderType == RenderType.VertexColor)
                {
                    DrawScanLine((int)curX1, (int)curX2, scanLineY, left.color, right.color);
                }
                else if (Application.RenderType == RenderType.Texture)
                {
                    DrawScanLine(left, right);
                }

                //避免绘制非常窄的三角形时线段出界
                if ((invSlope1 < 0 && curX1 + invSlope1 >= p2.x) || (invSlope1 > 0 && curX1 + invSlope1 <= p2.x))
                {
                    curX1 += invSlope1;
                }
                if ((invSlope2 < 0 && curX2 + invSlope2 >= p3.x) || (invSlope2 > 0 && curX2 + invSlope2 <= p3.x))
                {
                    curX2 += invSlope2;
                }
            }
        }
        // v1为左上,v2为右上,v3为最低点
        public void FillTopFlatTriangle(Vertex v1, Vertex v2, Vertex v3)
        {
            Vertex bottomVert   = v3;
            Vertex leftTopVert  = v1.pos.x < v2.pos.x ? v1 : v2;
            Vertex rightTopVert = v1.pos.x > v2.pos.x ? v1 : v2;

            Vector p1           = leftTopVert.pos;
            Vector p2           = rightTopVert.pos;
            Vector p3           = v3.pos;
            float  invSlope1    = (p3.x - p1.x) / (p3.y - p1.y);
            float  invSlope2    = (p3.x - p2.x) / (p3.y - p2.y);
            float  onePerLength = 1 / (p3.y - p1.y);
            float  curX1        = p3.x;
            float  curX2        = p3.x;

            Color bottom   = v3.color;
            Color leftTop  = leftTopVert.color;
            Color rightTop = rightTopVert.color;

            for (int scanLineY = (int)p3.y; scanLineY >= p2.y; scanLineY--)
            {
                float  t     = (scanLineY - p1.y) * onePerLength;
                Vertex left  = TransformUtility.LerpVertexInScreenSpace(leftTopVert, bottomVert, t);
                Vertex right = TransformUtility.LerpVertexInScreenSpace(rightTopVert, bottomVert, t);

                if (Application.RenderType == RenderType.VertexColor)
                {
                    DrawScanLine((int)curX1, (int)curX2, scanLineY, left.color, right.color);
                }
                else if (Application.RenderType == RenderType.Texture)
                {
                    DrawScanLine(left, right);
                }

                if ((invSlope1 > 0 && curX1 - invSlope1 >= p1.x) || (invSlope1 < 0 && curX1 - invSlope1 <= p1.x))
                {
                    curX1 -= invSlope1;
                }
                if ((invSlope2 < 0 && curX2 - invSlope2 <= p2.x) || (invSlope2 > 0 && curX2 - invSlope2 >= p2.x))
                {
                    curX2 -= invSlope2;
                }
            }
        }
Example #4
0
        public override void Render()
        {
            if (mesh == null)
            {
                return;
            }
            Vertex[] tempVerts = new Vertex[] { new Vertex(), new Vertex(), new Vertex() };
            int      length    = mesh.Surfaces.Length;

            for (int i = 0; i < length; i++)
            {
                var surface = mesh.Surfaces[i];
                //不把变换后的结果保存顶点中,否则下次渲染顶点位置就不对了
                tempVerts[0].Copy(mesh.Vertices[surface.A]);
                tempVerts[1].Copy(mesh.Vertices[surface.B]);
                tempVerts[2].Copy(mesh.Vertices[surface.C]);

                Vector v1 = tempVerts[0].pos;
                Vector v2 = tempVerts[1].pos;
                Vector v3 = tempVerts[2].pos;

                //Obj -> World
                Vector wp1 = transform.ApplyObj2World(v1);
                Vector wp2 = transform.ApplyObj2World(v2);
                Vector wp3 = transform.ApplyObj2World(v3);

                //World -> view
                Vector vp1 = transform.ApplyWorldToView(wp1);
                Vector vp2 = transform.ApplyWorldToView(wp2);
                Vector vp3 = transform.ApplyWorldToView(wp3);

                //背面消隐
                if (TransformUtility.IsBackCulling(vp1, vp2, vp3))
                {
                    continue;
                }

                //view -> 齐次裁剪空间
                Vector cp1 = transform.ApplyViewToClip(vp1);
                Vector cp2 = transform.ApplyViewToClip(vp2);
                Vector cp3 = transform.ApplyViewToClip(vp3);

                // CVV裁剪
                //if(...)

                //变换到齐次裁剪空间后,w值保存view空间下的z信息
                //保存 1/z 用于后面求u'、v'

                tempVerts[0].onePerZ = 1 / cp1.w;
                tempVerts[1].onePerZ = 1 / cp2.w;
                tempVerts[2].onePerZ = 1 / cp3.w;

                tempVerts[0].u *= tempVerts[0].onePerZ;
                tempVerts[0].v *= tempVerts[0].onePerZ;
                tempVerts[1].u *= tempVerts[1].onePerZ;
                tempVerts[1].v *= tempVerts[1].onePerZ;
                tempVerts[2].u *= tempVerts[2].onePerZ;
                tempVerts[2].v *= tempVerts[2].onePerZ;

                //透视除法 齐次裁剪空间 -> NDC空间
                cp1 /= cp1.w;
                cp2 /= cp2.w;
                cp3 /= cp3.w;

                //屏幕映射 NDC空间下 -> 屏幕空间
                Vector sp1 = Screen.ComputeToScreenPos(cp1);
                Vector sp2 = Screen.ComputeToScreenPos(cp2);
                Vector sp3 = Screen.ComputeToScreenPos(cp3);

                tempVerts[0].pos = sp1;
                tempVerts[1].pos = sp2;
                tempVerts[2].pos = sp3;
                switch (Application.RenderType)
                {
                case RenderType.Wireframe:
                    graphics.DrawTriangleLine(tempVerts[0].pos, tempVerts[1].pos, tempVerts[2].pos, Color.Red);
                    break;

                case RenderType.Fill:
                    graphics.FillTriangle(tempVerts[0].pos, tempVerts[1].pos, tempVerts[2].pos, Color.Red);
                    break;

                case RenderType.VertexColor:
                    graphics.FillTriangle(tempVerts[0], tempVerts[1], tempVerts[2]);
                    break;

                case RenderType.Texture:
                    graphics.FillTriangle(tempVerts[0], tempVerts[1], tempVerts[2]);
                    break;
                }
            }
        }