转 第十六章 3D 线条与填充(2) (as3.0)

创建 3D 填充
      大家也许都想到了,进行填充的准备工作大部分都已经完成了。我们已经创建了图形的
所有点,并从头到尾全部用线连接上了。现在所需的全部就是加入 beginFill 和 endFill,如
FilledE.as 文件中的代码。运行结果如图 16-7 所示。代码相应的变化部分用粗体表示:
graphics.clear();
graphics.lineStyle(0);
graphics.beginFill(0xffcccc);
graphics.moveTo(points[0].screenX, points[0].screenY);
for (i = 1; i < numPoints; i++) {
  graphics.lineTo(points[i].screenX, points[i].screenY);
}
graphics.lineTo(points[0].screenX, points[0].screenY);
graphics.endFill();
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

 

图 16-7 第一个 3D 填充
     前面例子中,正方形与字母 E 都是多边形。多边形是由至少三条线段组成的一个封闭
的图形。因此,三角形就是一个最简单的多边形。我们发现在许多 3D 建模或渲染的程序
中—— 甚至那些使用面片(patches),网格(meshes),非均匀 B 样曲线(NURBS)以及
复合多边形 —— 在渲染之前,所有的 3D 图形最终都简化为一个三角集合。
运用三角形
     运用三角形有许多好处——或许比我知道的还多,我们这里只做简单的介绍介绍。首先,
运用三角形我们能够确定多边形的所有顶点都在一个平面上,因为一个三角形定义一个平
面。如果大家还不确定为什么它很重要,那么我们就拿字母 E 的例子来说,随机地改变一
些点的 z 值。这时可能会得到一些有趣的结果,这些结果也很快会变成不可预想或不可预
知的。
    其次,使用三角形,在绘制复杂的形状时,可能会很简单。例如,

考虑一下图 16-8。
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-8 更加复杂的 3D 图形
     这样的形状要使用单一的多边形是很难创建的。我们至少要多做两倍的功。同样,还有
可能遇到这样的情况, 我们创建的每个多边形的顶点都有不同的数值并需要特别的处理。此
外,使用三角形可以创建如图 16-9 所示 A 这样的模型。
图 16-9 如图 16-8,用三角形重新渲染
     接下来,我们可以创建一个函数,给入三个点即可渲染出一个三角形。我们只需要一个
顶点的列表和一个三角形的列表。一层循环顶点列表,设置所有顶点的位置,并应用透视。
另一层循环遍历三角形列表并渲染每个三角形。
     这并不是说只能使用三角形创建的方法。我们可以也写一个动态渲染多边形的方法。但
是这里为了保证简单与灵活,让我们从三角形开始吧。先来试验字母 A 这个例子。
     首先,需要定义所需的顶点和三角形。如图 16-10 所示,并为每个三角形的每个顶点
编号。
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-10 组成该形状的顶点与多面形
     标出顶点后,得到如下这些值:
points[0] = new Point3D( -50, -250, 100);
points[1] = new Point3D( 50, -250, 100);
points[2] = new Point3D( 200, 250, 100);
points[3] = new Point3D( 100, 250, 100);
points[4] = new Point3D( 50, 100, 100);
points[5] = new Point3D( -50, 100, 100);
points[6] = new Point3D(-100, 250, 100);
points[7] = new Point3D(-200, 250, 100);
points[8] = new Point3D( 0, -150, 100);
points[9] = new Point3D( 50, 0, 100);
points[10] = new Point3D(-50, 0, 100);
     下面,需要定义三角形。每个三角形只不过就是三个点的列表;就叫它们 a, b, c 吧。
