当前位置: 首页 > news >正文

个人网站怎么建立步骤北大青鸟

个人网站怎么建立步骤,北大青鸟,个人适合做跨境电商吗,房产管理局信息查询入口目录 光照原理 光源类型 平行光 点光源 环境光 反射类型 漫反射 漫反射光颜色 计算公式 环境反射 环境反射光颜色 表面的反射光颜色(漫反射和环境反射同时存在时)计算公式 平行光下的漫反射 根据光线和法线方向计算入射角θ(以便…

目录

光照原理

光源类型

平行光

点光源

环境光

反射类型

漫反射

漫反射光颜色 计算公式

环境反射

环境反射光颜色

表面的反射光颜色(漫反射和环境反射同时存在时)计算公式

平行光下的漫反射

根据光线和法线方向计算入射角θ(以便求两者点积:cosθ)

归一化

法线:表面的朝向

一个表面具有两个法向量

平面的法向量唯一

示例代码——平行光漫反射(LightedCube.js)

示例效果

代码详解 

顶点着色器部分

JavaScript程序部分

环境光下的漫反射

示例代码——平行光漫反射+环境反射(LightedCube_ambient.js)

示例效果


光照原理

现实世界中的物体被光线照射时,会反射一部分光。只有当反射光线进入你的眼睛时,你才能够看到物体并辩认出它的颜色。比如,白色的盒子会反射白光,当白光进入你的眼睛时,你才能看到盒子是白色的。

在现实世界中,当光线照射到物体上时,发生了两个重要的现象(见下图):

● 根据光源和光线方向,物体不同表面的明暗程度变得不一致。

● 根据光源和光线方向,物体向地面投下了影子。

在生活中,你可能常常会注意到阴影,却很少注意到明暗差异。实际上正是明暗差异给了物体立体感,虽然难以察觉,但它始终存在。虽然上图所示的立方体是纯白色的,但我们还是能够辨认它的每个面,因为它的每个面受到光照的程度不同。如你所见,向着光的表面看上去明亮一些,而侧着光或背着光的表面看上去就暗一些。正是有了这些差异,立方体看上去才真正像一个立方体。 

在三维图形学中术语着色(shading)的真正含义就是,根据光照条件重建“物体各表面明暗不一的效果”的过程。物体向地面投下影子的现象,又被称为阴影(shadowing)。

在讨论着色过程之前,考虑两件事:

● 发出光线的光源的类型。

● 物体表面如何反射光线。

在开始编写代码之前,我们先来理解一下上述两个问题。

光源类型

当物体被光线照射时,必然存在发出光线的光源。真实世界中的光主要有两种类型:平行光(directional light),类似于自然中的太阳光;点光源光(point light),类似于人造灯泡的光。此外,我们还用环境光(ambient light)来模拟真实世界中的非直射光(也就是由光源发出后经过墙壁或其他物体反射后的光)。三维图形学还使用一些其他类型的光,比如用聚光灯(spot light)来模拟电筒、车前灯等。至于其他的更加特殊的光源类型,可以参考OpenGL ES 2.0 Programming Guide一书

平行光

 顾名思义,平行光的光线是相互平行的,平行光具有方向。平行光可以看作是无限远处的光源(比如太阳)发出的光。因为太阳距离地球很远,所以阳光到达地球时可以认为是平行的。平行光很简单,可以用一个方向和一个颜色来定义

点光源

点光源光是从一个点向周围的所有方向发出的光。点光源光可以用来表示现实中的灯泡、火焰等。我们需要指定点光源的位置和颜色。光线的方向将根据点光源的位置和被照射之处的位置计算出来,因为点光源的光线的方向在场景内的不同位置是不同的。

环境光

环境光(间接光)是指那些经光源(点光源或平行光源)发出后,被墙壁等物体多次反射,然后照到物体表面上的光。环境光从各个角度照射物体,其强度都是一致的。比如说,在夜间打开冰箱的门,整个厨房都会有些微微亮,这就是环境光的作用。环境光不用指定位置和方向,只需要指定颜色即可。

现在,你已经了解了三种主要的光源类型,下面来讨论物体表面反射光线的几种方式。

反射类型

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

物体表面反射光线的方式有两种:漫反射(diffuse reflection)和环境反射(enviroment/ambient reflection)。本节的重点是如何根据上述两种信息(入射光和物体表面特性)来计算出反射光的颜色。本节会涉及一些简单的数学计算。

漫反射

漫反射是针对平行光或点光源而言的。漫反射的反射光在各个方向上是均匀的,如下图所示。如果物体表面像镜子一样光滑,那么光线就会以特定的角度反射出去;但是现实中的大部分材质,比如纸张、岩石、塑料等,其表面都是粗糙的,在这种情况下反射光就会以不固定的角度反射出去。漫反射就是针对后一种情况而建立的理想反射模型。

在漫反射中,反射光的颜色取决于入射光的颜色、表面的基底色、入射光与表面形成的入射角。我们将入射角定义为入射光与表面的法线形成的夹角,并用θ表示,那么漫反射光的颜色可以根据下式计算得到: 

漫反射光颜色 计算公式

<漫反射光颜色>=<入射光颜色>×<表面基底色>× cosθ

式子中,<入射光颜色>指的是点光源或平行光的颜色,乘法操作是在颜色矢量上逐分量(R、G、B)进行的。因为漫反射光在各个方向上都是“均匀”的,所以从任何角度看上去其强度都相等,如下图所示。 

漫反射光各方向均匀

环境反射

环境反射是针对环境光而言的。在环境反射中,反射光的方向可以认为就是入射光的反方向。由于环境光照射物体的方式就是各方向均匀、强度相等的,所以反射光也是各向均匀的,如下图所示。我们可以这样来描述它: 

环境反射光颜色

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

这里的<入射光颜色>实际上也就是环境光的颜色。

当漫反射和环境反射同时存在时,将两者加起来,就会得到物体最终被观察到的颜色:

表面的反射光颜色(漫反射和环境反射同时存在时)计算公式

<表面的反射光颜色>=<漫反射光颜色>+<环境反射光颜色>

注意,两种反射光并不一定总是存在,也并不一定要完全按照上述公式来计算。渲染三维模型时,你可以修改这些公式以达到想要的效果。 

下面来建立一个示例程序,在合适的位置放置一个光源,对场景进行着色。首先实现平行光下的漫反射。

平行光下的漫反射

如前所述,漫反射的反射光,其颜色与入射光在入射点的入射角θ有关。平行光入射产生的漫反射光的颜色很容易计算,因为平行光的方向是唯一的,对于同一个平面上的所有点,入射角是相同的。根据等式——漫反射光颜色 计算平行光入射的漫反射光颜色。

<漫反射光颜色>=<入射光颜色>×<表面基底色>× cosθ

上式用到了三项数据:

● 平行入射光的颜色

● 表面的基底色

● 入射光与表面形成的入射角θ

入射光的颜色可能是白色的,比如阳光;也可能是其他颜色的,比如隧道中的橘黄色灯光。我们知道颜色可以用RGB值来表示,比如标准强度的白光颜色值就是(1.0,1.0,1.0)。物体表面的基底色其实就是“物体本来的颜色”(或者说是“物体在标准白光下的颜色”)。按照上式公式计算反射光颜色时,我们对RGB值的三个分量逐个相乘。 

假设入射光是白色(1.0,1.0,1.0),而物体表面的基底色是红色(1.0,0.0,0.0),而入射角θ为0.0(即入射光垂直入射),根据上式,入射光的红色分量R为1.0,基底色的红色分量R为1.0,入射角余弦值cosθ为1.0,那么反射光的红色分量R就可以有如下计算得到:

R=1.0*1.0*1.0=1.0

类似地,我们可以算出绿色分量G和蓝色分量B:

G=1.0*0.0*1.0=0.0

B=1.0*0.0*1.0=0.0

根据上面的计算,当白光垂直入射到红色物体的表面时,漫反射光的颜色就变成了红色(1.0,0.0,0.0)。而如果是红光垂直入射到白色物体的表面时,漫反射光的颜色也会是红色。在这两种情况下,物体在观察者看来就是红色的,这很符合我们在现实世界中的经验。

那么如果入射角θ是90度,也就是说入射光与表面平行(90度相当于没有没有照射到物体),一点都没有“照射”到表面上,在这种情况下会怎样呢?根据我们在现实世界中的经验,物体表面应该完全不反光,看上去是黑的。验证一下:当θ是90度的时候,cosθ的值是0,那么根据上面的式子,不管入射光的颜色和物体表面基底色是什么,最后得到的漫反射光颜色都为(0.0,0.0,0.0),也就是黑色,正如我们预期的那样。同样,如果θ是60度,也就是斜射平行光斜射到物体表面上,那么该表面应该还是红色的,只不过比垂直入射时暗一些。根据上式,cosθ是0.5,漫反射光颜色为(0.5,0.0,0.0),即暗红色。 

这个简单的例子帮助你了解了如何计算漫反射光的颜色。但是我们并不知道入射角θ是多少,只知道光线的方向。下面我们就来通过光线和物体表面的方向来计算入射角θ,将漫反射光颜色公式中的θ换成我们更加熟悉的东西。

根据光线和法线方向计算入射角θ(以便求两者点积:cosθ)

在程序中,我们没法像前一节最后那样,直接说“入射角θ是多少多少度”。我们必须根据入射光的方向和物体表面的朝向(即法线方向)来计算出入射角。这并不简单,因为在创建三维模型的时候,我们无法预先确定光线将以怎样的角度照射到每个表面上。但是,我们可以确定每个表面的朝向。在指定光源的时候,再确定光的方向,就可以用这两项信息来计算出入射角了。

幸运的是,我们可以通过计算两个矢量的点积,来计算这两个矢量的夹角余弦值cosθ(两个矢量的点积等同于两个矢量归一化后的夹角的cos值)。点积运算的使用非常频繁,GLSL ES内置了点积运算函数。在公式中,我们使用点符号·来表示点积运算(进行点积运算前需对矢量归一化,即长度为1)。这样,cosθ就可以通过下式计算出来:

cosθ =<光线方向>·<法线方向>

因此,上述漫反射光颜色公式可以改写成下式,如下所示: 

<漫反射光颜色>=<入射光颜色>×<表面基底色>×(<光线方向>·<法线方向>)

这里有两点需要注意:其一,光线方向矢量和表面法线矢量的长度必须为1,否则反射光的颜色就会过暗或过亮。将一个矢量的长度调整为1,同时保持方向不变的过程称之为归一化(normalization) 。GLSL ES提供了内置的归一化函数,你可以直接使用。

归一化

比如矢量n为(nx, ny, nz),则其长度为|n| = (nx平方 + ny平方 + nz平方)的开方

对矢量n进行归一化后的结果是(nx/m, ny/m, nz/m),式中m为n的长度,比如,矢量(2.0, 2.0, 1.0)的长度|n| = sqrt(9) = 3,那么其归一化之后就是(2.0/3.0, 2.0/3.0, 1.0/3.0)

其二,这里(包括后面)所谓的“光线方向”,实际上是入射方向的反方向,即从入射点指向光源方向(因为这样,该方向与法线方向的夹角才是入射角),如下图所示。

这里用到了表面的法线方向来参与对θ的计算,可是我们还不知道法线方向,下一节就来研究如何获取表面的法线方向。 

法线:表面的朝向

物体表面的朝向,即垂直于表面的方向,又称法线或法向量。法向量有三个分量,向量(nx,ny,nz)表示从原点(0,0,0)指向点(nx,ny,nz)的方向。比如说,向量(1,0,0)表。示x轴正方向,向量(0,0,1)表示z轴正方向。涉及到表面和法向量的问题时,必须考虑以下两点:

一个表面具有两个法向量

在三维图形学中,表面的正面和背面取决于绘制表面时的顶点顺序。当你按照v0,v1,v2,v3的顶点顺序绘制了一个平面,那么当你从正面观察这个表面时,这4个顶点是顺时针的,而你从背面观察该表面,这4个顶点就是逆时针的(即第3章中用来确定旋转方向的“右手法则”)。如上图所示,该平面正面的法向量是(0,0,-1)。 

平面的法向量唯一

由于法向量表示的是方向,与位置无关,所以一个平面只有一个法向量。换句话说,平面的任意一点都具有相同的法向量。

进一步来说,即使有两个不同的平面,只要其朝向相同(也就是两个平面平行),法向量也相同。比方说,有一个经过点(10,98,9)的平面,只要它垂直于z轴,它的法向量仍然是(0,0,1)和(0,0,-1),和经过原点并垂直于z轴的平面一样,如图。

下图(左)显示了示例程序中的立方体及每个表面的法向量。比如立方体表面上的法向量表示为n(0,1,0)。 

一旦计算好每个平面的法向量,接下来的任务就是将数据传给着色器程序。以前的程序把颜色作为“逐顶点数据”存储在缓冲区中,并传给着色器。对法向量数据也可以这样做。如上图(右)所示,每个顶点对应3个法向量,就像之前每个顶点都对应3个颜色值一样 

示例程序LightedCube显示了一个处于白色平行光照射下的红色三角形,看下面代码

示例代码——平行光漫反射(LightedCube.js)

var VSHADER_SOURCE =  // p288'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'attribute vec4 a_Normal;\n' +        // 法向量'uniform mat4 u_MvpMatrix;\n' +'uniform vec3 u_LightColor;\n' +     // 光线颜色'uniform vec3 u_LightDirection;\n' + // 归一化的世界坐标'varying vec4 v_Color;\n' +'void main() {\n' +'  gl_Position = u_MvpMatrix * a_Position ;\n' +// 对法向量进行归一化'  vec3 normal = normalize(a_Normal.xyz);\n' +// 计算光线方向和法向量的点积(即两者归一化后的夹角的余弦值:cosθ)'  float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' +// 计算漫反射光的颜色(入射光颜色 * 表面基底色 * cosθ)'  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +'  v_Color = vec4(diffuse, a_Color.a);\n' +'}\n';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';function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return// 设置顶点的坐标、颜色和法向量var n = initVertexBuffers(gl);// 设置清除颜色并启用深度测试gl.clearColor(0, 0, 0, 1);gl.enable(gl.DEPTH_TEST);// 获取统一变量的存储位置等等var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');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); // 见cuon-matrix// 计算模型视图投影矩阵var mvpMatrix = new Matrix4();    // 模型视图投影矩阵mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); // 计算投影矩阵mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); // 计算视图矩阵// 将模型视图投影矩阵传给u_MvpMatrix变量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) {// Create a cube//    v6----- v5//   /|      /|//  v1------v0|//  | |     | |//  | |v7---|-|v4//  |/      |///  v2------v3var vertices = new Float32Array([   // 顶点坐标1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0, // v0-v1-v2-v3 front1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0, // v0-v3-v4-v5 right1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0, // v0-v5-v6-v1 up-1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0, // v1-v6-v7-v2 left-1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0, // v7-v4-v3-v2 down1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0  // v4-v7-v6-v5 back]);var colors = new Float32Array([    // 颜色1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0     // v4-v7-v6-v5 back]);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,  // v0-v1-v2-v3 front1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up-1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back]);// 顶点的索引var indices = new Uint8Array([0, 1, 2,   0, 2, 3,    // front4, 5, 6,   4, 6, 7,    // right8, 9,10,   8,10,11,    // up12,13,14,  12,14,15,    // left16,17,18,  16,18,19,    // down20,21,22,  20,22,23     // back]);// 将顶点属性写入缓冲区(坐标、颜色和法线)if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;var indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);return indices.length;
}function initArrayBuffer (gl, attribute, data, num, type) {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);gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);gl.enableVertexAttribArray(a_attribute);gl.bindBuffer(gl.ARRAY_BUFFER, null);return true;
}

