swift 软渲染立方体

要软渲染个立方体,首先要把立方体的点表示出来,然后转化到2D平面.
3D世界怎么转化到2D世界中呢?很简单,图形学的前辈已经给我们解决了。
首先是三个矩阵MVP,这个是什么意思呢?其实就是要把3D世界中每一个点的坐标转化到2D中,

1
v' = v*M*V*P

那么矩阵怎么表示的,数学问题这么复杂,没关系,我们有库,苹果的GLKit,已经把这些封装好了。
点就用GLKVector4表示
矩阵就是 GLKMatrix4

模型矩阵有单位矩阵GLKMatrix4Identity,
旋转有GLKMatrix4MakeRotation
放大有GLKMatrix4MakeScale
移动有GLKMatrix4MakeTranslation

视图矩阵有
GLKMatrix4MakeLookAt

透视矩阵有
GLKMatrix4MakePerspective

这些想要知道怎么实现的呢,没关系,微软实现了个WinObj工程

GLKMatrix4MakeLookAt

1
2
3
4
5
6
7
8
9
10
11
GLKIT_EXPORT GLKMatrix4
GLKMatrix4MakeLookAt(float eyeX, float eyeY, float eyeZ, float lookX, float lookY, float lookZ, float upX, float upY, float upZ) {
GLKVector3 eye = GLKVector3Make(eyeX, eyeY, eyeZ);
GLKVector3 initialUp = GLKVector3Make(upX, upY, upZ);
GLKVector3 fwd = GLKVector3Normalize(GLKVector3Subtract(GLKVector3Make(lookX, lookY, lookZ), eye));
GLKVector3 right = GLKVector3Normalize(GLKVector3CrossProduct(fwd, initialUp));
GLKVector3 up = GLKVector3CrossProduct(right, fwd);
GLKMatrix4 trans = GLKMatrix4MakeTranslation(-eyeX, -eyeY, -eyeZ);
return GLKMatrix4Multiply(GLKMatrix4MakeOrthonormalXform(right, up, GLKVector3Negate(fwd), GLKVector3Origin()), trans);
}

GLKMatrix4MakePerspective

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
GLKIT_EXPORT GLKMatrix4 GLKMatrix4MakePerspective(float yrad, float aspect, float near, float far) {
float yd = tanf(yrad / 2.f) * near;
float xd = tanf(aspect * yrad / 2.f) * near;
return GLKMatrix4MakeFrustum(-xd, xd, -yd, yd, near, far);
}
GLKIT_EXPORT GLKMatrix4 GLKMatrix4MakeFrustum(float left, float right, float bottom, float top, float near, float far) {
GLKMatrix4 res;
res.m00 = (2.f * near) / (right - left);
res.m10 = 0.f;
res.m20 = (right + left) / (right - left);
res.m30 = 0.f;
res.m01 = 0.f;
res.m11 = (2.f * near) / (top - bottom);
res.m21 = (top + bottom) / (top - bottom);
res.m31 = 0.f;
res.m02 = 0.f;
res.m12 = 0.f;
res.m22 = (far + near) / (near - far);
res.m32 = (2.f * far * near) / (near - far);
res.m03 = 0.f;
res.m13 = 0.f;
res.m23 = -1.f;
res.m33 = 0.f;
return res;
}

渲染

点表示出来就很好办了,画8条线就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
func drawCube(){
let p1 = testMatrix(GLKVector4Make(-10, 10, -10, 1))
let p2 = testMatrix(GLKVector4Make(-10, 10, 10, 1))
let p3 = testMatrix(GLKVector4Make(10, 10, 10, 1))
let p4 = testMatrix(GLKVector4Make(10, 10, -10, 1))
let p5 = testMatrix(GLKVector4Make(-10, -10, -10, 1))
let p6 = testMatrix(GLKVector4Make(-10, -10, 10, 1))
let p7 = testMatrix(GLKVector4Make(10, -10, 10, 1))
let p8 = testMatrix(GLKVector4Make(10, -10, -10, 1))
clear()
drawLine(p1, v2: p2)
drawLine(p2, v2: p3)
drawLine(p3, v2: p4)
drawLine(p4, v2: p1)
drawLine(p5, v2: p6)
drawLine(p6, v2: p7)
drawLine(p7, v2: p8)
drawLine(p8, v2: p5)
drawLine(p1, v2: p5)
drawLine(p2, v2: p6)
drawLine(p3, v2: p7)
drawLine(p4, v2: p8)
updateImage()
}
func drawLine(v1:GLKVector4, v2:GLKVector4) {
drawLine(v1.x.f, y1: v1.y.f, x2: v2.x.f, y2: v2.y.f)
}
var rotateY:Float = 0.0
func testMatrix(v:GLKVector4) -> GLKVector4{
let r:Float = 1.0
let eyeX:Float = 0
let eyeY:Float = 0
let eyeZ:Float = 20.0
let pm = GLKMatrix4MakePerspective(Float(M_PI)/180.0*80.0, r, 0, 100.0)
let vm = GLKMatrix4MakeLookAt(eyeX, eyeY, eyeZ, 0, 0, 0.0, 0, 1.0, 0)
let mm = GLKMatrix4MakeYRotation(rotateY)
let pv = GLKMatrix4Multiply(pm, vm)
let pvm = GLKMatrix4Multiply(pv, mm)
let v1 = GLKMatrix4MultiplyVector4(pvm, v)
let v2 = GLKVector4Make(v1.x/v1.w * 100, v1.y/v1.w * 100, v1.z/v1.w, v1.w)
print(v1.format(),v2.format())
return v2
}

如图

cube-3d.png

完整例子