为了能够明确知道每个三角形的所有顶点,我们来创建一个三角形的类(Triangle class)。甚
至还可以为每个三角形写一个 draw 方法,让它自己就可以进行绘制。我把代码列出来,稍
后会看到如何使用它。
package {
  import flash.display.Graphics;
  public class Triangle {
    private var pointA:Point3D;
    private var pointB:Point3D;
    private var pointC:Point3D;
    private var color:uint;
    public function Triangle(a:Point3D, b:Point3D, c:Point3D, color:uint) {
      pointA = a;
      pointB = b;
      pointC = c;
      this.color = color;
    }
    public function draw(g:Graphics):void {
      g.beginFill(color);
      g.moveTo(pointA.screenX, pointA.screenY);
      g.lineTo(pointB.screenX, pointB.screenY);
      g.lineTo(pointC.screenX, pointC.screenY);
      g.lineTo(pointA.screenX, pointA.screenY);
      g.endFill();
    }
  }
}
现在我们需要另一个数组来保存三角形的列表。因此,请在类的顶部定义一个数组:
     private var triangles:Array;
然后,在定义了所有的顶点后,再在 init 函数中定义所有三角形。注意每个三角形都用指
定的颜色创建。
triangles = new Array();
triangles[0] = new Triangle(points[0], points[1], points[8], 0xffcccc);
triangles[1] = new Triangle(points[1], points[9], points[8], 0xffcccc);
triangles[2] = new Triangle(points[1], points[2], points[9], 0xffcccc);
triangles[3] = new Triangle(points[2], points[4], points[9], 0xffcccc);
triangles[4] = new Triangle(points[2], points[3], points[4], 0xffcccc);
triangles[5] = new Triangle(points[4], points[5], points[9], 0xffcccc);
triangles[6] = new Triangle(points[9], points[5], points[10],0xffcccc);
triangles[7] = new Triangle(points[5], points[6], points[7], 0xffcccc);
triangles[8] = new Triangle(points[5], points[7], points[10],0xffcccc);
triangles[9] = new Triangle(points[0], points[10], points[7], 0xffcccc);
triangles[10] = new Triangle(points[0], points[8], points[10],0xffcccc);
      大家也许注意到了,我将每个三角形的顶点坐标都以顺时针方向排列。这样做对我们当
前这个程序来说并不重要,但是在下一章就非常重要了,因此应该养成这样的好习惯。
      现在,我们来循环渲染这个图形(别担心,稍后我会给出全部代码),程序如下:
graphics.clear();
for (i = 0; i < triangles.length; i++) {
  triangles[i].draw(graphics);
}
      这里用到了 Triangle 类中定义的 draw 方法,传入一个 graphics 的引用。这样就完成
了!三角形根据定义的颜色开始填充,移动到第一个点的位置上,绘制一个图形出来,结束
填充。您也许会说,太精致了。
      这样来看,我对于有些朋友的面向对象程序设计让编程更复杂的说法要产生质疑了。
Triangle 类非常容易理解,主类部分也是再简单不过了。与本书前一版所使用的那个更加复
杂并且需要大量解释的线性解决方案相比,这是最好的答案。
      本例的运行结果如图 16-11 所示。
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-11 A 形
      以下是代码可见 Triangles.as,要确保新的类已经加入了:
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class Triangles extends Sprite {
   private var points:Array;
   private var triangles:Array;
   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;
   private var vpY:Number = stage.stageHeight / 2;
   public function Triangles() {
     init();
   }
   private function init():void {
     points = new Array();
     points[0] = new Point3D( -50, -250, 100);
     points[1] = new Point3D( 50, -250, 100);
     points[2] = new Point3D( 200, 250, 100);
     points[3] = new Point3D( 100, 250, 100);
     points[4] = new Point3D( 50, 100, 100);
     points[5] = new Point3D( -50, 100, 100);
     points[6] = new Point3D(-100, 250, 100);
     points[7] = new Point3D(-200, 250, 100);
     points[8] = new Point3D( 0, -150, 100);
     points[9] = new Point3D( 50, 0, 100);
  points[10] = new Point3D(-50, 0, 100);
  for (var i:uint = 0; i < points.length; i++) {
    points[i].setVanishingPoint(vpX, vpY);
    points[i].setCenter(0, 0, 200);
  }
  triangles = new Array();
  triangles[0] = new Triangle(points[0], points[1],
  points[8], 0xffcccc);
  triangles[1] = new Triangle(points[1], points[9],
  points[8], 0xffcccc);
  triangles[2] = new Triangle(points[1], points[2],
  points[9], 0xffcccc);
  triangles[3] = new Triangle(points[2], points[4],
  points[9], 0xffcccc);
  triangles[4] = new Triangle(points[2], points[3],
  points[4], 0xffcccc);
  triangles[5] = new Triangle(points[4], points[5],
  points[9], 0xffcccc);
  triangles[6] = new Triangle(points[9], points[5],
  points[10],0xffcccc);
  triangles[7] = new Triangle(points[5], points[6],
  points[7], 0xffcccc);
  triangles[8] = new Triangle(points[5], points[7],
  points[10],0xffcccc);
  triangles[9] = new Triangle(points[0], points[10],
  points[7], 0xffcccc);
  triangles[10] =new Triangle(points[0], points[8],
  points[10],0xffcccc);
  addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
    private function onEnterFrame(event:Event):void {
      var angleX:Number = (mouseY - vpY) * .001;
      var angleY:Number = (mouseX - vpX) * .001;
      for (var i:uint = 0; i < points.length; i++) {
        var point:Point3D = points[i];
        point.rotateX(angleX);
        point.rotateY(angleY);
      }
      graphics.clear();
      for (i = 0; i < triangles.length; i++) {
        triangles[i].draw(graphics);
      }
    }
  }
}
3D 立体建模

    最后,认真考虑一下本章开始所说的:在 Flash 中创建 3D 立体模型!
    在计算世界里,任何书籍或指南给出的第一个例子基本上都是用某种方法在屏幕上输出
