我正在做的事情将绘制具有可见性和恒定阴影的柏拉图立体。我必须在没有 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;
}
}
}