示例效果

代码详解 

注意,顶点着色器实现了漫反射光颜色公式

<漫反射光颜色>=<入射光颜色>×<表面基底色>×(<光线方向>·<法线方向>)

计算漫反射光颜色需要:(1)入射光颜色,(2)表面基底色,(3)入射光方向,(4)表面法线方向。其中后两者都必须是归一化的(即长度为1.0)。 

顶点着色器部分

顶点着色器中的a_Color变量表示表面基底色(第3行),a_Normal变量表示表面法线方向(第4行),u_LightColor变量表示入射光颜色(第6行),u_LightDirection变量表示入射光方向(第7行)。注意,入射光方向u_LightDirection是在世界坐标系下的,而且在传入着色器前已经在JavaScript中归一化了。这样,我们就可以避免在顶点着色器每次执行时都对它进行归一化。

有了这些信息,就可以开始在顶点着色器中进行计算了。首先,对a_Normal进行归一化(第12行)。严格地说,本例通过缓冲区传入的法向量都是已经归一化过的,所以实际上这一步可以略去。但是顶点着色器可不知道传入的矢量是否经过了归一化,而且这里没有节省开销的理由(法向量是逐顶点的),所以,有这一步总比没有要好: 

a_Normal变量是vec4类型的,使用前三个分量x、y和z表示法线方向,所以我们将这三个分量提取出来进行归一化。对vec3类型的变量进行归一化就不必这样做。本例使用vec4类型的a_Normal变量是为了方便对下一个示例程序进行扩展。GLSL ES提供了内置函数normalize()对矢量参数进行归一化。归一化的结果赋给了vec3类型的normal变量,供之后使用。 