“Hello,World”。在 3D 立体编程中,就相当于一个旋转立方体的程序。我们也不要打破这
个传统。


建立旋转立方体模型
    首先,我们需要八个点来定义立方体的八个角。如图 16-12 所示。
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)
图 16-12 3D 立方体的顶点
代码中点的定义如下:
// 前面四个角
points[0] = new Point3D(-100, -100, -100);
points[1] = new Point3D( 100, -100, -100);
points[2] = new Point3D( 100, 100, -100);
points[3] = new Point3D(-100, 100, -100);
// 后面四个角
points[4] = new Point3D(-100, -100, 100);
points[5] = new Point3D( 100, -100, 100);
points[6] = new Point3D( 100, 100, 100);
points[7] = new Point3D(-100, 100, 100);
     然后我们要定义三角形。    立方体的每个面都由两个三角形组成。总共要有 12 个三角形
—— 六个面,每面两个。同样,我会为每个三角形以逆时针的方向排列这些点。这里有个
小技巧,试将立方体按照您的意思旋转,让这些三角形面向我们,然后将这些点以观察点的
顺时针方向排列。
     例如,正面很好办;如图 16-13 所示的两个三角形。

转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-13 立方体的正面
     图 16-14 所示为顶面。图 16-15 所示为背面。
转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-14 立方体的顶面

转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-15 立方体的背面
       继续旋转每个面,我们得到三角形的定义如下:
// front
triangles[0] = new Triangle(points[0], points[1], points[2], 0x6666cc);
triangles[1] = new Triangle(points[0], points[2], points[3], 0x6666cc);
// top
triangles[2] = new Triangle(points[0], points[5], points[1], 0x66cc66);
triangles[3] = new Triangle(points[0], points[4], points[5], 0x66cc66);
//back
triangles[4] = new Triangle(points[4], points[6], points[5], 0xcc6666);
triangles[5] = new Triangle(points[4], points[7], points[6], 0xcc6666);
// bottom
triangles[6] = new Triangle(points[3], points[2], points[6], 0xcc66cc);
triangles[7] = new Triangle(points[3], points[6], points[7], 0xcc66cc);
// right
triangles[8] = new Triangle(points[1], points[5], points[6], 0x66cccc);
triangles[9] = new Triangle(points[1], points[6], points[2], 0x66cccc);
// left
triangles[10] =new Triangle(points[4], points[0], points[3], 0xcccc66);
triangles[11] =new Triangle(points[4], points[3], points[7], 0xcccc66);
        注意每个面都有不同的颜色,由于两个三角形构成一个面,所以它们是同一种颜色。
