Видимость платоновых тел

Я делаю то, что будет рисовать платоновые тела с видимостью и постоянной штриховкой. Я должен сделать это без opengl или directX. Я решаю сейчас видимость. Вероятно, меня мог бы решить алгоритм художников, но я не знаю, как реализовать его в своем коде. Вот как я это рисую. Поэтому я прошу совета, как реализовать алгоритм рисования в моем коде. Я уже кое-что знаю об этом алгоритме - теоретически. И, вероятно, будет достаточно просто сделать шаг с сортировкой лиц по координате z.

введите описание изображения здесь
Для проекции я использую матрицу проекции класса, и у меня есть массивы vertexBuffer и indexBuffer, как в OpenGL.
Делаю все в Visual Studio 2010 на C#.

Проекционная матрица

   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; 
        }


    }
}

person user1097772    schedule 11.05.2012    source источник
comment
Какой совет вам нужен? Алгоритм художника довольно прост. Ваша идея использовать z-сортировку звучит как отличный первый шаг. Вам разрешено использовать Z-буфер?   -  person RustyTheBoyRobot    schedule 26.05.2012
comment
Зависит от того, что вы имеете в виду, реализует Z-буфер - если я сделаю свой собственный да, иначе нет. Я не могу использовать Z-буфер, который уже реализован, или, может быть, какое-то решение на GPU - я точно не знаю, для чего он нужен, но я использовал его в OpenGl в каком-то другом приложении.   -  person user1097772    schedule 26.05.2012


Ответы (1)


Если вам нужно учитывать пересекающиеся треугольники (см. изображение), я думаю, что самым простым решением будет использование z-буфера (их довольно легко создать и использовать). Если вам не нужно визуализировать пересекающиеся треугольники, предлагаемое вами решение сортировки по оси z будет проще и будет работать нормально.

введите здесь описание изображения

person RustyTheBoyRobot    schedule 26.05.2012