<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      碼農(nóng)干貨系列【10】--光線追蹤進(jìn)階:javascript玩轉(zhuǎn)3D紋理映射

      2013-03-18 07:59  【當(dāng)耐特】  閱讀(7394)  評(píng)論(11)    收藏  舉報(bào)

      17205735-13eeab7f28c647548f5218bdc77d6050

      簡(jiǎn)介

      本文在光線追蹤的基礎(chǔ)之上,為了追求渲染速度和效率,去除了光線的反射、去除了透視投影(如我前面兩篇干貨8干貨9,所以渲染雖然是3D場(chǎng)景,其實(shí)不是真實(shí)看到的,但不影響實(shí)驗(yàn)),進(jìn)行了一些有趣的嘗試。此文將分享這兩天嘗試的成果:3D雕刻。

      3D雕刻,顧名思義--在3D物體上進(jìn)行雕刻,所以要達(dá)到的目的不僅僅是渲染幾種常見的幾何形狀,還包括在幾何形狀上繪制、繪畫等等。本文依舊使用大家熟悉的javascript語言,HTML5 canvas作為顯示屏。

      在讀本文之前,最好可以了解一些下面這些基礎(chǔ)知識(shí):

      正交投影

      線性代數(shù)基礎(chǔ)

      數(shù)據(jù)結(jié)構(gòu)和算法

      javascript基礎(chǔ)知識(shí)

      射線、AABB、面、球體之間碰撞檢測(cè)算法

      透視投影(本文雖然略去了canvas和影像屏的mapping,使用了固定視錐體去渲染,所以下面的的demo不建議去修改eye的坐標(biāo))

      Vector3的幾何意義(使用時(shí)候要區(qū)分什么時(shí)候代表點(diǎn),什么時(shí)候代表向量)

      Canvas像素操作getImageData/putImageData/跨域、漸變createLinearGradient、繪制文字fillText、圖片drawImage/base64等

      如果不了解上面相關(guān)的內(nèi)容,可以做一些search,或者通過本文做一些熟悉。

      Vector3類

      這個(gè)類是最常用的類了。最重要的一點(diǎn)就使用的時(shí)候理解它是代表點(diǎn)還是向量,以及各個(gè)方法的幾何意義。

      var Vector3 = function (x, y, z) { this.x = x; this.y = y; this.z = z; };
      Vector3.prototype = {
          dot: function (v) { return this.x * v.x + this.y * v.y + this.z * v.z; },
          sub: function (v) { return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z); },
          normalize: function () { return this.divideScalar(this.length()); },
          divideScalar: function (s) { return new Vector3(this.x / s, this.y / s, this.z / s); },
          length: function () { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); },
          sqrLength: function () { return this.x * this.x + this.y * this.y + this.z * this.z; },
          multiplyScalar: function (s) { return new Vector3(this.x * s, this.y * s, this.z * s); },
          add: function (v) { return new Vector3(this.x + v.x, this.y + v.y, this.z + v.z); },
          cross: function (v) { return new Vector3(-this.z * v.y + this.y * v.z, this.z * v.x - this.x * v.z, -this.y * v.x + this.x * v.y); },
          round: function () { return new Vector3(Math.round(this.x), Math.round(this.y), Math.round(this.z)) },
          distanceTo: function (v) {   return Math.sqrt(this.distanceToSquared(v)); },
          distanceToSquared: function (v) {   var dx = this.x - v.x;   var dy = this.y - v.y; var dz = this.z - v.z;
             return dx * dx + dy * dy + dz * dz;
          }
      }

      題外話:為什么Vector3?為什么不用齊次坐標(biāo)Vector4?(主要是因?yàn)闆]有透視投影的過程了。)

      var Vector4 = function ( x, y, z, w ) {
          this.x = x || 0;
          this.y = y || 0;
          this.z = z || 0;
          this.w = w || 1;
      }

      Vector4的最后一個(gè)參數(shù)w是干什么的?為什么不使用4*4矩陣?為什么是4*4?不是3*3?

      齊次坐標(biāo)表示是計(jì)算機(jī)圖形學(xué)的重要手段之一,它既能夠用來明確區(qū)分向量和點(diǎn),同時(shí)也更易用于進(jìn)行仿射(線性)幾何變換。”

                                                                                                                               —— F.S. Hill, JR

      有了w,可以進(jìn)行透視除法,隱藏面消除等算法。那怎么才能知道w?

      3*3的矩陣擴(kuò)大成4*3的矩陣==>增加的那個(gè)維度可以用來進(jìn)行w的計(jì)算

      線性代數(shù)的基礎(chǔ)是過原點(diǎn),向量是標(biāo)量的數(shù)組,矩陣是向量的數(shù)組,把4*3的矩陣擴(kuò)大成4*4的矩陣==>增加的那個(gè)維度可以用來表示平移

      所以:利用齊次坐標(biāo)技術(shù)來描述空間各點(diǎn)的坐標(biāo),用4*4的矩陣來解決空間各點(diǎn)的變換,已經(jīng)成了計(jì)算機(jī)圖形學(xué)的一個(gè)標(biāo)準(zhǔn)。

                                                                             -----摘自《HTML5實(shí)驗(yàn)室:Canvas世界》

      射線與正方體碰撞檢測(cè)

      為了簡(jiǎn)單起見,本文出現(xiàn)的cube都屬于AABB,不屬于OBB。為了渲染正方體,首先需要推導(dǎo)出射線與正方體是否相交和交點(diǎn)坐標(biāo)。

      rp1

      使用矩形的中心點(diǎn)和邊長(zhǎng)表示這個(gè)矩形:

      var Cube = function (center, length) {
          this.center = center;
          this.length = length;
          this.hLength = length / 2;
      
          this.minX = this.center.x - this.hLength;
          this.maxX = this.center.x + this.hLength;
          this.minY = this.center.y - this.hLength;
          this.maxY = this.center.y + this.hLength;
          this.minZ = this.center.z - this.hLength;
          this.maxZ = this.center.z + this.hLength;
      }

      使用射線的起點(diǎn)和方向表示射線:

      Ray3 = function (origin, direction) {
          this.origin = origin;
          this.direction = direction;
      }
      
      Ray3.prototype = {
          getPoint: function (t) {
              return this.origin.add(this.direction.multiplyScalar(t));
          }
      }

      給定射線和正方體之后,分6次求出射線也正方體六個(gè)面(無區(qū)域限制)的交點(diǎn),如果有交點(diǎn),再判斷該交點(diǎn)是否在正方體矩形面之內(nèi)。都滿足的話,判定為相交。如下面代碼所示:

      Cube.prototype.intersect = function (r3) {
          var d = r3.direction, p1 = r3.origin;
          var irs = [];
          var ir1 = this.getTIntersectPlane(p1, d, "z", this.center.z - this.hLength);
          var ir2 = this.getTIntersectPlane(p1, d, "z", this.center.z + this.hLength);
          var ir3 = this.getTIntersectPlane(p1, d, "x", this.center.x - this.hLength);
          var ir4 = this.getTIntersectPlane(p1, d, "x", this.center.x + this.hLength);
          var ir5 = this.getTIntersectPlane(p1, d, "y", this.center.y - this.hLength);
          var ir6 = this.getTIntersectPlane(p1, d, "y", this.center.y + this.hLength);
      
          if (ir1) irs.push(ir1);
          if (ir2) irs.push(ir2);
          if (ir3) irs.push(ir3);
          if (ir4) irs.push(ir4);
          if (ir5) irs.push(ir5);
          if (ir6) irs.push(ir6);
      
          if (irs.length === 1) {
              return irs[0].cp;
          }
          else if (irs.length === 2) {
              if (irs[0].t > irs[1].t) return irs[1].cp;
              if (irs[1].t > irs[0].t) return irs[0].cp;
          }
      
          return null;
      }
      
      Cube.prototype.getTIntersectPlane = function (p1,d,type,value) {
      
          var _intersectResult = [];
          var t, cp;
          if (type === "z")
          {     
              t= (value - p1.z) / d.z;
              cp = p1.add(d.multiplyScalar(t));       
              if (cp.x < this.maxX && cp.x > this.minX && cp.y < this.maxY && cp.y > this.minY) return { t: t, cp: cp };
          }
          if (type === "x") {
               t = (value - p1.x) / d.x;
               cp = p1.add(d.multiplyScalar(t));
               if (cp.z < this.maxZ && cp.z > this.minZ && cp.y < this.maxY && cp.y > this.minY) return { t: t, cp: cp };
          }
          if (type === "y") {
               t = (value - p1.y) / d.y;
               cp = p1.add(d.multiplyScalar(t));
               if (cp.x < this.maxX && cp.x > this.minX && cp.z < this.maxZ && cp.z > this.minZ) return { t: t, cp: cp };
          }
      
          return null;
      }

      可以想象,直線與正方體的交點(diǎn)只可能是兩個(gè)或者一個(gè)。所以當(dāng)交點(diǎn)為兩個(gè)的時(shí)候,最后返回離射線發(fā)射點(diǎn)近的相交點(diǎn),該點(diǎn)才是先與正方體相交的點(diǎn)。

       if (irs[0].t > irs[1].t) return irs[1].cp;
       if (irs[1].t > irs[0].t) return irs[0].cp;

      渲染測(cè)試(在上篇球的基礎(chǔ)上加入正方體):

      請(qǐng)使用現(xiàn)代瀏覽器,你的瀏覽器過時(shí)了!下載地址http://dl.pconline.com.cn/download/51614.html

      修改球的半徑、正方體的邊長(zhǎng)等參數(shù)==>

      繪制漸變文字

      要在正方體的外表面繪制文字,先嘗試在canvas中繪制漸變文字。canvas提供了createLinearGradient方法來設(shè)置fillStyle為漸變色,然后使用fillText來繪制文字。如下所示:

      var linearText = ctx.createLinearGradient(0, 0, 200, 200);
      linearText.addColorStop(0, "blue");
      linearText.addColorStop(0.5, "yellow");
      linearText.addColorStop(1, "red");
      ctx.fillStyle = linearText;
      var fontSize = fontSize || "200px";
      ctx.font = "bold 200px Arial";
      ctx.textBaseline = "top";
      ctx.fillText("當(dāng)", 0, 0);

      效果如下所示:



      修改顯示的文字和字體大小==>

      2D文字Mapping立方體表面

      因?yàn)橐谡襟w表面繪制文字,所以在把2D文字里面每個(gè)像素的坐標(biāo)和顏色保存起來,然后對(duì)應(yīng)到正方體的表面。所以專門創(chuàng)建了一個(gè)方法createWordData來生成文字像素坐標(biāo)和顏色信息:

      function createWordData(word) {
          var canvas = document.createElement("canvas");
          canvas.width = 90;
          canvas.height = 90;
          var ctx = canvas.getContext("2d");
      
          var linearText = ctx.createLinearGradient(0, 0, 90, 90);
          linearText.addColorStop(0, "blue");
          linearText.addColorStop(0.5, "yellow");
          linearText.addColorStop(1, "red");
          ctx.fillStyle = linearText;
          ctx.font = "bold 70px Arial";
          ctx.textBaseline = "top";
          ctx.fillText(word, 20, 15);
      
          var imgdata = ctx.getImageData(0, 0, canvas.width, canvas.height);
          var pixels = imgdata.data, tempN = 0;
          var wordData = [];
          for (var y = 0; y < canvas.height; y++) {
              for (var x = 0; x < canvas.width; x++) {
                  if (pixels[tempN + 3] !== 0) {
                      wordData.push({ position: { x: x, y: y }, color: [pixels[tempN], pixels[tempN + 1], pixels[tempN + 2], pixels[tempN + 3]] })
                  }
                  tempN += 4;
              }
          }
      
          return wordData;
      }

      這里使用提取方式是:遍歷每個(gè)像素的rgba中的a是否是0,如果不是0,則判定為在字的像素坐標(biāo)范圍。如果寫過canvas庫(kù)的經(jīng)歷的話,這種方式一定不陌生,一些點(diǎn)擊操作精確到像素級(jí)別的時(shí)候,用如下的方式去判定:

      p._testHit = function (ctx) {
          try {
              var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
          } catch (e) {
                  throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
          }
          return hit;
      }

      題外話:在寫canvas庫(kù)的時(shí)候發(fā)現(xiàn),谷歌瀏覽器精確到像素非常快,而IE9/10精確像素非常慢,包括win8上的webapp/webgame殼(微軟webapp/webgame套上該殼就是native的)也非常非常慢,每次點(diǎn)擊都掉幀,點(diǎn)得越快,掉得越快。因?yàn)榫_到像素涉及到了一些矩陣變換,所以IE該好好優(yōu)化優(yōu)化了,或者基于webkit二次開發(fā)吧,落后Chrome好多好多了。

      創(chuàng)建完三個(gè)文字?jǐn)?shù)據(jù)信息,把三個(gè)字mapping到正方體的三個(gè)面:正面、右側(cè)面和頂部表面。

      當(dāng)mapping正面時(shí):wordX===CubeX(相對(duì)于左上角,下面一樣)、wordY===CubeY(相對(duì)于左上角,下面一樣)

      當(dāng)mapping右側(cè)面時(shí):wordX===CubeZ、wordY===CubeY

      當(dāng)mapping頂部表面時(shí):wordX===CubeX、wordY===CubeZ

      所以有:

      var color = null;
      for (var k = 0, l = word1.length; k < l; k++) {
          var dD = word1[k];
          if (Math.round(result2.y) === cube.maxY && Math.round(result2.x - cube.minX) === dD.position.x && Math.round(result2.z - cube.minZ) === dD.position.y) {
              color = dD.color;
              break;
      
          }
      }
      if (!color) {
          for (var k = 0, l = word2.length; k < l; k++) {
              var dD = word2[k];
              if (Math.round(result2.z) === cube.maxZ && Math.round(result2.x - cube.minX) === dD.position.x && Math.round(cube.maxY - result2.y) === dD.position.y) {
                  color = dD.color;
                  break;
      
              }
          }
      }
      if (!color) {
          for (var k = 0, l = word3.length; k < l; k++) {
              var dD = word3[k];
              if (Math.round(result2.x) === cube.maxX && Math.round(cube.maxY - result2.y) === dD.position.y && Math.round(cube.maxZ - result2.z) === dD.position.x) {
                  color = dD.color;
                  break;
      
              }
          }
      }

      渲染測(cè)試效果如下:

      請(qǐng)使用現(xiàn)代瀏覽器,你的瀏覽器過時(shí)了!下載地址http://dl.pconline.com.cn/download/51614.html

      修改各個(gè)參數(shù)試試( 這個(gè)有點(diǎn)久,耐心等會(huì)兒,或者使用chrome瀏覽器)==>

      2D圖片Mapping立方體表面

      圖片的mapping也是同樣的道理。不同的地方的,由于getImageData會(huì)報(bào)跨域的安全問題,所以會(huì)受到限制。這里把圖片進(jìn)行base64編碼,然后繪制到canvas當(dāng)中去。

      效果如下所示:

      請(qǐng)使用現(xiàn)代瀏覽器,你的瀏覽器過時(shí)了!下載地址http://dl.pconline.com.cn/download/51614.html

      通過 這個(gè)網(wǎng)站 把64*64的圖標(biāo)轉(zhuǎn)成base64然后粘貼進(jìn)來 (這個(gè)有點(diǎn)久,耐心等會(huì)兒,或者使用chrome瀏覽器)
      (要以data:image/png;base64,開頭,別把整個(gè)img粘貼進(jìn)textarea)
      ps:這里還有一只base64企鵝http://1.iamzhanglei.sinaapp.com/base64.html

      雕刻球體

      最后,要做的是在球體表面進(jìn)行2D雕刻。這里涉及到正交投影的問題。如下圖所示:

      透視投影:

      201110230819366352 

      正交投影:

      201110230819372514

       

      這里值得注意的地方是,球體雕刻渲染的管線如下步驟:

      1.在球體的正面放上一個(gè)垂直地面的正方形平面(可以理解為正方體的正面),

      2.假定文字繪制到正方形平面,

      3.正方形上的文字通過正交投影至球體表面。

      所以,通過所以正面形平面上有像素的點(diǎn),發(fā)射一條平行于地面(y=0)的射線(可以得到,ray3的方向?yàn)椋?,0,-1))打在球體表面,這個(gè)時(shí)候影像平面是球體表面。所以下面的方法用于保存所有射線通過正交投影打在球體表面上的點(diǎn):

      function generateWordToBall(word1, word2, word3, ball, cube) {
          var color = null;
          var bca = [];
          for (var k = 0, l = word1.length; k < l; k++) {
              var dD = word1[k];
              var r3 = new Ray3(new Vector3(cube.minX + dD.position.x - 20, cube.maxY - dD.position.y + 30, cube.maxZ), new Vector3(0, 0, -1));
              var result3 = ball.intersect(r3);
              if (result3)
                  bca.push({ cp: result3.round(), cl: dD.color });
          }
      
          for (var k = 0, l = word2.length; k < l; k++) {
              var dD = word2[k];
              var r3 = new Ray3(new Vector3(cube.minX + dD.position.x - 60, cube.maxY - dD.position.y, cube.maxZ), new Vector3(0, 0, -1));
              var result3 = ball.intersect(r3);
              if (result3)
                  bca.push({ cp: result3.round(), cl: dD.color });
          }
      
          for (var k = 0, l = word3.length; k < l; k++) {
              var dD = word3[k];
              var r3 = new Ray3(new Vector3(cube.minX + dD.position.x, cube.maxY - dD.position.y - 20, cube.maxZ), new Vector3(0, 0, -1));
              var result3 = ball.intersect(r3);
              if (result3)
                  bca.push({ cp: result3.round(), cl: dD.color });
          }
      
          return bca;
      }

      拿到這些點(diǎn)之后,剩下的就很簡(jiǎn)單明了。只需把從視點(diǎn)發(fā)出去的射線與球體的交點(diǎn)和bca中的對(duì)比,如果在bca當(dāng)中有,則繪制對(duì)應(yīng)文字像素的顏色,在此不再闡述分析。

      在線演示

      請(qǐng)使用現(xiàn)代瀏覽器,你的瀏覽器過時(shí)了!下載地址http://dl.pconline.com.cn/download/51614.html

      that's all.Have fun!

      主站蜘蛛池模板: 国产爽视频一区二区三区| 国产激情免费视频在线观看| 欧美一区二区三区啪啪| 久久精品国产亚洲av天海翼 | 噜噜噜噜私人影院| 国产精品无卡毛片视频| 91福利一区福利二区| 梁平县| 国产va免费精品观看| 亚洲成a∨人片在线观看不卡| 日本道不卡一二三区视频| 亚洲欧美自偷自拍视频图片| 少妇高潮水多太爽了动态图| 久久亚洲精品11p| 国产精品一区免费在线看| av无码av无码专区| 999精品视频在线| 后入内射无码人妻一区| 午夜成年男人免费网站| 国产一区二区三区导航| 虞城县| 思思久99久女女精品| 欧美日韩精品一区二区视频| 四虎永久在线精品无码视频| 成人性无码专区免费视频| 亚洲av在线观看| 强奷乱码中文字幕| 国产午夜福利视频在线| 国产精品无码aⅴ嫩草| 国产毛片精品av一区二区| 成人无码午夜在线观看| 国产一区二区三区色老头| 乱码中文字幕| 青青草成人免费自拍视频| 国产视频一区二区三区麻豆| 亚洲av无码国产在丝袜线观看| 亚洲精品自拍在线视频| 国产亚洲情侣一区二区无| 精品无码日韩国产不卡av| 国产在线98福利播放视频 | 久久精品人人看人人爽|