目前来看,是否使用顺时针方向无关紧要,但是在下一章,我们要在背面剔除中用到它。这
个术语是指决定哪个面面向我们的一种方法。大家马上会看到为什么如此重要的原因。
Cube.as 文件与 Triangles.as 非常像,只不过是在 init 函数中使用了这些新的点和三角形的
进行定义。
        继续,运行它。哇 —— 一片混乱!有时我们可以看到一些背面。有些面似乎永远也
看不可见。为什么会这样呢?立方体的背面总是被绘制出来。同样,那两个三角形也根据它
们在 triangles 数组中的位置进行绘制。因此在列表底部的面总是在列表顶部面之前被绘制
出来,您会感到奇怪,不可预知的效果就是这样形成的。我们需要调用 cull(剔除),或铲掉、
清除这些背面,因为它们不需要渲染。
       我们会在下一章详细地讲解背面剔除,同时还要学到如何根据角度在每个面上应用一些
基本的灯光。
       作为本章的一个临时修正,我们可以进入到 Triangle 类的 draw 方法,设置填充的透
明度为 0.5:
public function draw(g:Graphics):void {
  g.beginFill(color, .5);
  g.moveTo(pointA.screenX, pointA.screenY);
  g.lineTo(pointB.screenX, pointB.screenY);
  g.lineTo(pointC.screenX, pointC.screenY);
  g.lineTo(pointA.screenX, pointA.screenY);
  g.endFill();
}
    这样就任何面都可以看到了,它使整个立方体像是用有色玻璃制成的。在学习背面剔除
之前这只是个临时性的修正。完成后的 3D 立方体如图 16-16 所示。

转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)
图 16-16 3D 立方体运行结果
建立其它形状的模型
     恭喜各位!您已经掌握了旋转立方体。现在我们可以去建立所有种类的形状了。只要先
将它们绘制在方格纸上,标出点和三角形,再将放入数组即可。这张图可以帮助我们用几种
视角绘出物体,  旋转后可以看到每个面以及在三角形上标出的点。本节提供了一些其它的图
形作为起点。
金字塔形
     下面是 3D 金字塔形的代码(可以在 Pyramid.as 中找到) 

首先是点:
points[0] = new Point3D( 0, -200, 0);
points[1] = new Point3D( 200, 200, -200);
points[2] = new Point3D(-200, 200, -200);
points[3] = new Point3D(-200, 200, 200);
points[4] = new Point3D( 200, 200, 200);
     然后是三角形:
triangles[0] = new Triangle(points[0], points[1], points[2], 0x6666cc);
triangles[1] = new Triangle(points[0], points[2], points[3], 0x66cc66);
triangles[2] = new Triangle(points[0], points[3], points[4], 0xcc6666);
triangles[3] = new Triangle(points[0], points[4], points[1], 0x66cccc);
triangles[4] = new Triangle(points[1], points[3], points[2], 0xcc66cc);
triangles[5] = new Triangle(points[1], points[4], points[3], 0xcc66cc);
运行结果如图 16-17 所示。

转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-17 一个 3D 金字塔
挤出的字母 A
     在 ExtrudedA.as 中,我用尽全力将前面字母 A 的例子进行了挤压。这就意味着复制