接下来,根据漫反射光颜色公式计算点积<光线方向>·<法线方向>。光线方向存储在u_LightDirection变量中,而且已经被归一化了,可以直接使用。法线方向存储在之前进行归一化后的结果normal变量中(第12行)。使用GLSL ES提供的内置函数dot()计算两个矢量的点积<光线方向>·<法线方向>,该函数接收两个矢量作为参数,返回它们的点积(第14行)。

如果点积大于0,就将点积赋值给nDotL变量,如果其小于0,就将0赋给该变量。使用内置函数max()完成这个任务,将点积和0两者中的较大者赋值给nDotL。

点积值小于0,意味着cosθ中的θ大于90度。θ是入射角,也就是入射反方向(光线方向)与表面法向量的夹角,θ大于90度说明光线照射在表面的背面上,如图8.11所示。此时,将nDotL赋为0.0。

现在准备工作都已经就绪了,我们在顶点着色器中直接计算漫反射颜色公式第16行)。注意a_Color变量即顶点的颜色,被从vec4对象转成了vec3对象,因为其第4个分量(透明度)与漫反射颜色公式无关。 

实际上,物体表面的透明度确实会影响物体的外观。但这时光照的计算较为复杂,现在暂时认为物体都是不透明的,这样就计算出了漫反射光的颜色diffuse:

然后,将diffuse的值赋给v_Color变量(第17行)。v_Color是vec4对象,而diffuse是vec3对象,需要将第4分量补上为1.0。 

顶点着色器运行的结果就是计算出了v_Color变量,其值取决于顶点的颜色、法线方向、平行光的颜色和方向。v_Color变量将被传入片元着色器并赋值给gl_FragColor变量。本例中的光是平行光,所以立方体上同一个面的颜色也是一致的,没有之前出现的颜色渐变效果。 

这就是顶点着色器的代码,下面来看一下JavaScript程序如何将数据传给顶点着色器并计算式漫反射光颜色公式。

JavaScript程序部分

JavaScript将光的颜色u_LightColor和方向u_LightDirection传给顶点着色器。首先用gl.uniform3f()函数将u_LightColor赋值为(1.0,1.0,1.0),表示入射光是白光:

下一步是设置光线方向,注意光线方向必须被归一化。cuon-matrix.js为Vector3类型提供了normalize()函数,以实现归一化。该函数的用法非常简单:在你想要进行归一化的Vector3对象上调用normalize()函数即可(第49行)。注意JavaScript和GLSL ES中对矢量进行归一化的不同之处。

归一化后的光线方向以Float32Array类型的形式存储在lightDirection对象的elements属性中,使用gl.uniform3fv()将其分配给着色器中的u_LightDirection变量(第50行)。 

