面来看看drawtriangles函数的扩展。利用drawtriangles函数来实现一个旋转的3D地球,效果如下


因为lufylegend1.5.0版的drawtriangles函数有个bug,所以我悄悄的更新了lufylegend1.5.1版,大家可以到官网下载,地址如下

​http://lufylegend.com/lufylegend​

其实绘制3d球体效果的话,首先就是绘制一个平面,然后将这个平面分成一个一个的小三角形,然后再用这些小三角形拼凑成一个圆球就可以了

现在,我先创建一个空白的LBitmapData对象,然后将这个对象分割成N个小三角形,具体做法看下面代码

[html]​view plain​​​​copy​​​​print​​​​?​


  1. earthBitmapData = new LBitmapData("#ffffff", 0, 0, 500, 300);
  2. var i, j;
  3. vertices = new Array();
  4. for(i=0;i<=cols;i++){
  5. for(j=0;j<=rows;j++){
  6. vertices.push(i*15,j*15);
  7. }
  8. }
  9. indices = new Array();
  10. for (i = 0; i < cols; i++) {
  11. for (j = 0; j < rows; j++) {
  12. indices.push(i * (rows + 1) + j, (i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1);
  13. indices.push((i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1, (i + 1) * (rows + 1) + j + 1);
  14. }
  15. }
  16. uvtData = new Array();
  17. for (i = 0; i <= cols; i++) {
  18. for (j = 0; j <= rows; j++) {
  19. uvtData.push(i / cols, j / rows);
  20. }
  21. }


earthBitmapData = new LBitmapData("#ffffff", 0, 0, 500, 300); var i, j; vertices = new Array(); for(i=0;i<=cols;i++){  for(j=0;j<=rows;j++){   vertices.push(i*15,j*15);  } } indices = new Array(); for (i = 0; i < cols; i++) {  for (j = 0; j < rows; j++) {   indices.push(i * (rows + 1) + j, (i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1);   indices.push((i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1, (i + 1) * (rows + 1) + j + 1);  } } uvtData = new Array(); for (i = 0; i <= cols; i++) {  for (j = 0; j <= rows; j++) {   uvtData.push(i / cols, j / rows);  } }

接着,利用drawtriangles函数将LBitmapData对象绘制到画面上

[html]​view plain​​​​copy​​​​print​​​​?​


  1. backLayer = new LSprite();
  2. addChild(backLayer);
  3. backLayer.graphics.clear();
  4. backLayer.graphics.beginBitmapFill(earthBitmapData);
  5. backLayer.graphics.drawTriangles(vertices, indices, uvtData, 2);


backLayer = new LSprite(); addChild(backLayer); backLayer.graphics.clear(); backLayer.graphics.beginBitmapFill(earthBitmapData); backLayer.graphics.drawTriangles(vertices, indices, uvtData, 2);

得到效果如下图。


要想将这个平面编程一个圆,就需要计算图中每个小三角形的坐标,先来看看y坐标应该如何计算,看下面一张图,是一个球的垂直切面


利用三角函数,计算图中的y坐标,和y坐标所在位置的球的水平切面圆的半径r1

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. var a = Math.sin(angle);
  2. if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  3. var y = -r*a;
  4. var sa = Math.cos(angle);
  5. var r1 = Math.abs(r*sa);


var a = Math.sin(angle); if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1; var y =  -r*a; var sa = Math.cos(angle); var r1 =  Math.abs(r*sa);

于是,首先将计算好的y坐标带入到vertices数组中

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. for(i=0;i<=cols;i++){
  2. for(j=0;j<=rows;j++){
  3. var angle = (90-180*j/rows)*Math.PI/180;
  4. var a = Math.sin(angle);
  5. if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  6. if((90-180*j/rows)%180==0)a=0;
  7. var sy = -r*a;
  8. vertices.push(i*15,sy);
  9. }
  10. }


for(i=0;i<=cols;i++){  for(j=0;j<=rows;j++){   var angle = (90-180*j/rows)*Math.PI/180;   var a = Math.sin(angle);   if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;   if((90-180*j/rows)%180==0)a=0;   var sy =  -r*a;   vertices.push(i*15,sy);  } }

因为还没有计算x的坐标,所以得到一个特殊图形,如下


接着,看看x的坐标如何计算,首先将半径为r1的平面切面拿出来,如下图


利用三角函数,计算图中的x坐标

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. var b = Math.cos(angle*Math.PI/180);
  2. var x = r1*b;


var b = Math.cos(angle*Math.PI/180); var x =  r1*b;

这时,如果只将计算好的x坐标带入到vertices数组中的话

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. for(i=0;i<=cols;i++){
  2. for(j=0;j<=rows;j++){
  3. var sa = Math.cos(angle);
  4. if((90-180*j/rows)%180==0)sa=1;
  5. var sr = Math.abs(r*sa);
  6. var angle2 = 360*(i+1)/cols;
  7. var b = Math.cos(angle2*Math.PI/180);
  8. if(angle2%360==0)b=1;
  9. else if(angle2%180==0)b=-1;
  10. var sx = sr*b;
  11. vertices.push(sx,j*15);
  12. }
  13. }


for(i=0;i<=cols;i++){  for(j=0;j<=rows;j++){   var sa = Math.cos(angle);   if((90-180*j/rows)%180==0)sa=1;   var sr =  Math.abs(r*sa);   var angle2 = 360*(i+1)/cols;   var b = Math.cos(angle2*Math.PI/180);   if(angle2%360==0)b=1;   else if(angle2%180==0)b=-1;   var sx =  sr*b;   vertices.push(sx,j*15);  } }

因为没有计算y的坐标,所以得到一个很有意思的图形,如下


如果将计算好的x坐标和y坐标,同时带入到vertices数组中的话

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. for(i=0;i<=cols;i++){
  2. for(j=0;j<=rows;j++){
  3. var angle = (90-180*j/rows)*Math.PI/180;
  4. var a = Math.sin(angle);
  5. if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;
  6. if((90-180*j/rows)%180==0)a=0;
  7. var sy = -r*a;
  8. var sa = Math.cos(angle);
  9. if((90-180*j/rows)%180==0)sa=1;
  10. var sr = Math.abs(r*sa);
  11. var angle2 = 360*(i+1)/cols;
  12. var b = Math.cos(angle2*Math.PI/180);
  13. if(angle2%360==0)b=1;
  14. else if(angle2%180==0)b=-1;
  15. var sx = sr*b;
  16. vertices.push(sx, sy);
  17. }
  18. }


for(i=0;i<=cols;i++){  for(j=0;j<=rows;j++){   var angle = (90-180*j/rows)*Math.PI/180;   var a = Math.sin(angle);   if((90-180*j/rows)%90==0 && (90-180*j/rows)%180!=0)a=(90-180*j/rows)>0?1:-1;   if((90-180*j/rows)%180==0)a=0;   var sy =  -r*a;   var sa = Math.cos(angle);   if((90-180*j/rows)%180==0)sa=1;   var sr =  Math.abs(r*sa);   var angle2 = 360*(i+1)/cols;   var b = Math.cos(angle2*Math.PI/180);   if(angle2%360==0)b=1;   else if(angle2%180==0)b=-1;   var sx =  sr*b;   vertices.push(sx, sy);  } }

得到一个完整的球体图形,如下


接下来就简单了,将空白图片换成地球的平面图,代码如下

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. earthBitmapData = new LBitmapData(imglist["earth"]);


earthBitmapData = new LBitmapData(imglist["earth"]);

再次运行代码,就可以得到下面的3D图形了


下面,该让这个地球转动起来了,根据上一篇介绍的内容,传入drawtriangles函数的uvtData数组中的元素是每个小三角形在原图片中的相对位置,它们决定了绘制图片的开始位置,如果将一组位置比如0123,变换其中的位置成为1230,再继续变换成2301,这样不断的进行位置变换,那么从视觉上,其实就已经实现了旋转了,那么在代码中,只需要将分割完的数组的按照每一列进行移动,每次都将第一列的两组三角形移到最后一列,这样第二列的两组三角形就变为了第一列,这样不停的变换就能让一个地球转动起来

[javascript]​view plain​​​​copy​​​​print​​​​?​


  1. for (i = 0; i <= rows; i++) {
  2. uvtData.push(uvtData.shift());
  3. uvtData.push(uvtData.shift());
  4. }


for (i = 0; i <= rows; i++) {  uvtData.push(uvtData.shift());  uvtData.push(uvtData.shift()); }

如果要改变这个地球的大小的话,就更简单了,改变LSprite对象的scaleX和scaleY属性就可以改变它的大小了


<!DOCTYPE html> <html lang="en">     <head>         <meta charset="utf-8" />         <title>3D</title>         <script type="text/javascript" src="../lufylegend-1.7.6.min.js">         </script>         <script type="text/javascript">             LSystem.screen(0.5);         </script>     </head>     <body>         <div id="legend">         </div>         <script>              //初始化引擎             init(50, "legend", 600, 500, main);              var leftspeed = 0;             var leftindex = 1;              var upspeed = 10;             var upindex = 1;             //定义每个顶点的坐标               var vertices;             //定义三角形,数组vertices中每三个顶点可以组成一个三角形             var indices;             //定义上面的每个顶点相对于整张图片的比例             var uvtData;              var backLayer;             var bitmapData, earthBitmapData;             //分割行数和列数             var rows = 24, cols = 24;             var mi = 0;             var imgData = [{                 name: "earth",                 path: "earth.jpg"             }];             var imglist;             function main() {                 //加载图片                 LLoadManage.load(imgData, null, gameInit);                  //var angle = (90 - 180 * 1 / 24) * Math.PI / 180;                 //var a = Math.sin(angle);                 //alert(a);             }                          function gameInit(result){                 imglist = result;                 //获取图片数据                 earthBitmapData = new LBitmapData(imglist["earth"]);                 //earthBitmapData = new LBitmapData("#ffffff", 0, 0, 500, 300);                                  var i, j;                 //地球半径                 var r = 100;                  //计算定点坐标                 vertices = new Array();                 for (i = 0; i <= cols; i++) {                     for (j = 0; j <= rows; j++) {                         //计算弧度                         var angle = (90 - 180 * j / rows) * Math.PI / 180;                          //计算定点Y坐标                         var a = Math.sin(angle);                         //if (j = 0) { alert(a); }                         if ((90 - 180 * j / rows) % 90 == 0 && (90 - 180 * j / rows) % 180 != 0)                              a = (90 - 180 * j / rows) > 0 ? 1 : -1;                         if ((90 - 180 * j / rows) % 180 == 0)                              a = 0;                         var sy = -r * a;                          //计算顶点X坐标                         var sa = Math.cos(angle);                         if ((90 - 180 * j / rows) % 180 == 0) sa = 1;                         var sr = Math.abs(r * sa);                         var angle2 = 360 * (i + 1) / cols;                         var b = Math.cos(angle2 * Math.PI / 180);                         if (angle2 % 360 == 0)                              b = 1;                         else if (angle2 % 180 == 0)                              b = -1;                         var sx = sr * b;                         vertices.push(sx, sy);                     }                 }                  //计算定点三角形位置                 indices = new Array();                 for (i = 0; i < cols; i++) {                     for (j = 0; j < rows; j++) {                         indices.push(i * (rows + 1) + j, (i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1);                         indices.push((i + 1) * (rows + 1) + j, i * (rows + 1) + j + 1, (i + 1) * (rows + 1) + j + 1);                     }                 }                  //定义定点三角形比例                 uvtData = new Array();                 for (i = 0; i <= cols; i++) {                     for (j = 0; j <= rows; j++) {                         uvtData.push(i / cols, j / rows);                     }                 }                  //创建一个Layer层                 backLayer = new LSprite();                 backLayer.x = 110;                 backLayer.y = 110;                 //添加对象                 addChild(backLayer);                 //使用Sprite的graphic绘图                 backLayer.graphics.clear();                 //填充背景                 backLayer.graphics.beginBitmapFill(earthBitmapData);                 //LGraphics类添加drawTriangles方法,用于呈现一组三角形                 backLayer.graphics.drawTriangles(vertices, indices, uvtData);                                  //进入图之后,按帧速率调用onframe函数                   backLayer.addEventListener(LEvent.ENTER_FRAME, onframe);             }                          function onframe() {                  //变换绘制图片的开始位置,造成旋转动画                 if (leftspeed < 10 && leftindex++ > leftspeed) {                     leftindex = 0;                     for (i = 0; i <= rows; i++) {                         uvtData.push(uvtData.shift());                         uvtData.push(uvtData.shift());                     }                 }                 if (upspeed < 10 && upindex++ > upspeed) {                     upindex = 0;                     for (var i = 1; i < uvtData.length; i += 2) {                         uvtData[i] += 1 / rows;                         if (uvtData[i] > 1)                              uvtData[i] -= 1;                     }                 }             }                          //调整旋转速度             function runspeed(value){                 leftspeed = 10 - value;             }              //改变地球大小             function runmax(value){                 backLayer.scaleX = value / 50;                 backLayer.scaleY = backLayer.scaleX;             }         </script>         <div id="mylegend1" style="position:absolute;top:400px;width:400px;z-index:1;color: #ffffff;background-color:#000000;">             旋转速度:<input type="number" max="10" min="0" value="10" onchange="runspeed(this.value);"/>             <br/>             伸缩:<input type="number" max="100" min="0" value="50" onchange="runmax(this.value);"/>%         </div>     </body> </html>