前面 11 个点,将一个字母的 z 设置为 -50,另一个设置为 +50,然后为第二个设置三角
形(确认它们也是顺时针排列的) ,最后用三角形连接两边。很乏味?当然!但是完成后的
效果非常好:
points[0] = new Point3D( -50, -250, -50);
points[1] = new Point3D( 50, -250, -50);
points[2] = new Point3D( 200, 250, -50);
points[3] = new Point3D( 100, 250, -50);
points[4] = new Point3D( 50, 100, -50);
points[5] = new Point3D( -50, 100, -50);
points[6] = new Point3D(-100, 250, -50);
points[7] = new Point3D(-200, 250, -50);
points[8] = new Point3D( 0, -150, -50);
points[9] = new Point3D( 50, 0, -50);
points[10] = new Point3D( -50, 0, -50);
points[11] = new Point3D( -50, -250, 50);
points[12] = new Point3D( 50, -250, 50);
points[13] = new Point3D( 200, 250, 50);
points[14] = new Point3D( 100, 250, 50);
points[15] = new Point3D( 50, 100, 50);
points[16] = new Point3D( -50, 100, 50);
points[17] = new Point3D(-100, 250, 50);
points[18] = new Point3D(-200, 250, 50);
points[19] = new Point3D( 0, -150, 50);
points[20] = new Point3D( 50, 0, 50);
points[21] = new Point3D( -50, 0, 50);
triangles[0] =new Triangle(points[0], points[1], points[8], 0x6666cc);
triangles[1] =new Triangle(points[1], points[9], points[8], 0x6666cc);
triangles[2] =new Triangle(points[1], points[2], points[9], 0x6666cc);
triangles[3] =new Triangle(points[2], points[4], points[9], 0x6666cc);
triangles[4] =new Triangle(points[2], points[3], points[4], 0x6666cc);
triangles[5] =new Triangle(points[4], points[5], points[9], 0x6666cc);
triangles[6] =new Triangle(points[9], points[5], points[10], 0x6666cc);
triangles[7] =new Triangle(points[5], points[6], points[7], 0x6666cc);
triangles[8] =new Triangle(points[5], points[7], points[10], 0x6666cc);
triangles[9] =new Triangle(points[0], points[10], points[7], 0x6666cc);
triangles[10] = new Triangle(points[0], points[8], points[10], 0x6666cc);
triangles[11] = new Triangle(points[11], points[19], points[12], 0xcc6666);
triangles[12] = new Triangle(points[12], points[19], points[20], 0xcc6666);
triangles[13] = new Triangle(points[12], points[20], points[13], 0xcc6666);
triangles[14] = new Triangle(points[13], points[20], points[15], 0xcc6666);
triangles[15] = new Triangle(points[13], points[15], points[14], 0xcc6666);
triangles[16] = new Triangle(points[15], points[20], points[16], 0xcc6666);
triangles[17] = new Triangle(points[20], points[21], points[16], 0xcc6666);
triangles[18] = new Triangle(points[16], points[18], points[17], 0xcc6666);
triangles[19] = new Triangle(points[16], points[21], points[18], 0xcc6666);
triangles[20] = new Triangle(points[11], points[18], points[21], 0xcc6666);
triangles[21] = new Triangle(points[11], points[21], points[19], 0xcc6666);
triangles[22] = new Triangle(points[0], points[11], points[1], 0xcccc66);
triangles[23] = new Triangle(points[11], points[12], points[1], 0xcccc66);
triangles[24] = new Triangle(points[1], points[12], points[2], 0xcccc66);
triangles[25] = new Triangle(points[12], points[13], points[2], 0xcccc66);
triangles[26] = new Triangle(points[3], points[2], points[14], 0xcccc66);
triangles[27] = new Triangle(points[2], points[13], points[14], 0xcccc66);
triangles[28] = new Triangle(points[4], points[3], points[15], 0xcccc66);
triangles[29] = new Triangle(points[3], points[14], points[15], 0xcccc66);
triangles[30] = new Triangle(points[5], points[4], points[16], 0xcccc66);
triangles[31] = new Triangle(points[4], points[15], points[16], 0xcccc66);
triangles[32] = new Triangle(points[6], points[5], points[17], 0xcccc66);
triangles[33] = new Triangle(points[5], points[16], points[17], 0xcccc66);
triangles[34] = new Triangle(points[7], points[6], points[18], 0xcccc66);
triangles[35] = new Triangle(points[6], points[17], points[18], 0xcccc66);
triangles[36] = new Triangle(points[0], points[7], points[11], 0xcccc66);
triangles[37] = new Triangle(points[7], points[18], points[11], 0xcccc66);
triangles[38] = new Triangle(points[8], points[9], points[19], 0xcccc66);
triangles[39] = new Triangle(points[9], points[20], points[19], 0xcccc66);
triangles[40] = new Triangle(points[9], points[10], points[20], 0xcccc66);
triangles[41] = new Triangle(points[10], points[21], points[20], 0xcccc66);
triangles[42] = new Triangle(points[10], points[8], points[21], 0xcccc66);
triangles[43] = new Triangle(points[8], points[19], points[21], 0xcccc66);
运行结果如图 16-8 所示。