最后,在initVertexBuffers()函数中为每个顶点定义法向量,法向量数据存储在normals数组中(第92行),然后被initArrayBuffer()函数(第115行)传给了顶点着色器的a_Normal变量。

initArrayBuffer()函数的作用是将第3个参数指定的数组(normals)分配给第2个参数指定的着色器中的变量。

环境光下的漫反射

现在,我们已经成功实现了平行光下的漫反射光,LightedCube的效果如下图所示。但是下图和现实中的立方体还是有点不大一样,特别是右侧表面是全黑的,仿佛不存在一样。

虽然程序是严格按照 等式:漫反射光颜色 对场景进行光照的,但经验告诉我们肯定有什么地方不对劲。在现实世界中,光照下物体的各表面的差异不会如此分明:那些背光的面虽然会暗一些,但绝不至于黑到看不见的程度。实际上,那些背光的面是被非直射光(即其他物体,如墙壁的反射光等)照亮的,前面提到的环境光就起到了这部分非直射光的作用,它使场景更加逼真。因为环境光均匀地从各个角度照在物体表面,所以由环境光反射产生的颜色只取决于光的颜色和表面基底色,使用 等式:环境反射光颜色 计算后我们再来看一下: 

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

接下来,向示例程序中加入上式中的环境光所产生的反射光颜色,如 等式:表面的反射光颜色(漫反射和环境反射同时存在)所示: 

