8-光照

一、学习要点

本章主要讨论光照问题,如何在三维场景中实现不同类型的光照以及其产生的效果。

  1. 明暗、阴影、不同类型的光 : 点光源光,平行光和散射光。
  2. 物体表面反射光线的方式: 漫反射和环境反射
  3. 编写代码实现光照效果,使三维模型更逼真

二、光照原理

现实世界中物体被光照射时会反着一部分光。只有当反射光线进入你的眼睛是,你才能看到并辨认出他的颜色。

在现实世界中,当光照射到物体上时,发生了两个重要的现象。

  • 根据光源和光线的方向,物体不同表面的明暗程度变得不一致。
  • 根据光源和光线的方向,物体向地面投下影子。

微信图片_20190507161255.png

现实生活中我们会注意到阴影,却很少关注明暗差异。实际上敏感差异给了我们物体立体感。

在三维图形学中术语 着色(shading) 的真生含义就是,根据光照条件重建“物体各个表面明暗不一的效果”的过程。

在讨论着色之前我们考虑两件事情:

  • 光源的类型
  • 物体表面如何反射光线

光源类型

微信图片_20190507161852.png

  1. 平行光:顾名思义,平行光的管线是相互平行的,平行光具有方向。平行光可以看做是无限远处的光源(比如太阳)发出的光。平行光很简单,可以用一个方向和一个颜色来定义。
  2. 点光源:点光源光是从一个点想周围的所有方向发出的光。点光源光可以用来表示灯泡,火焰等。我们需要制定光源的位置和颜色。光线的方向根据点光源的位置和配照射指出的位置计算出来。因为点光源的光线方向在场景没不同位置是不同的。
  3. 环境光:(间接光) 指那些经光源发出后,被墙壁等物体多次反射然后找到物体表面的光。环境光从各个角度照射物体,其强度都是一致的。环境光只需指定颜色即可。

反射类型

物体向哪个方向反射光,反射光是什么颜色的,取决于以下两个因素: 入射光和物体表面的类型。入射光的信息包括入射光的方向和颜色,二物体表面信息包括物体的固有颜色和反射特性。

  1. 漫反射:漫反射针对于平行光或者点光源而言。漫反射的繁盛光在各个方向上是均匀的。漫反射的反射光的颜色取决于入射光的颜色、反射表面的基底色、入射光与表面行车的入射角。 微信图片_20190507163210.png

其计算公式为 <漫反射光颜色> = <入射光颜色> * <表面基底色> * cosθ

  1. 环境反射:在环境反射中,反射的方向可以认为就是入射光的反方向。 微信图片_20190507170258.png 其计算公式为 <漫反射光颜色> = <入射光颜色> * <表面基底色> * cosθ

三、平行光下的漫反射

<漫反射光颜色> = <入射光颜色> * <表面基底色> * cosθ 比如 白色的光找到一个红色额基底色上,如果是垂直照射 cosθ = 1.0

R = 1.0 * 1.0 * 1.0;

G = 1.0 * 0.0 * 1.0;

B = 1.0 * 0.0 * 1.0; 计算得出还是红色, 这是符合我们现实生活的。看起来计算并不困难。 但是实际中我们不知道入射角θ 只能知道光线的方向。

根据光线和表面的方向计算入射角

在程序中,我们没办法想前一届最后直接说入射角度是多少。我们必须根据入射光的方向和物体表面的朝向(即法线方向)来计算出入射角。这并不简单, 因为在创建三维模型的时候,我们无法预先确定光线将以怎样的角度照射到每个表面上。但是我们可以确定每个表面的朝向。 我们可以通过计算两个矢量的点积,来计算着两个矢量的夹角的余璇值cosθ 所以公式可以改成 <漫反射光颜色> = <入射光颜色> * <表面基底色> * (<光线方向> . <法线方向>) 这里要注意:

  1. 的是光线方向和法线方向的长度必须为1,否则反射光的颜色会过暗或者过亮。将一个矢量长度调整为1 的过程称为归一化。
  2. 光线方向实际上是入射方向的反方向。即从如何点只想光源方向。 那又有个疑问,法线方向如何得到呢?

法线: 表面的朝向

物体表面的朝向,即垂直于表面的方向,又称法线或者法向量。涉及到表面和法向量的问题时,必须考虑一下两点:

  1. 一个表面具有两个法向量,每个面都有正反两面,即有正反两个法向量 微信图片_20190507173823.png