转 <wbr>第十六章 <wbr>3D <wbr>线条与填充(2) <wbr>(as3.0)

图 16-8 一个被挤出的字母 A
     如同我们看到的,这些东西建立得很快。最初,平面 A 有 11 个三角形。挤出后是原
来的四倍!代码仍然运行得非常稳定,但是我们已进入了 Flash 的宏大的 3D 世界。相同
的程序在 AS 2 中运行得也非常好。使用 AS 3 效率会变得更高,目前我不认为已经到达
了它速度的极限。随着 Flay Player 性能的改进,未来会怎样,谁知道呢?

移动三维立体模型
      我们已经有了 Point3D 类,要移动一个 3D 立体模型非常简单。 只需要用 setCenter 方
法改变中心位置。我们已经在 z 轴上做过图形的移动。这次只需要在其它轴上做同样的事
情即可。但是,请快速看一下,当改变这些值的时候,执行的代码是什么样的。它们都在
Point3D 类的 screenX 和 screenY 方法中:
public function get screenX():Number {
  var scale:Number = fl / (fl + z + cZ);
  return vpX + cX + x * scale;
}
public function get screenY():Number {
  var scale:Number = fl / (fl + z + cZ);
  return vpY + cY + y * scale;
}
      z 属性的中心 cZ,被加到 z 坐标上,当 scale 计算好的情况下,它将该点向外推出一
段距离,并不真正地改变 z 本身的位置。
      同样的事情也发生在 cX 和 cY 上。它们在使用 scale 之前被加到了 x 和 y 坐标上。
将一个方向上的点向外推,同样,不会永久改变它们。由于我们从没改变过这些值,这个点
—— 因此会使形状变大 —— 将继续绕着自身的中心旋转,而不是绕着整个世界的中心旋
转。
我们来看一下代码。回到 Cube.as 类,在顶部加入两个类属性:
private var offsetX:Number = 0;
private var offsetY:Number = 0;
并在 init 方法中,加入这样一行:
       stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
然后创建处理方法:
private function onKeyDown(event:KeyboardEvent):void {
  if (event.keyCode == Keyboard.LEFT) {
    offsetX = -5;
  } else if (event.keyCode == Keyboard.RIGHT) {
    offsetX = 5;
  } else if (event.keyCode == Keyboard.UP) {
    offsetY = -5;
  } else if (event.keyCode == Keyboard.DOWN) {
    offsetY = 5;
  }
  for (var i:Number = 0; i < points.length; i++) {
    points[i].x += offsetX;
    points[i].y += offsetY;
  }
}
不要忘记导入 KeyboardEvent 和 Keyboard 类。
       这些不过是循环每个点并在它们的值上加上或减去 5。由于所有点的实际坐标都在改
变,模型就像绕着 3D 空间的中心在旋转。这也许是或不是您想要的。如果我们只想移动
整个模型,并且还想让它绕着自身的中心点旋转,这就是 setCenter 方法出场的时候了。将
onKeyDown 中的代码做如下改变:
private function onKeyDown(event:KeyboardEvent):void {
  if (event.keyCode == Keyboard.LEFT) {
    offsetX -= 5;
  } else if (event.keyCode == Keyboard.RIGHT) {
    offsetX += 5;
  } else if (event.keyCode == Keyboard.UP) {
    offsetY -= 5;
  } else if (event.keyCode == Keyboard.DOWN) {
    offsetY += 5;
  }
  for (var i:Number = 0; i < points.length; i++) {
    points[i].setCenter(offsetX, offsetY, 200);
  }
}
       现在立方体运动起来就像一个整体,继续绕着它的中心点旋转,而不是绕着整个世界的
中心旋转。

本章总结:

本章虽然没有新出现的公式,绘制的技巧还是需要多熟悉的。虽然代码很多,但是都相对前面更容易理解

主要重点放在了图形显示上面,如果对3D构图感兴趣可以下去多构思下更复杂的图形比如圆柱,简单的房子外形等等~~

(如果要转载请注明出处http://blog.sina.com.cn/jooi,谢谢)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值