1

我正在做的事情将绘制具有可见性和恒定阴影的柏拉图立体。我必须在没有 opengl 或 directX 的情况下这样做。我现在正在解决能见度。我可能会被画家算法解决,但我不知道如何在我的代码中实现它。这就是我的画法。 所以我在征求意见如何在我的代码中实现画家算法。我已经对这个算法有所了解 - 理论上。只需按照 z 坐标对面进行排序就足够了。

在此处输入图像描述
对于投影,我使用类投影矩阵,并且像 OpenGL 中一样有数组 vertexBuffer 和 indexBuffer。
我用 C# 在 Visual Studio 2010 中完成所有工作。

投影矩阵

   using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KPG3D
{
    /// <summary>
    /// Structure of 3D point
    /// </summary>
    public struct Point3D
    {
        public float X;
        public float Y;
        public float Z;

        public Point3D(float x, float y, float z)
        {
            X = x;
            Y = y;
            Z = z;
        }
    }

    public class ProjectionMatrix
    {
        public float[,] projectionMatrix = new float[4, 4]; //projection matrix

        /// <summary>
        /// Konstructor of projection matrix
        /// </summary>
        public ProjectionMatrix()
        {
            setIdentity();
        }

        /// <summary>
        /// Konstructor of projection matrix
        /// </summary>
        public ProjectionMatrix(float zNear, float zFar, float viewportWidth, float viewportHeight)
        {
            setIdentity();
            float Q = zFar / (zFar - zNear);
            float w = 2 * zNear / viewportWidth;
            float h = 2 * zNear / viewportHeight;

            projectionMatrix[0, 0] = w;
            projectionMatrix[1, 1] = h;
            projectionMatrix[2, 2] = Q;
            projectionMatrix[3, 2] = 1;
            projectionMatrix[2, 3] = -Q * zNear;
        }

        /// <summary>
        /// Konstructor of projection matrix
        /// </summary>
        public ProjectionMatrix(float d, float z)
        {
            setIdentity();//set identity matrix

            projectionMatrix[0, 0] = d / (d - z);
            projectionMatrix[1, 1] = d / (d - z);
            projectionMatrix[2, 2] = 0;
        }

        /// <summary>
        /// Set the matrix to identity
        /// </summary>
        public void setIdentity()
        {
            projectionMatrix[0, 0] = 1;
            projectionMatrix[0, 1] = 0;
            projectionMatrix[0, 2] = 0;
            projectionMatrix[0, 3] = 0;

            projectionMatrix[1, 0] = 0;
            projectionMatrix[1, 1] = 1;
            projectionMatrix[1, 2] = 0;
            projectionMatrix[1, 3] = 0;

            projectionMatrix[2, 0] = 0;
            projectionMatrix[2, 1] = 0;
            projectionMatrix[2, 2] = 1;
            projectionMatrix[2, 3] = 0;

            projectionMatrix[3, 0] = 0;
            projectionMatrix[3, 1] = 0;
            projectionMatrix[3, 2] = 0;
            projectionMatrix[3, 3] = 1;
        }

        /// <summary>
        /// Aplicate projection on set point
        /// </summary>
        /// <param name="p">Point want to transformate</param>
        /// <returns>Transformated point</returns>
        public Point3D multiply(Point3D p)
        {
            Point3D result;

            float tmp = projectionMatrix[3, 0] * p.X + projectionMatrix[3, 1] * p.Y + projectionMatrix[3, 2] * p.Z + projectionMatrix[3, 3] * 1;

            result.X = (projectionMatrix[0, 0] * p.X + projectionMatrix[0, 1] * p.Y + projectionMatrix[0, 2] * p.Z + projectionMatrix[0, 3] * 1) / tmp;
            result.Y = (projectionMatrix[1, 0] * p.X + projectionMatrix[1, 1] * p.Y + projectionMatrix[1, 2] * p.Z + projectionMatrix[1, 3] * 1) / tmp;
            result.Z = (projectionMatrix[2, 0] * p.X + projectionMatrix[2, 1] * p.Y + projectionMatrix[2, 2] * p.Z + projectionMatrix[2, 3] * 1) / tmp;


            return result;
        }
    }
}

形式

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;


namespace KPG3D
{
    public partial class Form1 : Form
    {
        private Bitmap canvasBitmap; //bitmap of drawing area
        private Graphics g;          //access to graffics

        private List<Point3D> vertexBuffer = new List<Point3D>(); //list of all vertices
        private List<int> indexBuffer = new List<int>();//list of all indices
        private Point3D centroid = new Point3D(0, 0, 0);// center of object

        private ProjectionMatrix projection = null;   //projection matrix

        private bool rotation = false;
        private int objectID = 0;

        public Form1()
        {
            InitializeComponent();

            //create bitmap and set to canvas
            canvasBitmap = new Bitmap(canvas.Width, canvas.Height);
            canvas.Image = canvasBitmap;

            //prepare grafics
            g = Graphics.FromImage(canvasBitmap);
            g.SmoothingMode = SmoothingMode.AntiAlias;


            Matrix origin = new Matrix();
            origin.Translate(canvas.Width / 2, canvas.Height / 2);
            g.Transform = origin;
        }

        /// <summary>
        /// Reaction on click on start button
        /// </summary>
        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (projection == null)
            {
                projection = new ProjectionMatrix(1, 100, 1, 1);
                //createBox();
                //createTetrahedron();
                createChosenObject();
            }

            Timer t = new Timer();
            t.Tick += new EventHandler(timerDrawing);//set tick method
            t.Interval = 30; //every 30 ms
            t.Enabled = true;
            t.Start();
        }