在三维图形学中,表面的正面和背面取决于绘制表面时的顶点顺序。当你按照v0,v1,v2,v3的顶点顺序绘制了一个平面,那么当你从正面观察这个表面时,这四个顶点是顺镇的,而你从背面观察该表面,这4个顶点是逆时针的。(右手法则)上图平面的整法向量是(0,0,-1) 2. 平面的法向量唯一,由于法向量表示的是方向,与位置无关,所以一个平面只有一个法向量。换句话说 屏慢的任意一点都具有相同的法向量。 下图展示立方体的法向量

微信图片_20190507174801.png

一旦计算好每个平面的法向量,接下来的任务就是将数据传给着色器程序。以前的程序把颜色作为“逐顶点数据”处处在缓冲区中,并传给着色器。对法向量数据也可以这样做。每个顶点对应3个法向量。

// 示例程序 一个光照下的红色盒子
// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform vec3 u_LightColor;
        uniform vec3 u_LightDirection;
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            vec3 normal = normalize(vec3(a_Normal));
            float nDotL = max(dot(u_LightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;
            v_Color = vec4(diffuse,a_Color.a);
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    varying vec4 v_Color;
    void main() {
        gl_FragColor = v_Color;
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightDirection = gl.getUniformLocation(gl.program,'u_LightDirection');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置光线方向(世界坐标系下的)
    var lightDirection = new Vector3([0.5,3.0,4.0]);
    lightDirection.normalize(); // 归一化
    gl.uniform3fv(u_LightDirection,lightDirection.elements);

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    mvpMatrix.set(projMatrix).multiply(viewMatrix);
    // 将试图矩阵和投影矩阵传递给参数
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);
    // 开启偏移
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}


function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}

max(dot(u_LightDirection,normal), 0.0); 顶点着色器中 取了点积和0的最大值,表示如果光照来自于表面的背面,我们现在认为表面时完全不透光的。

四、环境光下的漫反射

我们发现 正方体还是有些和我们实际的生活中的不太一样,比如右侧的面全部都是黑色的。这不符合我们实际的情况。这其实是因为现实生活中除了直射光的影响,环境中的折射光也会对物体的颜色产生影响

<环境反射光颜色> = <入射光颜色> * <表面基底色>

<表面的反射光颜色> = <漫反射光颜色> + <表面基底色>

// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform vec3 u_LightColor;
        uniform vec3 u_LightDirection;
        uniform vec3 u_AmbientLight; 
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            vec3 normal = normalize(vec3(a_Normal));
            float nDotL = max(dot(u_LightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;
            vec3 ambient = u_AmbientLight * a_Color.rgb;
            
            v_Color = vec4(diffuse + ambient,a_Color.a);
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    varying vec4 v_Color;
    void main() {
        gl_FragColor = v_Color;
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightDirection = gl.getUniformLocation(gl.program,'u_LightDirection');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置环境光
    gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
    // 设置光线方向(世界坐标系下的)
    var lightDirection = new Vector3([0.5,3.0,4.0]);
    lightDirection.normalize(); // 归一化
    gl.uniform3fv(u_LightDirection,lightDirection.elements);

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    mvpMatrix.set(projMatrix).multiply(viewMatrix);
    // 将试图矩阵和投影矩阵传递给参数
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);
    // 开启偏移
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}


function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}

五、运动物体的光照效果

立方体旋转时 每个表面的法向量也会随之变化。由下午我们发现

  • 平移不会使得法向量变化,
  • 而旋转会引起法向量的变化。
  • 缩放变化比较复杂 有时引起 有时不引起。 微信图片_20190508144718.png

那这个法向量的变化怎么计算得到呢?

魔法矩阵:你转置矩阵

变化之后的法向量 = 变化之前的法向量 * 模型矩阵的 逆转置矩阵

逆转置矩阵:

  1. 求原矩阵的逆矩阵。
  2. 将上一步求得的逆矩阵进行转置。

微信图片_20190508145615.png

// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform mat4 u_NormalMatrix;
        uniform vec3 u_LightColor;
        uniform vec3 u_LightDirection;
        uniform vec3 u_AmbientLight; 
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
            float nDotL = max(dot(u_LightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;
            vec3 ambient = u_AmbientLight * a_Color.rgb;
            v_Color = vec4(diffuse + ambient,a_Color.a);
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    varying vec4 v_Color;
    void main() {
        gl_FragColor = v_Color;
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightDirection = gl.getUniformLocation(gl.program,'u_LightDirection');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置环境光
    gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
    // 设置光线方向(世界坐标系下的)
    var lightDirection = new Vector3([0.5,3.0,4.0]);
    lightDirection.normalize(); // 归一化
    gl.uniform3fv(u_LightDirection,lightDirection.elements);

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 逆转置矩阵
    var normalMatrix = new Matrix4();
    // 模型矩阵
    var modelMatrix = new Matrix4();

    modelMatrix.setTranslate(0,0.5,0);// 沿Y轴平移
    modelMatrix.rotate(90,0,0,1);// 沿Z轴平移



    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
    // 将试图矩阵和投影矩阵传递给参数
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);


    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

    // 开启偏移
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}
function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}