<表面的反射光颜色>=<漫反射光颜色>+<环境反射光颜色>

环境光是由墙壁等其他物体反射产生的,所以环境光的强度通常比较弱。假设环境光是较弱的白光(0.2,0.2,0.2),而物体表面是红色的(1.0,0.0,0.0)。根据 等式:环境反射光颜色,由环境光产生的反射光颜色就是暗红色(0.2,0.0,0.0)。同样,在蓝色的房间中,环境光为(0.0,0.0,0.2),有一个白色的物体,即表面基底色为(1.0,1.0,1.0),那么由环境光产生的漫反射光颜色就是淡蓝色(0.0,0.0,0.2)。 

示例程序LightedCube_ambient实现了环境光漫反射的效果,如下所示。可见,完全没有被平行光照到的表面也不是全黑,而是呈现较暗的颜色,与真实世界更加相符。

示例代码——平行光漫反射+环境反射(LightedCube_ambient.js)

示例程序代码大部分与LightedCube一样,只有少量修改,如下所示。

顶点着色器中新增了u_AmbientLight变量(第10行)用来接收环境光的颜色值。接着根据式8.2,使用该变量和表面的基底色a_Color计算出反射光的颜色,将其存储在ambient变量中(第21行)。这样我们就即有环境光反射产生的颜色ambient,又有了由平行光漫反射产生的颜色diffuse。最后根据 等式:表面的反射光颜色(漫反射和环境反射同时存在)计算物体最终的颜色(第23行)并存储在v_Color变量中,作为物体表面最终显示出的颜色,和LightedCube一样。 

如你所见,与LightedCube相比,本例对顶点着色器的v_Color变量加上了ambient变量(第23行),就使得整个立方体变亮了一些,这正是环境光从各个方向均匀照射在立方体上产生的。

示例效果

http://www.zhongyajixie.com/news/28230.html

相关文章:

  • 做网站横幅的软件安徽seo
  • 网站开发套餐google play商店
  • 网站制作要花多少钱最新国内重大新闻
  • wordpress酷黑主题整站优化深圳
  • 室内设计网站平台军事新闻头条最新消息
  • 哪些做展架图的网站好seo点击软件哪个好用
  • wordpress迁移到laravel上海网络优化seo
  • 个人网站建设站长之家最新网站
  • 易雅达网站建设公司宁波seo教程推广平台
  • 西北建设有限公司官方网站网站推广的作用在哪里
  • 关于网站开发中网站上传网站流量分析工具
  • 腾讯云官网入口武汉网站开发公司seo
  • 网站服务器基本要素有哪些八百客crm登录入口
  • 网站做不做301目前好的推广平台
  • 爱南宁app信息查看在哪里seo如何提高排名
  • 我做钓鱼网站自首了武汉seo优化服务
  • 武汉手机网站建设公司哪家好如何做互联网营销推广
  • 金融中介做网站需要培训机构排名全国十大教育机构排名
  • 做网站后台都要自己写吗免费的关键词优化工具
  • 做网站 要域名 主机 还有啥站长网站查询工具
  • 商丘建设委员会官方网站个人博客搭建
  • 企业网站策划论文发稿推广
  • 杭州网站建设费用学电商出来一般干什么工作
  • 免费微信建站有哪些网站湖南专业的关键词优化
  • 网站建设seo 视频游戏代理怎么找渠道
  • 做视频网站需要什么架构宁波seo网站排名
  • 大连网络备案做网站汕头网站推广
  • 花都网站制作公司影视剪辑培训机构排名
  • 芜湖网站建设怎么做家电企业网站推广方案
  • 平面设计专业网站郑州优化公司有哪些