Developing an actual 3D engine is an incredible amount of work, and including a 3D library in your project can add a considerable amount of bloat to your project. Fortunately you can implement some very simple 3D projections in just a couple of lines of code, and they are not computationally intensive. There are limitations on using this particular method though:
- This can draw wire frames only.
- You need all of the coordinates of your “world” to be centered horizontally and vertically relative to your screen (or you need to convert them to be that way).
- All of the lines must fit on the screen (e.g. you can’t have the edge of an object partially off-screen)
The basic algorithm is incredibly simple. For each given point (x,y,z) in your 3D model, you convert it to 2D screen coordinates (x’,y’) by the following equations:
x'=d*x/z y'=d*y/z
Where (x,y,z) are the points in your 3D world, (x’,y’) are the x,y coordinates from the center of your screen. And d is the focal length of the camera (or scaling factor), if you’re not sure what to use you can get good results just by experimenting.
Below is C# code to demonstrate this concept. Notice that most of the code is for manipulating, offsetting, and scaling the box, the actual code projecting to 2D is only two lines.
protected override void OnPaint(PaintEventArgs e) { float[] box = { -1,-1,0, 1,-1,0, 1,1,0, -1,1,0, -1,-1,0, -1,-1,2, 1,-1,2, 1,-1,0, 1,-1,2, 1,1,2, 1,1,0, 1,1,2, -1,1,2, -1,1,0, -1,1,2, -1,-1,2 }; base.OnPaint(e); float scale = .2f; for (int x = 0; x < box.Length; x++) box[x] *= scale; for (int j = 0; j < 10; j++) { for (int k = 0; k < 10; k++) { float zoffset = 10f; float xoffset = 1f*(j-5); float yoffset = 1f*(k-5); float d = 500; float lastX = d * (box[0] + xoffset) / (box[2] + zoffset); float lastY = d * (box[1] + yoffset) / (box[2] + zoffset); Pen p = new Pen(new SolidBrush(ForeColor)); int i = 3; while (i < box.Length) { float x = d * (box[i + 0] + xoffset) / (box[i + 2] + zoffset); float y = d * (box[i + 1] + yoffset) / (box[i + 2] + zoffset); e.Graphics.DrawLine(p, translate(new PointF(lastX, lastY)), translate(new PointF(x, y))); lastX = x; lastY = y; i += 3; } } } } private PointF translate(PointF p) { PointF f = new PointF(p.X+this.Width/2, p.Y+this.Height/2); return f; } }
This is a very useful technique for drawing diagrams and simple objects, and can be a starting point for adding more features like rotation, or solids.