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

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

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

      WebGL或OpenGL關(guān)于模型視圖投影變換的設(shè)置技巧

      1. 具體實(shí)例

      看了不少的關(guān)于WebGL/OpenGL的資料,筆者發(fā)現(xiàn)這些資料在講解圖形變換的時(shí)候都講了很多的原理,然后舉出一個(gè)特別簡(jiǎn)單的實(shí)例(坐標(biāo)是1.0,0.5的那種)來(lái)講解。確實(shí)一看就懂,但用到實(shí)際的場(chǎng)景之中就一臉懵逼了(比如地形的三維坐標(biāo)都是很大的數(shù)字)。所以筆者這里結(jié)合一個(gè)具體的實(shí)例,總結(jié)下WebGL/OpenGL中,關(guān)于模型變換、視圖變換、投影變換的設(shè)置技巧。

      繪制任何復(fù)雜的場(chǎng)景之前,都可以先繪制出其包圍盒,能應(yīng)用于包圍盒的圖形變換,基本上就能用于該場(chǎng)景了,因此,筆者這里繪制一幅地形的包圍盒。它的最大最小范圍為:

      //包圍盒范圍
      var minX = 399589.072;
      var maxX = 400469.072;
      var minY = 3995118.062;
      var maxY = 3997558.062;
      var minZ = 732;
      var maxZ = 1268;
      

      2. 解決方案

      WebGL是OpenGL的子集,因此我這里直接用WebGL的例子,但是各種接口函數(shù)跟OpenGL是非常類似的,尤其是圖形變換的函數(shù)。

      1) Cube.html

      <!DOCTYPE html>
      <html lang="zh">
        <head>
          <meta charset="utf-8" />
          <title>Hello cube</title>
        </head>
      
        <body onload="main()">
          <canvas id="webgl" width="600" height="600">
          Please use a browser that supports "canvas"
          </canvas>
      
          <script src="lib/webgl-utils.js"></script>
          <script src="lib/webgl-debug.js"></script>
          <script src="lib/cuon-utils.js"></script>
          <script src="lib/cuon-matrix.js"></script>
          <script src="Cube.js"></script>
        </body>
      </html>
      
      

      2) Cube.js

      // Vertex shader program
      var VSHADER_SOURCE =
          'attribute vec4 a_Position;\n' +
          'attribute vec4 a_Color;\n' +
          'uniform mat4 u_MvpMatrix;\n' +
          'varying vec4 v_Color;\n' +
          'void main() {\n' +
          '  gl_Position = u_MvpMatrix * a_Position;\n' +
          '  v_Color = a_Color;\n' +
          '}\n';
      
      // Fragment shader program
      var FSHADER_SOURCE =
          '#ifdef GL_ES\n' +
          'precision mediump float;\n' +
          '#endif\n' +
          'varying vec4 v_Color;\n' +
          'void main() {\n' +
          '  gl_FragColor = v_Color;\n' +
          '}\n';
      
      //包圍盒范圍
      var minX = 399589.072;
      var maxX = 400469.072;
      var minY = 3995118.062;
      var maxY = 3997558.062;
      var minZ = 732;
      var maxZ = 1268;
      
      //包圍盒中心
      var cx = (minX + maxX) / 2.0;
      var cy = (minY + maxY) / 2.0;
      var cz = (minZ + maxZ) / 2.0;
      
      //當(dāng)前l(fā)ookAt()函數(shù)初始視點(diǎn)的高度
      var eyeHight = 2000.0;
      
      //根據(jù)視點(diǎn)高度算出setPerspective()函數(shù)的合理角度
      var fovy = (maxY - minY) / 2.0 / eyeHight;
      fovy = 180.0 / Math.PI * Math.atan(fovy) * 2;
      
      //setPerspective()遠(yuǎn)截面
      var far = 3000;
      
      //
      function main() {
          // Retrieve <canvas> element
          var canvas = document.getElementById('webgl');
      
          // Get the rendering context for WebGL
          var gl = getWebGLContext(canvas);
          if (!gl) {
              console.log('Failed to get the rendering context for WebGL');
              return;
          }
      
          // Initialize shaders
          if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
              console.log('Failed to intialize shaders.');
              return;
          }
      
          // Set the vertex coordinates and color
          var n = initVertexBuffers(gl);
          if (n < 0) {
              console.log('Failed to set the vertex information');
              return;
          }
      
          // Get the storage location of u_MvpMatrix
          var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
          if (!u_MvpMatrix) {
              console.log('Failed to get the storage location of u_MvpMatrix');
              return;
          }
      
          // Register the event handler
          var currentAngle = [0.0, 0.0]; // Current rotation angle ([x-axis, y-axis] degrees)
          initEventHandlers(canvas, currentAngle);
      
          // Set clear color and enable hidden surface removal
          gl.clearColor(0.0, 0.0, 0.0, 1.0);
          gl.enable(gl.DEPTH_TEST);
      
          // Start drawing
          var tick = function () {
      
              //setPerspective()寬高比
              var aspect = canvas.width / canvas.height;
      
              //
              draw(gl, n, aspect, u_MvpMatrix, currentAngle);
              requestAnimationFrame(tick, canvas);
          };
          tick();
      }
      
      function initEventHandlers(canvas, currentAngle) {
          var dragging = false;         // Dragging or not
          var lastX = -1, lastY = -1;   // Last position of the mouse
      
          // Mouse is pressed
          canvas.onmousedown = function (ev) {
              var x = ev.clientX;
              var y = ev.clientY;
              // Start dragging if a moue is in <canvas>
              var rect = ev.target.getBoundingClientRect();
              if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
                  lastX = x;
                  lastY = y;
                  dragging = true;
              }
          };
      
          //鼠標(biāo)離開(kāi)時(shí)
          canvas.onmouseleave = function (ev) {
              dragging = false;
          };
      
          // Mouse is released
          canvas.onmouseup = function (ev) {
              dragging = false;
          };
      
          // Mouse is moved
          canvas.onmousemove = function (ev) {
              var x = ev.clientX;
              var y = ev.clientY;
              if (dragging) {
                  var factor = 100 / canvas.height; // The rotation ratio
                  var dx = factor * (x - lastX);
                  var dy = factor * (y - lastY);
                  // Limit x-axis rotation angle to -90 to 90 degrees
                  //currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90.0), -90.0);
                  currentAngle[0] = currentAngle[0] + dy;
                  currentAngle[1] = currentAngle[1] + dx;
              }
              lastX = x, lastY = y;
          };
      
          //鼠標(biāo)縮放
          canvas.onmousewheel = function (event) {
              var lastHeight = eyeHight;
              if (event.wheelDelta > 0) {
                  eyeHight = Math.max(1, eyeHight - 80);
              } else {
                  eyeHight = eyeHight + 80;
              }
      
              far = far + eyeHight - lastHeight;
          };
      }
      
      function draw(gl, n, aspect, u_MvpMatrix, currentAngle) {
          //模型矩陣
          var modelMatrix = new Matrix4();
          modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 
          modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis    
          modelMatrix.translate(-cx, -cy, -cz);
      
          //視圖矩陣
          var viewMatrix = new Matrix4();
          viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0);
      
          //投影矩陣
          var projMatrix = new Matrix4();
          projMatrix.setPerspective(fovy, aspect, 10, far);
      
          //模型視圖投影矩陣
          var mvpMatrix = new Matrix4();
          mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
      
          // Pass the model view projection matrix to u_MvpMatrix
          gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
      
          // Clear color and depth buffer
          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
          // Draw the cube
          gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
      }
      
      function initVertexBuffers(gl) {
          // Create a cube
          //    v6----- v5
          //   /|      /|
          //  v1------v0|
          //  | |     | |
          //  | |v7---|-|v4
          //  |/      |/
          //  v2------v3
      
          var verticesColors = new Float32Array([
              // Vertex coordinates and color
              maxX, maxY, maxZ, 1.0, 1.0, 1.0,  // v0 White
              minX, maxY, maxZ, 1.0, 0.0, 1.0,  // v1 Magenta
              minX, minY, maxZ, 1.0, 0.0, 0.0,  // v2 Red
              maxX, minY, maxZ, 1.0, 1.0, 0.0,  // v3 Yellow
              maxX, minY, minZ, 0.0, 1.0, 0.0,  // v4 Green
              maxX, maxY, minZ, 0.0, 1.0, 1.0,  // v5 Cyan
              minX, maxY, minZ, 0.0, 0.0, 1.0,  // v6 Blue
              minX, minY, minZ, 1.0, 0.0, 1.0   // v7 Black
          ]);
      
          // Indices of the vertices
          var indices = new Uint8Array([
              0, 1, 2, 0, 2, 3,    // front
              0, 3, 4, 0, 4, 5,    // right
              0, 5, 6, 0, 6, 1,    // up
              1, 6, 7, 1, 7, 2,    // left
              7, 4, 3, 7, 3, 2,    // down
              4, 7, 6, 4, 6, 5     // back
          ]);
      
          // Create a buffer object
          var vertexColorBuffer = gl.createBuffer();
          var indexBuffer = gl.createBuffer();
          if (!vertexColorBuffer || !indexBuffer) {
              return -1;
          }
      
          // Write the vertex coordinates and color to the buffer object
          gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
      
          var FSIZE = verticesColors.BYTES_PER_ELEMENT;
          // Assign the buffer object to a_Position and enable the assignment
          var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
          if (a_Position < 0) {
              console.log('Failed to get the storage location of a_Position');
              return -1;
          }
          gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
          gl.enableVertexAttribArray(a_Position);
          // Assign the buffer object to a_Color and enable the assignment
          var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
          if (a_Color < 0) {
              console.log('Failed to get the storage location of a_Color');
              return -1;
          }
          gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
          gl.enableVertexAttribArray(a_Color);
      
          // Write the indices to the buffer object
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
      
          return indices.length;
      }
      
      

      3) 運(yùn)行結(jié)果

      這份代碼改進(jìn)《WebGL編程指南》一書(shū)里面繪制一個(gè)簡(jiǎn)單立方體的例子,引用的幾個(gè)JS-lib也是該書(shū)提供。本例全部源代碼地址鏈接為:https://share.weiyun.com/52XmsFv ,密碼:h1lbay。
      用chrome打開(kāi)Cube.html,會(huì)出現(xiàn)一個(gè)長(zhǎng)方體的包圍盒,還可以用鼠標(biāo)左鍵旋轉(zhuǎn),鼠標(biāo)滾輪縮放:

      3. 詳細(xì)講解

      本例的思路是通過(guò)JS的requestAnimationFrame()函數(shù)不停的調(diào)用繪制函數(shù)draw(),同時(shí)將一些變量關(guān)聯(lián)到鼠標(biāo)操作事件和draw(),達(dá)到頁(yè)面圖形變換的效果。這里筆者就不講原理,重點(diǎn)講一講設(shè)置三個(gè)圖形變換的具體過(guò)程,網(wǎng)上已經(jīng)有非常多的原理介紹了。

      1) 模型變換

      在draw()函數(shù)中設(shè)置模型矩陣:

      //模型矩陣
      var modelMatrix = new Matrix4();
      modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 
      modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis    
      modelMatrix.translate(-cx, -cy, -cz);
      

      由于這個(gè)包圍盒(長(zhǎng)方體)的坐標(biāo)值都非常大,所以第一步需要對(duì)其做平移變換translate(-cx, -cy, -cz),cx,cy,cz就是包圍盒的中心:

      //包圍盒中心
      var cx = (minX + maxX) / 2.0;
      var cy = (minY + maxY) / 2.0;
      var cz = (minZ + maxZ) / 2.0;
      

      接下來(lái)是旋轉(zhuǎn)變換,數(shù)組currentAngle記錄了繞X軸和Y軸旋轉(zhuǎn)的角度,初始值為0。配合onmousedown,onmouseup,onmousemove三個(gè)鼠標(biāo)事件,將頁(yè)面鼠標(biāo)X、Y方向的移動(dòng),轉(zhuǎn)換成繞X軸,Y軸的角度值,累計(jì)到currentAngle中,從而實(shí)現(xiàn)了三維模型隨鼠標(biāo)旋轉(zhuǎn)。

      // Mouse is moved
      canvas.onmousemove = function (ev) {
          var x = ev.clientX;
          var y = ev.clientY;
          if (dragging) {
              var factor = 100 / canvas.height; // The rotation ratio
              var dx = factor * (x - lastX);
              var dy = factor * (y - lastY);
              // Limit x-axis rotation angle to -90 to 90 degrees
              //currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90.0), -90.0);
              currentAngle[0] = currentAngle[0] + dy;
              currentAngle[1] = currentAngle[1] + dx;
          }
          lastX = x, lastY = y;
      };
      

      注意模型矩陣的平移變換要放后面,需要把坐標(biāo)軸換到包圍盒中心,才能繞三維模型自轉(zhuǎn)。

      2) 視圖變換

      通過(guò)lookAt()函數(shù)設(shè)置視圖矩陣:

      //當(dāng)前l(fā)ookAt()函數(shù)初始視點(diǎn)的高度
      var eyeHight = 2000.0;
      
      // …
      
      //視圖矩陣
      var viewMatrix = new Matrix4();
      viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0);
      

      視圖變換調(diào)整的是觀察者的狀態(tài),lookAt()函數(shù)分別設(shè)置了視點(diǎn)、目標(biāo)觀察點(diǎn)以及上方向。雖然可以在任何位置去觀察三維場(chǎng)景的點(diǎn),從而得到渲染結(jié)果。但在實(shí)際的應(yīng)用當(dāng)中,這個(gè)函數(shù)設(shè)置的結(jié)果很難以想象,所以筆者設(shè)置成,觀察者站在包圍盒中心上方的位置,對(duì)準(zhǔn)坐標(biāo)系原點(diǎn)(注意這個(gè)時(shí)候經(jīng)過(guò)模型變換,包圍盒的中心點(diǎn)已經(jīng)是坐標(biāo)系原點(diǎn)了),常見(jiàn)的Y軸作為上方向。這樣,視圖內(nèi)無(wú)論如何都是可見(jiàn)的。
      這里將視點(diǎn)的高度設(shè)置成變量eyeHight,初始值為2000,是一個(gè)大于0的經(jīng)驗(yàn)值。同時(shí)通過(guò)鼠標(biāo)的滾輪事件onmousewheel()調(diào)整該值,從而實(shí)現(xiàn)三維模型的縮放的:

       //鼠標(biāo)縮放
       canvas.onmousewheel = function (event) {
           var lastHeight = eyeHight;
           if (event.wheelDelta > 0) {
               eyeHight = Math.max(1, eyeHight - 80);
           } else {
               eyeHight = eyeHight + 80;
           } 
       };
      

      3) 投影變換

      通過(guò)setPerspective()來(lái)設(shè)置投影變換:

      //根據(jù)視點(diǎn)高度算出setPerspective()函數(shù)的合理角度
      var fovy = (maxY - minY) / 2.0 / eyeHight;
      fovy = 180.0 / Math.PI * Math.atan(fovy) * 2;
      
      //setPerspective()遠(yuǎn)截面
      var far = 3000;
      
      //setPerspective()寬高比
      var aspect = canvas.width / canvas.height;
      
      //...
      
      //投影矩陣
      var projMatrix = new Matrix4();
      projMatrix.setPerspective(fovy, aspect, 10, far);
      

      前面的視圖變換已經(jīng)論述了,這個(gè)模型是在中心點(diǎn)上方去觀察中心點(diǎn),相當(dāng)于視線垂直到前界面near的表面,那么setPerspective()就可以確定其角度f(wàn)ovy了,示意圖如下:

      很明顯的看出,當(dāng)光線射到包圍盒的中心,包圍盒Y方向長(zhǎng)度的一半,除以視點(diǎn)高,就是fovy一般的正切值。

      寬高比aspect即是頁(yè)面canvas元素的寬高比。

      近界面near一般設(shè)置成較近的值,但是不能太近(比如小于1),否則會(huì)影響深度判斷的精度造成頁(yè)面閃爍。《OpenGL繪制紋理,縮放相機(jī)導(dǎo)致紋理閃爍的解決方法gluPerspective ()》論述了這個(gè)問(wèn)題。

      而遠(yuǎn)界面far也是需要跟著鼠標(biāo)滾輪一起變換的,否則當(dāng)eyeHight變大,三維物體會(huì)逐漸離開(kāi)透視變換的視錐體:

      //鼠標(biāo)縮放
      canvas.onmousewheel = function (event) {
          var lastHeight = eyeHight;
          if (event.wheelDelta > 0) {
              eyeHight = Math.max(1, eyeHight - 80);
          } else {
              eyeHight = eyeHight + 80;
          }
      
          far = far + eyeHight - lastHeight;
      };
      

      4) 模型視圖投影矩陣

      將三個(gè)矩陣都應(yīng)用起來(lái),就得到最終的模型視圖投影矩陣。注意計(jì)算式是:投影矩陣 * 視圖矩陣 * 模型矩陣:

      //模型視圖投影矩陣
      var mvpMatrix = new Matrix4();
      mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
      

      4. 存在問(wèn)題

      本例中的三維物體隨著鼠標(biāo)旋轉(zhuǎn),是把鼠標(biāo)X、Y方向的移動(dòng)距離轉(zhuǎn)換成繞X軸,Y軸方向的角度來(lái)實(shí)現(xiàn)的。但是如何用鼠標(biāo)實(shí)現(xiàn)繞Z軸(第三軸)旋轉(zhuǎn)呢?例如像OSG這樣的渲染引擎,是可以用鼠標(biāo)繞第三個(gè)軸旋轉(zhuǎn)的(當(dāng)然操作有點(diǎn)費(fèi)力)。這里希望大家能批評(píng)指正下。

      posted @ 2019-02-17 23:14  charlee44  閱讀(1670)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 色婷婷久久综合中文久久一本 | 欧美日韩国产图片区一区| 成人国产精品一区二区不卡| 日本另类αv欧美另类aⅴ| 麻豆国产AV剧情偷闻女邻居内裤| 亚洲熟妇中文字幕五十路| 亚洲免费观看视频| 国产成人综合亚洲精品国产| 国产精品自拍午夜福利| 亚洲人成电影网站色mp4| 99热门精品一区二区三区无码 | 日韩国产精品一区二区av| 色爱综合另类图片av| 亚洲熟女精品一区二区| 成人性无码专区免费视频| 日韩蜜桃AV无码中文字幕不卡高清一区二区 | 鄄城县| 亚洲熟女乱一区二区三区| 国产午夜亚洲精品国产成人| 国产精品视频白浆免费视频| 成人国产av精品免费网| 欧美 亚洲 中文 国产 综合| 亚洲国产成人精品女久久| 亚洲精品一区二区动漫| 国产成人AV大片大片在线播放| 日韩一区二区三区亚洲一| 久久精品免视看国产成人| 亚洲精品码中文在线观看| 亚洲av日韩av永久无码电影| 亚洲日韩成人无码不卡网站| 国产精品伦人视频免费看| 精品蜜臀国产av一区二区| 青春草公开在线视频日韩| 国产午夜视频在线观看| 国产午夜亚洲精品福利| 国产午夜A理论毛片| 亚洲av产在线精品亚洲第一站| 久久精品夜夜夜夜夜久久| 69精品无人区国产一区| 亚洲日韩乱码一区二区三区四区| 精品国产一区二区色老头|