这个正方体除了向上移动了一点好像没什么变化,其实主要是因为正好旋转了90度的原因。

六 点光源光

与平行光想必,点光源发出的光,在三维空间的不同位置上期方向也不同。所以在对点光源光下的物体进行着色时,需要每个入射点计算点光源光在该处的方向。

// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform mat4 u_ModelMatrix;
        uniform mat4 u_NormalMatrix;
        uniform vec3 u_LightColor;
        uniform vec3 u_LightPosition;
        uniform vec3 u_AmbientLight; 
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
            vec4 vertexPosition = u_ModelMatrix * a_Position;
            vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));
            float nDotL = max(dot(lightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;
            vec3 ambient = u_AmbientLight * a_Color.rgb;
            
            v_Color = vec4(diffuse + ambient,a_Color.a);
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    varying vec4 v_Color;
    void main() {
        gl_FragColor = v_Color;
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');



    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightPosition = gl.getUniformLocation(gl.program,'u_LightPosition');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置环境光
    gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
    // 设置光线方向(世界坐标系下的)
    // var lightDirection = new Vector3([0.5,3.0,4.0]);
    // lightDirection.normalize(); // 归一化
    gl.uniform3f(u_LightPosition,2.0,3.0,3.0);

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 逆转置矩阵
    var normalMatrix = new Matrix4();
    // 模型矩阵
    var modelMatrix = new Matrix4();

    modelMatrix.setTranslate(0,0.5,0);// 沿Y轴平移
    modelMatrix.rotate(90,0,0,1);// 沿Z轴平移



    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
    // 将试图矩阵和投影矩阵传递给参数
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);


    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

    gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);
    // 开启偏移
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}


function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}

我们发现现在的效果更加逼真了, 可是还是感觉不太自然,因为我们是逐顶点计算光照找过的,正方体上的面上像素的颜色是根据顶点颜色来渐变出来的,现实世界中每个像素都是自己根据光照计算出自己的颜色的。 下面有这个正方体运动起来的示例可以比较明显的看出这一点。

// 运动的立方体
// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform mat4 u_ModelMatrix;
        uniform mat4 u_NormalMatrix;
        uniform vec3 u_LightColor;
        uniform vec3 u_LightPosition;
        uniform vec3 u_AmbientLight; 
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
            vec4 vertexPosition = u_ModelMatrix * a_Position;
            vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));
            float nDotL = max(dot(lightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;
            vec3 ambient = u_AmbientLight * a_Color.rgb;
            
            v_Color = vec4(diffuse + ambient,a_Color.a);
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    varying vec4 v_Color;
    void main() {
        gl_FragColor = v_Color;
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');
    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightPosition = gl.getUniformLocation(gl.program,'u_LightPosition');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置环境光
    gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
    // 设置点光源的位置
    gl.uniform3f(u_LightPosition,2.3,4.0,3.5);
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 逆转置矩阵
    var normalMatrix = new Matrix4();
    // 模型矩阵
    var modelMatrix = new Matrix4();
    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    var currentAngle = 0.0;
    var tick = function () {
        currentAngle = animate(currentAngle);
        modelMatrix.setTranslate(0,0.5,0);// 沿Y轴平移
        modelMatrix.rotate(currentAngle,0,1,0);// 沿Z轴平移
        mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
        // 将试图矩阵和投影矩阵传递给参数
        gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);
        normalMatrix.setInverseOf(modelMatrix);
        normalMatrix.transpose();
        gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);
        gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);
        // 开启偏移
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
        requestAnimationFrame(tick);
    }
    tick();
}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}


function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}
var ANGLE_STEP = 30.0;
var g_last = Date.now();
function animate(angle) {
 var now = Date.now();
 var elapsed = now - g_last;
 g_last = now;
 var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
 return newAngle %= 360;
}

七、更逼真 逐片元光照

我们只需要在片元着色器里面进行计算就可以。

// 顶点着色器
var VSHANER_SOURCE =
    `
        attribute vec4 a_Position;
        attribute vec4 a_Color;
        attribute vec4 a_Normal;
        uniform mat4 u_MvpMatrix;
        uniform mat4 u_NormalMatrix;
        uniform mat4 u_ModelMatrix;
        varying vec3 v_Normal;
        varying vec3 v_Position;
        varying vec4 v_Color;
        void main() {
            gl_Position =u_MvpMatrix * a_Position;
            v_Position = vec3(u_ModelMatrix * a_Position);
            v_Normal =  normalize(vec3(u_NormalMatrix * a_Normal));
            v_Color = a_Color;
        }
    `;