        /// <summary>
        /// Create tetrahedron
        /// </summary>
        private void createTetrahedron() 
        {



            Point3D a = new Point3D(-3,-3, 7);//h
            Point3D b = new Point3D( 3,-3,13);//c
            Point3D c = new Point3D( 3, 3, 7);//a
            Point3D d = new Point3D(-3, 3,13);//f

            vertexBuffer.Add(a);
            vertexBuffer.Add(b);
            vertexBuffer.Add(c);
            vertexBuffer.Add(d);

            //acb
            indexBuffer.Add(0);
            indexBuffer.Add(2);
            indexBuffer.Add(1);

            //adc
            indexBuffer.Add(0);
            indexBuffer.Add(3);
            indexBuffer.Add(2);

            //cdb
            indexBuffer.Add(2);
            indexBuffer.Add(3);
            indexBuffer.Add(1);

            //adb
            indexBuffer.Add(0);
            indexBuffer.Add(3);
            indexBuffer.Add(1);

            centroid = new Point3D(0, 0, 10);
        }


        /// <summary>
        /// Create chosen object
        /// </summary>
        private void createChosenObject() {
            switch (objectID) {
                case 1:
                    createTetrahedron();
                    break;
                case 2:
                    createHexahedron();
                    break;
                case 3:
                    createOctahedron();
                    break;
                case 4:
                    createDodecahedron();
                    break;
                case 5:
                    createIcosahedron();
                    break;
                default:
                    //do nothing
                    break;
            }

        }




        /// <summary>
        /// Rotate direcion vector by the specified angle
        /// </summary>
        /// <param name="vector">Direction vector</param>
        /// <param name="angle">Angle of rotation</param>
        /// <returns></returns>
        private Point3D rotateVector(Point3D vector, Point3D centroid, double angle)
        {
            vector.X -= centroid.X;
            vector.Y -= centroid.Y;
            vector.Z -= centroid.Z;

            Point3D result;
            result.X = vector.X * (float)Math.Cos(angle) - vector.Z * (float)Math.Sin(angle);
            result.Z = vector.X * (float)Math.Sin(angle) + vector.Z * (float)Math.Cos(angle);
            result.Y = vector.Y;

            result.X += centroid.X;
            result.Y += centroid.Y;
            result.Z += centroid.Z;

            return result;
        }




        /// <summary>
        /// Reaction on timer
        /// </summary>
        private void timerDrawing(Object obj, EventArgs ea)
        {

            //rotation of object
            if (rotation == true)
            {
                for (int i = 0; i < vertexBuffer.Count; i++)
                {
                    vertexBuffer[i] = rotateVector(vertexBuffer[i], centroid, 0.02);
                }
            }

            //drawing of object
            draw(vertexBuffer, indexBuffer);


            //refresh what is on canvas
            canvas.Invalidate();
        }

        /// <summary>
        ///Draw point and triangles
        /// </summary>
        /// <param name="vert"></param>
        /// <param name="ind"></param>
        private void draw(List<Point3D> vert, List<int> ind)
        {
            //clear drawing area
            g.Clear(Color.Maroon);

            //prepare pen and brush
            Pen pen = new Pen(Color.Black, 1);
            SolidBrush brush = new SolidBrush(Color.Black);
            SolidBrush faceBrush = new SolidBrush(Color.FromArgb(75, Color.Green));

            //draw edges
            for (int i = 0; i < ind.Count / 3; i++)
            {
                Point3D A = projection.multiply(vert[ind[3 * i + 0]]);
                Point3D B = projection.multiply(vert[ind[3 * i + 1]]);
                Point3D C = projection.multiply(vert[ind[3 * i + 2]]);

                //count to 2D
                PointF a = new PointF(A.X * 200, -A.Y * 200);
                PointF b = new PointF(B.X * 200, -B.Y * 200);
                PointF c = new PointF(C.X * 200, -C.Y * 200);

                g.FillPolygon(faceBrush, new PointF[] { a, b, c });

                //draw triangle
                g.DrawLine(pen, a, b);
                g.DrawLine(pen, b, c);
                g.DrawLine(pen, c, a);
            }

            //draw element
            for (int i = 0; i < vert.Count; i++)
            {



                Point3D p = projection.multiply(vert[i]); //projection from 3D to 2D
                g.FillRectangle(brush, p.X * 200 - 2, -p.Y * 200 - 2, 4, 4);


            }

        }



        /// <summary>
        /// Printscreen to file
        /// </summary>
        private void buttonPrintScreen_Click(object sender, EventArgs e)
        {
            canvas.Image.Save("out.png");
        }

        private void checkBoxRotate_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxRotate.Checked) rotation = true;
            else rotation = false;
        }



        private void objectChooser_SelectedIndexChanged(object sender, EventArgs e)
        {
            //set ide of chosen object
            if(objectChooser.SelectedItem.Equals("Tetrahedron")) objectID = 1;
            else if (objectChooser.SelectedItem.Equals("Box")) objectID = 2;
            else if (objectChooser.SelectedItem.Equals("Octahedron")) objectID = 3; 
            else if(objectChooser.SelectedItem.Equals("Dodecahedron")) objectID = 4;
            else if (objectChooser.SelectedItem.Equals("Icosahedron")) objectID = 5; 
        }


    }
}
4

1 回答 1

0

如果您必须考虑相交的三角形(见图),我认为最简单的解决方案将涉及 z 缓冲区(它们很容易创建和使用)。如果您不必渲染相交的三角形,那么您提出的 z 排序解决方案将更简单并且可以正常工作。

在此处输入图像描述

于 2012-05-26T20:46:56.157 回答