谈谈Processing 3D世界 二

来源:互联网 发布:自学linux运维要多久 编辑:程序博客网 时间:2024/04/28 22:10
有了第一节的知识,我们其实已经可以做很多事情了。比如,


绘制一个立方体(cube)。



不过绘制立方体相对复杂。我们先从绘制一个正方形起步吧。



仍然使用beginShape(),与endShape()这对基友来绘制我们的多边形:

// 绘制图形beginShpae();    // 开始绘制vertex();        // 这里填入顶点位置...endShpae(CLOSE); // 结束绘制

step 1 定义顶点


如何定义正方形的4个顶点?
经过第一节知识洗礼,我们知道第一步要把正方形画在坐标系原点附近,最好图形的中心点能与原点重合
这样无论图形在之后要怎么倒腾,我们只需对这个坐标系进行矩阵变换(平移、旋转、缩放)就可以应付了。
事实上,我们把这些操作称为对模型矩阵的变换操作。



上图标示了正方形4个顶点位置。我们将申请4个向量来填装。

// 设置顶点PVector[] ver = new PVector[4];ver[0] = new PVector(-0.5, -0.5);ver[1] = new PVector( 0.5, -0.5);ver[2] = new PVector( 0.5,  0.5);ver[3] = new PVector(-0.5,  0.5);

你可能注意到了,这一次我们并没有使用具体的数值来描述顶点的位置。相比官网提供的例子,如:

vertex(30, 20);vertex(85, 20);vertex(85, 75);vertex(30, 75);

将所有的数值都限定在-1.0~ 1.0 这个区间内。这称之为标准化。这使我们只专注关心图形本生的形
状的变化,并方便我们后续对模型矩阵的变换操作。


step 2 变换矩阵

我们希望这个正方形显示在屏幕正中央,因此使用平移矩阵,将画面移到屏幕中央:

translate(width/2,height/2);

然而,这时候我们任然看不见图形,因为我们需要给图形指定大小:

scale(100);

我们将图形放大100倍,正方形的边长由1.0放大成了100.0。


step 3 绘制图形

// 绘制图形beginShape();for(int i = 0; i < ver.length; i++) {  vertex(ver[i].x, ver[i].y);}endShape(CLOSE);

使用遍历顶点的结构,我们可以过程化多边形的绘制工作。


这里是完整的代码:
你可以尝试运行第25行代码。

// 定义顶点PVector[] ver;void setup() {  size(200, 200, P3D);  // 设置顶点  ver = new PVector[4];  ver[0] = new PVector(-0.5, -0.5);  ver[1] = new PVector( 0.5, -0.5);  ver[2] = new PVector( 0.5,  0.5);  ver[3] = new PVector(-0.5,  0.5);  // 设置图形模式  noStroke();  noLoop();}void draw() {  // 模型矩阵变换  // 在opengl中,我们建议您在组合矩阵时,先进行缩放操作,  // 然后是旋转,最后才是平移,否则它们会(消极地)互相影响。  // 在Processing中,恰恰相反。  translate(width/2, height/2);   //rotateZ(radians(45));  scale(100);  // 绘制图形  fill(255, 127, 39);  beginShape();  for(int i = 0; i < ver.length; i++) {    vertex(ver[i].x, ver[i].y);  }  endShape(CLOSE);}

--------------------------这里是华丽的分割线 --------------------------------


现在我们回过头来想象立方体的顶点分布



step 1 定义顶点

这一次,我们的顶点将加上z轴坐标。
  
// 设置顶点ver = new PVector[8];ver[0] = new PVector(-0.5, -0.5,  0.5); // 顶点1ver[1] = new PVector(-0.5, -0.5, -0.5); // 顶点2ver[2] = new PVector( 0.5, -0.5, -0.5); // ...ver[3] = new PVector( 0.5, -0.5,  0.5);ver[4] = new PVector(-0.5,  0.5,  0.5);ver[5] = new PVector(-0.5,  0.5, -0.5);ver[6] = new PVector( 0.5,  0.5, -0.5);ver[7] = new PVector( 0.5,  0.5,  0.5);

step 2 变换矩阵

没有什么变化。


step 3 绘制图形

然而我们还能像刚才那样遍历顶点吗?

.
.
.

真正开始时,我们似乎有一点不知从何下手,对吧?事实上这里会遇到两个问题:

1.描述绘制多边形的方法

关于这一点我们可以查看beginShape()的官方说明。我们可以看到说明里列举了很多绘制的方法,一
般我们选:

TRIANGLES           - 绘制独立的三角面
或 TRIANGLE_FAN     - 绘制连续的三角面


因为我们的显卡在处理三角面时效率会快的多。同时,使用三角面,我们也无需考虑线条穿插问题。我
倾向选择前者。处理起来直截了当。我们的立方体有6个面,每个面可以由2个三角形构成,那么一共就
是12个三角面。

2.描述顶点的方法

当然,我们可以老老实实把12个三角面的顶点都描述一遍。不过这样的操作似乎是机器的爱好。。。想
必我们会很厌烦。仔细想想,尽管有12个三角面,但它们的顶点仍然是由上文中ver 那8个顶点构成的。
因此我们可以称ver为[顶点列表],然后添加一个[顶点索引列表],用来描述每一个三角面使用了那些
顶点:

face= new PVector[12];face[0] = new PVector(0, 1, 2); // 三角面1face[1] = new PVector(0, 2, 3); // 三角面2...

3.更新绘制图形方法

既然我们添加了顶点索引列表,那么原来的绘制图形的过程就有必要更新一下了:

// 绘制图形fill(255, 127, 39);for (int i = 0; i < face.length; i++) { // 遍历三角面  // 绘制三角面  beginShape();  vertex(ver[int(face[i].x)].x, ver[int(face[i].x)].y, ver[int(face[i].x)].z);  vertex(ver[int(face[i].y)].x, ver[int(face[i].y)].y, ver[int(face[i].y)].z);  vertex(ver[int(face[i].z)].x, ver[int(face[i].z)].y, ver[int(face[i].z)].z);  endShape(TRIANGLES);}

现在,我们来更新一下立方体的绘制过程:

// 定义顶点PVector[] ver;PVector[] face;void setup() {  size(200, 200, P3D);  // 设置顶点  ver = new PVector[8];  ver[0] = new PVector(-0.5, -0.5,  0.5); // 顶点1  ver[1] = new PVector(-0.5, -0.5, -0.5); // 顶点2  ver[2] = new PVector( 0.5, -0.5, -0.5); // ...  ver[3] = new PVector( 0.5, -0.5,  0.5);  ver[4] = new PVector(-0.5,  0.5,  0.5);  ver[5] = new PVector(-0.5,  0.5, -0.5);  ver[6] = new PVector( 0.5,  0.5, -0.5);  ver[7] = new PVector( 0.5,  0.5,  0.5);  // 设置顶点索引  face = new PVector[12];  // top  face[0]  = new PVector(0, 1, 2);  face[1]  = new PVector(0, 2, 3);  // front  face[2]  = new PVector(0, 3, 7);  face[3]  = new PVector(0, 7, 4);  // back  face[4]  = new PVector(1, 2, 6);  face[5]  = new PVector(1, 6, 5);  // right  face[6]  = new PVector(3, 2, 7);  face[7]  = new PVector(2, 6, 7);  // left  face[8]  = new PVector(0, 4, 5);  face[9]  = new PVector(1, 5, 0);  // bottom  face[10] = new PVector(4, 5, 7);  face[11] = new PVector(5, 6, 7);  // 设置图形模式  noStroke();}void draw() {  // 清楚缓冲区  background(0);  // 模型矩阵变换  // 在opengl中,我们建议您在组合矩阵时,先进行缩放操作,  // 然后是旋转,最后才是平移,否则它们会(消极地)互相影响。  // 在Processing中,恰恰相反。  translate(width/2, height/2, -100);  float angle = frameCount%360;  rotateX(radians(angle));  rotateY(radians(angle));  scale(100);  // 绘制图形  fill(255, 127, 39);  for (int i = 0; i < face.length; i++) {    beginShape();    vertex(ver[int(face[i].x)].x, ver[int(face[i].x)].y, ver[int(face[i].x)].z);    vertex(ver[int(face[i].y)].x, ver[int(face[i].y)].y, ver[int(face[i].y)].z);    vertex(ver[int(face[i].z)].x, ver[int(face[i].z)].y, ver[int(face[i].z)].z);    endShape(TRIANGLES);  }}




好啦,咱们的立方体终于顺利绘制到屏幕中了。
欣喜之余,你是不是会突然想到利用box()函数,不是
也可简单快速的绘制一个立方体吗?其实不然,通过上面的努力,你已经开启了通往3D世界大门。精彩
的世界就在眼前。
0 0