// 片元着色器
var FSHADER_SOURCE =
    `
    precision mediump float; // 需要声明浮点数精度否则报错
    uniform vec3 u_LightColor;
    uniform vec3 u_LightPosition;
    uniform vec3 u_AmbientLight; 
    varying vec3 v_Normal;
    varying vec3 v_Position;
    varying vec4 v_Color;
    void main() {
            vec3 normal = normalize(v_Normal);
            vec3 lightDirection = normalize(u_LightPosition - v_Position);
            float nDotL = max(dot(lightDirection,normal), 0.0);
            vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL;
            vec3 ambient = u_AmbientLight * v_Color.rgb;
            gl_FragColor = vec4(diffuse + ambient , v_Color.a );
    }
`
function main() {
    let canvas = document.getElementById('webgl');
    var gl = getWebGLContext(canvas);
    if(!gl){
        console.log('error');
        return;
    }
    // 初始化着色器
    if(!initShaders(gl,VSHANER_SOURCE,FSHADER_SOURCE)){
        console.log('error');
        return;
    }

    var n = initVertexBuffers(gl);
    if(n<0){
        console.log("error");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    // 开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');



    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_LightPosition = gl.getUniformLocation(gl.program,'u_LightPosition');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    // 设置光线颜色(白色)
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    // 设置环境光
    gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
    // 设置光线方向(世界坐标系下的)
    // var lightDirection = new Vector3([0.5,3.0,4.0]);
    // lightDirection.normalize(); // 归一化
    gl.uniform3f(u_LightPosition,2.0,3.0,3.0);

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
    // 混合投影
    var mvpMatrix = new Matrix4();
    // 逆转置矩阵
    var normalMatrix = new Matrix4();
    // 模型矩阵
    var modelMatrix = new Matrix4();

    modelMatrix.setTranslate(0,0.5,0);// 沿Y轴平移
    modelMatrix.rotate(90,0,0,1);// 沿Z轴平移



    // 设置视点 视线 和上方向
    var viewMatrix = new Matrix4();
    // 投影矩阵
    var projMatrix = new Matrix4();
    viewMatrix.setLookAt(3,3,7,0,0,0,0,1,0);
    projMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
    mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
    // 将试图矩阵和投影矩阵传递给参数
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);


    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

    gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);
    // 开启偏移
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

}

function initVertexBuffers(gl) {
    let v0  = [1.0,1.0,1.0];
    let v1  = [-1.0,1.0,1.0];
    let v2  = [-1.0,-1.0,1.0];
    let v3  = [1.0,-1.0,1.0];
    let v4  = [1.0,-1.0,-1.0];
    let v5  = [1.0,1.0,-1.0];
    let v6  = [-1.0,1.0,-1.0];
    let v7  = [-1.0,-1.0,-1.0];



    var vertices = new Float32Array([
        // 立方体的顶点坐标和颜色
        ...v0,...v1,...v2,...v3,
        ...v0,...v3,...v4,...v5,
        ...v0,...v5,...v6,...v1,
        ...v1,...v6,...v7,...v2,
        ...v2,...v3,...v4,...v7,
        ...v4,...v7,...v6,...v5
    ])

    // 顶点索引
    var indices = new Uint8Array([
        0,1,2,0,2,3, // 前
        4,5,6,4,6,7, // 右
        8,9,10,8,10,11, // 上
        12,13,14,12,14,15, // 左
        16,17,18,16,18,19, // 下
        20,21,22,20,22,23 // 后
    ])


    var color = new Float32Array([
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
    ])

    var normals = new Float32Array([
        // 法向量
        0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,
        1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,
        0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,
        -1.0,0.0,0.0,  -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,
        0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0,
        0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0
    ])

    // 创建缓冲区对象
    var indexBuffer = gl.createBuffer();
    if(!initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')){
        return -1;
    }
    if(!initArrayBuffer(gl,color,3,gl.FLOAT,'a_Color')){
        return -1;
    }
    if(!initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')){
        return -1;
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

    return indices.length;
}


function initArrayBuffer(gl,data,num,type,attribute) {
    // 创建缓冲区对象
    var buffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    var a_attribute = gl.getAttribLocation(gl.program,attribute);
    // 将缓冲区对象分配给 a_Position变量
    gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
    gl.enableVertexAttribArray(a_attribute);
    return true;
}

片元处的表面的法向量和片元的位置信息,是在从顶点缓冲区传递到片元缓冲区时webgl内部进行的插值计算好的。

这一张我们介绍了几种不同的光照类型和反射类型,讨论了如何为场景实现光照效果,下一章我们不再使用简单的正方体而是创建层次模型。