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

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

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

      WebGL簡易教程(十):光照

      1. 概述

      在上一篇教程《WebGL簡易教程(九):綜合實例:地形的繪制》中,實現(xiàn)了對一個地形場景的渲染。在這篇教程中,就給這個地形場景加上光照,讓其更加真實,立體感更強。

      2. 原理

      2.1. 光源類型

      在現(xiàn)實中,即使是一個純白色的物體,你也能很容易識別物體的輪廓。事實上,這是因為光照的產(chǎn)生的陰暗差異給了其立體感。類似于現(xiàn)實,WebGL有三種基本類型的光:

      1. 點光源光:一個點向周圍發(fā)出的光,如燈泡、火焰等。定義一個點光源光需要光源的位置、光線方向以及顏色。根據(jù)照射點的位置不同,光線的方向也不同。
      2. 平行光:平行光可以看成是無限遠處的光源發(fā)出的光,如太陽光。因為離光源的位置特別遠,所以到達被照物體時可以認為光線是平行的。只需要用一個方向和顏色來定義即可。
      3. 環(huán)境光:環(huán)境光也就是間接光,指的是那些光源發(fā)出后,經(jīng)過其他物體各種發(fā)射,然后照到物體表面上的光線。比如說夜間打開冰箱的門,這個廚房產(chǎn)生的亮光。因為經(jīng)過多次反射后,強度差距已經(jīng)非常小,沒有必要精確計算光線強度。所以一般認為環(huán)境光是均勻照射到物體表面的,只需要一個顏色來定義。

      如圖所示:
      image

      2.2. 反射類型

      由于物體最終顯示的顏色也就是光線反射造成的顏色,由兩部分因素決定:入射光和物體表面的類型。入射光信息包括入射光的方向和顏色,而物體表面的信息包含基底色和反射特性。根據(jù)物體反射光線的方式有環(huán)境反射(enviroment/ambient reflection)和漫反射(diffuse reflection)兩種類型的光:

      2.2.1. 環(huán)境反射(enviroment/ambient reflection)

      環(huán)境反射是針對環(huán)境光而言的,在環(huán)境反射中,環(huán)境光照射物體是各方面均勻、強度相等的,反射的方向可以認為就是入射光的反方向。也就是最終物體的顏色只跟入射光顏色和基底色有關。那么可以這樣定義環(huán)境反射光顏色:

      \[<環(huán)境反射光顏色>=<入射光顏色>×<表面基底色>\tag{1} \]

      注意在式子中,這個乘法操作指的是顏色矢量上逐分量相乘。

      2.2.2. 漫反射(diffuse reflection)

      漫反射是針對平行光和點光源光而言的。相信在初中物理的時候就已經(jīng)接觸過鏡面反射和漫反射。如果物體表面像鏡子一樣平滑,那么光線就會以特定的角度反射過去,從視覺效果來說就是刺眼的反光效果;如果物體表面是凹凸不平的,反射光就會以不固定的角度發(fā)射出去。在現(xiàn)實中大多數(shù)的物體表面都是粗糙的,所以才能看清各種各樣的物體。如圖所示:
      image

      漫反射中,反射光的顏色除了取決于入射光的顏色、表面的基底色,還有入射光與物體表面的法向量形成的入射角。令入射角為θ,漫反射光的顏色可以根據(jù)下式計算:

      \[<漫反射光顏色>=<入射光顏色>×<表面基底色>×cosθ\tag{2} \]

      入射角θ可以通過矢量的點積來計算:

      \[<光線方向>·<法線方向> = |光線方向|*|法線方向|*cosθ \]

      如果光線方向和法線方向都是歸一化的,那么向量的模(長度)就為1,則有:

      \[<漫反射光顏色>=<入射光顏色>×<表面基底色>×(<光線方向>·<法線方向>) \]

      注意,這里的“光線方向”,實際上指的是入射方向的反方向,即從入射點指向光源方向,如圖所示:
      image

      2.2.3. 綜合

      當漫反射和環(huán)境反射同時存在時,將兩者加起來,就會得到物體最終被觀察到的顏色:

      \[<表面的反射光顏色> = <漫反射光顏色>+<環(huán)境反射光顏色>\tag{3} \]

      3. 實例

      3.1. 具體代碼

      改進上一篇教程的JS代碼如下:

      // 頂點著色器程序
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' + //位置
        'attribute vec4 a_Color;\n' + //顏色
        'attribute vec4 a_Normal;\n' + //法向量
        'uniform mat4 u_MvpMatrix;\n' +
        'varying vec4 v_Color;\n' +
        'varying vec4 v_Normal;\n' +
        'void main() {\n' +
        '  gl_Position = u_MvpMatrix * a_Position;\n' + //設置頂點的坐標
        '  v_Color = a_Color;\n' +
        '  v_Normal = a_Normal;\n' +
        '}\n';
      
      // 片元著色器程序
      var FSHADER_SOURCE =
        'precision mediump float;\n' +
        'uniform vec3 u_DiffuseLight;\n' + // 漫反射光顏色
        'uniform vec3 u_LightDirection;\n' + // 漫反射光的方向
        'uniform vec3 u_AmbientLight;\n' + // 環(huán)境光顏色
        'varying vec4 v_Color;\n' +
        'varying vec4 v_Normal;\n' +
        'void main() {\n' +
        //對法向量歸一化
        '  vec3 normal = normalize(v_Normal.xyz);\n' +
        //計算光線向量與法向量的點積
        '  float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' +
        //計算漫發(fā)射光的顏色 
        '  vec3 diffuse = u_DiffuseLight * v_Color.rgb * nDotL;\n' +
        //計算環(huán)境光的顏色
        '  vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
        '  gl_FragColor = vec4(diffuse+ambient, v_Color.a);\n' +
        '}\n';
      
      //定義一個矩形體:混合構造函數(shù)原型模式
      function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) {
        this.minX = minX;
        this.maxX = maxX;
        this.minY = minY;
        this.maxY = maxY;
        this.minZ = minZ;
        this.maxZ = maxZ;
      }
      
      Cuboid.prototype = {
        constructor: Cuboid,
        CenterX: function () {
          return (this.minX + this.maxX) / 2.0;
        },
        CenterY: function () {
          return (this.minY + this.maxY) / 2.0;
        },
        CenterZ: function () {
          return (this.minZ + this.maxZ) / 2.0;
        },
        LengthX: function () {
          return (this.maxX - this.minX);
        },
        LengthY: function () {
          return (this.maxY - this.minY);
        }
      }
      
      //定義DEM
      function Terrain() {}
      Terrain.prototype = {
        constructor: Terrain,
        setWH: function (col, row) {
          this.col = col;
          this.row = row;
        }
      }
      
      var currentAngle = [0.0, 0.0]; // 繞X軸Y軸的旋轉角度 ([x-axis, y-axis])
      var curScale = 1.0; //當前的縮放比例
      
      function main() {
        var demFile = document.getElementById('demFile');
        if (!demFile) {
          console.log("Failed to get demFile element!");
          return;
        }
      
        demFile.addEventListener("change", function (event) {
          //判斷瀏覽器是否支持FileReader接口
          if (typeof FileReader == 'undefined') {
            console.log("你的瀏覽器不支持FileReader接口!");
            return;
          }
      
          var input = event.target;
          var reader = new FileReader();
          reader.onload = function () {
            if (reader.result) {
      
              //讀取
              var terrain = new Terrain();
              if (!readDEMFile(reader.result, terrain)) {
                console.log("文件格式有誤,不能讀取該文件!");
              }
      
              //繪制
              onDraw(gl, canvas, terrain);
            }
          }
      
          reader.readAsText(input.files[0]);
        });
      
        // 獲取 <canvas> 元素
        var canvas = document.getElementById('webgl');
      
        // 獲取WebGL渲染上下文
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }
      
        // 初始化著色器
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }
      
        // 指定清空<canvas>的顏色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
      
        // 開啟深度測試
        gl.enable(gl.DEPTH_TEST);
      
        //清空顏色和深度緩沖區(qū)
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      }
      
      //繪制函數(shù)
      function onDraw(gl, canvas, terrain) {
        // 設置頂點位置
        var n = initVertexBuffers(gl, terrain);
        if (n < 0) {
          console.log('Failed to set the positions of the vertices');
          return;
        }
      
        //注冊鼠標事件
        initEventHandlers(canvas);
      
        //設置燈光
        setLight(gl);
      
        //繪制函數(shù)
        var tick = function () {
          //設置MVP矩陣
          setMVPMatrix(gl, canvas, terrain.cuboid);
      
          //清空顏色和深度緩沖區(qū)
          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
          //繪制矩形體
          gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
      
          //請求瀏覽器調(diào)用tick
          requestAnimationFrame(tick);
        };
      
        //開始繪制
        tick();
      }
      
      //設置燈光
      function setLight(gl) {
        var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
        var u_DiffuseLight = gl.getUniformLocation(gl.program, 'u_DiffuseLight');
        var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection');
        if (!u_DiffuseLight || !u_LightDirection || !u_AmbientLight) {
          console.log('Failed to get the storage location');
          return;
        }
      
        //設置漫反射光
        gl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0);
      
        // 設置光線方向(世界坐標系下的)
        var solarAltitude = 45.0;
        var solarAzimuth = 315.0;
        var fAltitude = solarAltitude * Math.PI / 180; //光源高度角
        var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角
      
        var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth);
        var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth);
        var arrayvectorZ = Math.sin(fAltitude);
        
        var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]);
        lightDirection.normalize(); // Normalize
        gl.uniform3fv(u_LightDirection, lightDirection.elements);
      
        //設置環(huán)境光
        gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
      }
      
      //讀取DEM函數(shù)
      function readDEMFile(result, terrain) {
        var stringlines = result.split("\n");
        if (!stringlines || stringlines.length <= 0) {
          return false;
        }
      
        //讀取頭信息
        var subline = stringlines[0].split("\t");
        if (subline.length != 6) {
          return false;
        }
        var col = parseInt(subline[4]); //DEM寬
        var row = parseInt(subline[5]); //DEM高
        var verticeNum = col * row;
        if (verticeNum + 1 > stringlines.length) {
          return false;
        }
        terrain.setWH(col, row);
      
        //讀取點信息
        var ci = 0;
        var pSize = 9;
        terrain.verticesColors = new Float32Array(verticeNum * pSize);
        for (var i = 1; i < stringlines.length; i++) {
          if (!stringlines[i]) {
            continue;
          }
      
          var subline = stringlines[i].split(',');
          if (subline.length != pSize) {
            continue;
          }
      
          for (var j = 0; j < pSize; j++) {
            terrain.verticesColors[ci] = parseFloat(subline[j]);
            ci++;
          }
        }
      
        if (ci !== verticeNum * pSize) {
          return false;
        }
      
        //包圍盒
        var minX = terrain.verticesColors[0];
        var maxX = terrain.verticesColors[0];
        var minY = terrain.verticesColors[1];
        var maxY = terrain.verticesColors[1];
        var minZ = terrain.verticesColors[2];
        var maxZ = terrain.verticesColors[2];
        for (var i = 0; i < verticeNum; i++) {
          minX = Math.min(minX, terrain.verticesColors[i * pSize]);
          maxX = Math.max(maxX, terrain.verticesColors[i * pSize]);
          minY = Math.min(minY, terrain.verticesColors[i * pSize + 1]);
          maxY = Math.max(maxY, terrain.verticesColors[i * pSize + 1]);
          minZ = Math.min(minZ, terrain.verticesColors[i * pSize + 2]);
          maxZ = Math.max(maxZ, terrain.verticesColors[i * pSize + 2]);
        }
      
        terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ);
      
        return true;
      }
      
      
      //注冊鼠標事件
      function initEventHandlers(canvas) {
        var dragging = false; // Dragging or not
        var lastX = -1,
          lastY = -1; // Last position of the mouse
      
        //鼠標按下
        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;
          }
        };
      
        //鼠標離開時
        canvas.onmouseleave = function (ev) {
          dragging = false;
        };
      
        //鼠標釋放
        canvas.onmouseup = function (ev) {
          dragging = false;
        };
      
        //鼠標移動
        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);
            currentAngle[0] = currentAngle[0] + dy;
            currentAngle[1] = currentAngle[1] + dx;
          }
          lastX = x, lastY = y;
        };
      
        //鼠標縮放
        canvas.onmousewheel = function (event) {
          if (event.wheelDelta > 0) {
            curScale = curScale * 1.1;
          } else {
            curScale = curScale * 0.9;
          }
        };
      }
      
      //設置MVP矩陣
      function setMVPMatrix(gl, canvas, cuboid) {
        // 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;
        }
      
        //模型矩陣
        var modelMatrix = new Matrix4();
        modelMatrix.scale(curScale, curScale, curScale);
        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(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ());
      
        //投影矩陣
        var fovy = 60;
        var near = 1;
        var projMatrix = new Matrix4();
        projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000);
      
        //計算lookAt()函數(shù)初始視點的高度
        var angle = fovy / 2 * Math.PI / 180.0;
        var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle;
      
        //視圖矩陣  
        var viewMatrix = new Matrix4(); // View matrix   
        viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0);
      
        //MVP矩陣
        var mvpMatrix = new Matrix4();
        mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
      
        //將MVP矩陣傳輸?shù)街鞯膗niform變量u_MvpMatrix
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
      }
      
      //
      function initVertexBuffers(gl, terrain) {
        //DEM的一個網(wǎng)格是由兩個三角形組成的
        //      0------1            1
        //      |                   |
        //      |                   |
        //      col       col------col+1    
        var col = terrain.col;
        var row = terrain.row;
      
        var indices = new Uint16Array((row - 1) * (col - 1) * 6);
        var ci = 0;
        for (var yi = 0; yi < row - 1; yi++) {
          //for (var yi = 0; yi < 10; yi++) {
          for (var xi = 0; xi < col - 1; xi++) {
            indices[ci * 6] = yi * col + xi;
            indices[ci * 6 + 1] = (yi + 1) * col + xi;
            indices[ci * 6 + 2] = yi * col + xi + 1;
            indices[ci * 6 + 3] = (yi + 1) * col + xi;
            indices[ci * 6 + 4] = (yi + 1) * col + xi + 1;
            indices[ci * 6 + 5] = yi * col + xi + 1;
            ci++;
          }
        }
      
        //
        var verticesColors = terrain.verticesColors;
        var FSIZE = verticesColors.BYTES_PER_ELEMENT; //數(shù)組中每個元素的字節(jié)數(shù)
      
        // 創(chuàng)建緩沖區(qū)對象
        var vertexColorBuffer = gl.createBuffer();
        var indexBuffer = gl.createBuffer();
        if (!vertexColorBuffer || !indexBuffer) {
          console.log('Failed to create the buffer object');
          return -1;
        }
      
        // 將緩沖區(qū)對象綁定到目標
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
        // 向緩沖區(qū)對象寫入數(shù)據(jù)
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
      
        //獲取著色器中attribute變量a_Position的地址 
        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;
        }
        // 將緩沖區(qū)對象分配給a_Position變量
        gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 9, 0);
      
        // 連接a_Position變量與分配給它的緩沖區(qū)對象
        gl.enableVertexAttribArray(a_Position);
      
        //獲取著色器中attribute變量a_Color的地址 
        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;
        }
        // 將緩沖區(qū)對象分配給a_Color變量
        gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 3);
        // 連接a_Color變量與分配給它的緩沖區(qū)對象
        gl.enableVertexAttribArray(a_Color);
      
        // 向緩沖區(qū)對象分配a_Normal變量,傳入的這個變量要在著色器使用才行
        var a_Normal = gl.getAttribLocation(gl.program, 'a_Normal');
        if (a_Normal < 0) {
          console.log('Failed to get the storage location of a_Normal');
          return -1;
        }
        gl.vertexAttribPointer(a_Normal, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 6);
        //開啟a_Normal變量
        gl.enableVertexAttribArray(a_Normal);
      
        // 將頂點索引寫入到緩沖區(qū)對象
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
      
        return indices.length;
      }
      

      3.2. 改動詳解

      3.2.1. 設置日照

      主要改動是在繪制函數(shù)onDraw()中添加了一個設置光照的函數(shù)setLight():

      //繪制函數(shù)
      function onDraw(gl, canvas, terrain) {
        //...
      
        //注冊鼠標事件
        initEventHandlers(canvas);
      
        //設置燈光
        setLight(gl);
      
        //繪制函數(shù)
        var tick = function () {
          //...
        };
      
        //開始繪制
        tick();
      }
      

      具體展開這個函數(shù),可以看到這段代碼主要是給著色器傳入了環(huán)境光顏色u_AmbientLight、漫反射光顏色u_DiffuseLight、漫反射光方向u_LightDirection這三個參數(shù)。環(huán)境光顏色是由其他物體反射照成的,所以環(huán)境光強度較弱,設置為(0.2,0.2,0.2)。這里用漫反射光顏色來模擬太陽光,可以設為最強(1.0,1.0,1.0):

      //設置燈光
      function setLight(gl) {
        var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
        var u_DiffuseLight = gl.getUniformLocation(gl.program, 'u_DiffuseLight');
        var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection');
        if (!u_DiffuseLight || !u_LightDirection || !u_AmbientLight) {
          console.log('Failed to get the storage location');
          return;
        }
      
        //設置漫反射光
        gl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0);
      
        //...
      
        gl.uniform3fv(u_LightDirection, lightDirection.elements);
      
        //設置環(huán)境光
        gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
      }
      

      前面提到過,太陽光是一種平行光,所以只需要設置方向就行了。這個方向的計算與兩個地理學參數(shù)太陽高度角solarAltitude和太陽方位角solarAzimuth有關。可以暫時不用去關注其具體的推算細節(jié)(可參看我的另外一篇博文通過OSG實現(xiàn)對模型的日照模擬第二節(jié)和第四節(jié)),只需要知道這里的漫反射方向不是隨意指定,是根據(jù)實際情況參數(shù)計算出來的。

      function setLight(gl) {
      {
        //...
      
        // 設置光線方向(世界坐標系下的)
        var solarAltitude = 45.0;
        var solarAzimuth = 315.0;
        var fAltitude = solarAltitude * Math.PI / 180; //光源高度角
        var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角
      
        var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth);
        var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth);
        var arrayvectorZ = Math.sin(fAltitude);
        
        var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]);
        lightDirection.normalize(); // Normalize
      
        //...
      }
      

      3.2.2. 著色器光照設置

      這里頂點著色器中并沒有用到傳入的光照參數(shù),而是把頂點緩沖區(qū)對象的顏色值和法向量值保存為varying變量,用來傳入片元緩沖區(qū):

      // 頂點著色器程序
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' + //位置
        'attribute vec4 a_Color;\n' + //顏色
        'attribute vec4 a_Normal;\n' + //法向量
        'uniform mat4 u_MvpMatrix;\n' +
        'varying vec4 v_Color;\n' +
        'varying vec4 v_Normal;\n' +
        'void main() {\n' +
        '  gl_Position = u_MvpMatrix * a_Position;\n' + //設置頂點的坐標
        '  v_Color = a_Color;\n' +
        '  v_Normal = a_Normal;\n' +
        '}\n';
      

      在片元緩沖區(qū)中,傳入到片元緩沖區(qū)的顏色值和法向量值都經(jīng)過了內(nèi)插,變成了每個片元的基底色和法向量值。將該法向量歸一化,與傳入的漫反射方向做點積,得到漫反射入射角。漫反射入射角與傳入的漫反射光強度以及片元基底色,根據(jù)公式(2)計算漫反射光顏色。片元基底色與傳入的環(huán)境光顏色,根據(jù)公式(1)計算環(huán)境反射光顏色。根據(jù)公式(3)將兩者相加,得到最終顯示的片元顏色。

      // 片元著色器程序
      var FSHADER_SOURCE =
        'precision mediump float;\n' +
        'uniform vec3 u_DiffuseLight;\n' + // 漫反射光顏色
        'uniform vec3 u_LightDirection;\n' + // 漫反射光的方向
        'uniform vec3 u_AmbientLight;\n' + // 環(huán)境光顏色
        'varying vec4 v_Color;\n' +
        'varying vec4 v_Normal;\n' +
        'void main() {\n' +
        //對法向量歸一化
        '  vec3 normal = normalize(v_Normal.xyz);\n' +
        //計算光線向量與法向量的點積
        '  float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' +
        //計算漫發(fā)射光的顏色 
        '  vec3 diffuse = u_DiffuseLight * v_Color.rgb * nDotL;\n' +
        //計算環(huán)境光的顏色
        '  vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
        '  gl_FragColor = vec4(diffuse+ambient, v_Color.a);\n' +
        '}\n';
      

      4. 結果

      瀏覽器最終顯示的結果如下:
      image
      image

      相比上一篇教程的渲染效果,可以明顯發(fā)現(xiàn)立體感增強,能夠清楚看到地形的起伏情況。

      5. 參考

      本來部分代碼和插圖來自《WebGL編程指南》,源代碼鏈接:地址 。會在此共享目錄中持續(xù)更新后續(xù)的內(nèi)容。

      posted @ 2019-10-13 19:49  charlee44  閱讀(3676)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 99久久婷婷国产综合精品青草漫画 | 亚洲欧洲日产国码久在线| 日本阿v片在线播放免费| 久久人人97超碰国产精品| 风流少妇树林打野战视频| 精品国产一区二区三区性色| 超碰伊人久久大香线蕉综合| 蜜臀av性久久久久蜜臀aⅴ麻豆| 日本丰满白嫩大屁股ass| 在线视频一区二区三区色| 成人午夜在线观看日韩| 丰满无码人妻热妇无码区| 亚洲精品三区四区成人少| 精品国产乱子伦一区二区三区| 五月天中文字幕mv在线| 51妺嘿嘿午夜福利| 日韩一区二区三区精品区| 久久月本道色综合久久| 无码A级毛片免费视频下载| 亚洲天堂男人影院| 久久99日韩国产精品久久99| 亚在线观看免费视频入口| 资源县| 蜜芽久久人人超碰爱香蕉| 久久综合国产色美利坚| 国产精品国产三级国快看| 少妇无码av无码专区| 精品久久久久久无码免费 | 亚洲人成人无码www| 国产一区二区三区禁18| 久久精品国产福利一区二区| 精品人妻系列无码人妻漫画| 久操热在线视频免费观看| 精品无码久久久久国产电影| 99久久亚洲综合精品网| 静海县| 久久精品国产91精品亚洲| 波多野结衣乳喷高潮视频 | 国产精品日韩专区第一页| 国产永久免费高清在线观看| 一区二区三区四区高清自拍|