算法
两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
var addTwoNumbers = function(l1, l2) {
let curEle = result = new ListNode(0)
let carry = 0
while (l1 || l2 || carry) {
let sum = carry
if (l1) {
sum += l1.val
l1 = l1.next
}
if (l2) {
sum += l2.val
l2 = l2.next
}
carry = sum > 9 ? 1 : 0
curEle.next = new ListNode((sum) % 10)
curEle = curEle.next
}
return result.next
};
奇偶链表
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
var oddEvenList = function(head) {
if(!head || !head.next){
return head;
}
var odd = head;
var even = head.next;
var tmp = even;
while(even && even.next){
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = tmp
return head
};
相交链表
编写一个程序,找到两个单链表相交的起始节点。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
var getIntersectionNode = function(headA, headB) {
if(!headA || !headB) return null;
var countA = getListCount(headA);
var countB = getListCount(headB);
if (countA > countB) {
for (let i = countB; i < countA; i++) headA = headA.next;
} else if (countA < countB) {
for (let i = countA; i < countB; i++) headB = headB.next;
}
while(headA && headB){
if(headA === headB) return headA;
headA = headA.next;
headB = headB.next;
}
return null;
};
function getListCount(head){
var clone = head;
var count = 0;
while(clone){
++count;
clone = clone.next;
}
return count;
}
最佳实践:
let getIntersectionNode = function(headA, headB) {
if (!headA || !headB) return null;
let nodeA = headA;
let nodeB = headB;
while (nodeA !== nodeB) {
nodeA = nodeA === null ? headB : nodeA.next;
nodeB = nodeB === null ? headA : nodeB.next;
}
return nodeA;
}
中序遍历二叉树
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
var inorderTraversal = function(root) {
const result = [];
const inorder = node => {
if(!node) return;
inorder(node.left);
result.push(node.val);
inorder(node.right);
}
inorder(root);
return result;
};
三维立体世界
我们将观察者所处的位置称为视点,从视点出发沿着观察方向的射线称为视线。
- 视点:观察者所在的三维空间中位置,视线的七点
- 观察目标点:被观察目标所在的点。视线从视点出发,穿过观察目标点并继续延伸
- 上方向:最终绘制在屏幕上的影像中的向上的方向
通过设置视角矩阵设置视点的位置
//设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(-0.20, -0.25, -0.25, 0, -1, 0, 0, 1, 0);
再通过矩阵相乘的方式获得每个图形在途中应该显示的位置,gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ViewMatrix;
varying vec4 v_Color;
void main() {
gl_Position = u_ViewMatrix * a_Position;
gl_PointSize = 10.0;
v_Color = a_Color;
}
`;
var FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
var gl = canvas.getContext('webgl');
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
this.gl = gl;
var n = this.initVertexBuffers(gl)
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
//设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(-0.20, -0.25, -0.25, 0, -1, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//清空<canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
initVertexBuffers(gl){
var verticesColors = new Float32Array(
[
0.0, 0.5, -0.4, 0.4, 1.0, 0.4, // The back green one
-0.5, -0.5, -0.4, 0.4, 1.0, 0.4,
0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, 0.4, -0.2, 1.0, 0.4, 0.4, // The middle yellow one
-0.5, 0.4, -0.2, 1.0, 1.0, 0.4,
0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
0.0, 0.5, 0.0, 0.4, 0.4, 1.0, // The front blue one
-0.5, -0.5, 0.0, 0.4, 0.4, 1.0,
0.5, -0.5, 0.0, 1.0, 0.4, 0.4
]
);
var n = 9; //点的个数
//创建缓冲区对象
var verteColorBuffer = gl.createBuffer();
if (!verteColorBuffer) {
console.log("Failed to create thie buffer object");
return -1;
}
//将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, verteColorBuffer);
//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
var FSIZE = verticesColors.BYTES_PER_ELEMENT;
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;
}
//将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0);
//连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
if (a_Color < 0) {
console.log("Failed to get the storage location of a_Position");
return -1;
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
gl.bindBuffer(gl.ARRAY_BUFFER, null);//取消绑定的缓冲区对象
return n;
}
可视空间
可视空间分两种:正射投影和透视投影
正射投影中平行的光 照射到与其平行的物体就变成点或者线了,看不到平行物体的侧面
透视投影中平行的光实际上发散出去的(就像人眼看平行的铁轨,明明是平行的,但是透视效果确实两个平行铁轨越远越相较于某一点),实际的光是不平行的 当然就可以看到物体的侧面。
正射投影
正射投影是的可视空间由近裁剪面和远裁剪面决定。接近视点的面试近裁剪面,投影位置为远裁剪面,我们实际看到的内容就仅仅是可视空间中的内容
当我们不断调整近裁剪面和远裁剪面时,如果图形不在可视空间内之后就会看不见
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ProjMatrix;
varying vec4 v_Color;
void main() {
gl_Position = u_ProjMatrix * a_Position;
gl_PointSize = 10.0;
v_Color = a_Color;
}
`;
var FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
var gl = canvas.getContext('webgl');
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
this.gl = gl;
var n = this.initVertexBuffers(gl)
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
//设置视点、视线、上方向
var projMatrix = new Matrix4();
document.onkeydown = function (ev) {
keydown(ev, gl, n, u_ProjMatrix,projMatrix);
};
draw(gl, n, u_ProjMatrix, projMatrix);
var g_near = 0.0, g_far = 0.5;
function keydown(ev, gl, n, u_ProjMatrix, projMatrix) {
switch (ev.keyCode){
case 39: g_near += 0.01; break; //right
case 37: g_near -= 0.01; break; //left
case 38: g_far += 0.01; break; //up
case 40: g_far -=0.01; break; //down
default: return;
}
draw(gl, n, u_ProjMatrix, projMatrix);
}
function draw(gl, n, u_ProjMatrix, projMatrix) {
projMatrix.setOrtho(-1, 1, -1, 1, g_near, g_far);
//将视图矩阵传递给u_ViewMatrix变量
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
gl.clear(gl.COlOR_BUFFER_BIT);
console.log('near: ' + Math.round(g_near * 100)/100 + ', far: ' + Math.round(g_far * 100)/100)
gl.drawArrays(gl.TRIANGLES, 0, n);
}
透视投影
平行的视角看过去,物体没有那么真实,我们真实看到的世界一定是远小近大的,而不是平行的。透视投影也会有可视空间,同样的处于可视空间内的物体才会被显示出来。
但是透视投影近裁剪面比远裁剪面小,链接视点、近裁剪面、远裁剪面就会形成一个夹角,所以我们在定义透视投影时,一般使用,视角的夹角、近裁剪面的宽高比,视角距离近裁剪面和远裁剪面的距离
三个一样大小的三角形,从视角上看远近不同,大小也就不同,所以透视投影更贴近我们的视觉直观感受
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;
varying vec4 v_Color;
void main() {
gl_Position = u_ProjMatrix * u_ViewMatrix * a_Position;
gl_PointSize = 10.0;
v_Color = a_Color;
}
`;
var FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
var gl = canvas.getContext('webgl');
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
this.gl = gl;
var n = this.initVertexBuffers(gl)
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
//设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
initVertexBuffers(gl){
var verticesColors = new Float32Array(
[
// Three triangles on the right side
0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
0.25, -1.0, -4.0, 0.4, 1.0, 0.4,
1.25, -1.0, -4.0, 1.0, 0.4, 0.4,
0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
0.25, -1.0, -2.0, 1.0, 1.0, 0.4,
1.25, -1.0, -2.0, 1.0, 0.4, 0.4,
0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
0.25, -1.0, 0.0, 0.4, 0.4, 1.0,
1.25, -1.0, 0.0, 1.0, 0.4, 0.4,
// Three triangles on the left side
-0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
-1.25, -1.0, -4.0, 0.4, 1.0, 0.4,
-0.25, -1.0, -4.0, 1.0, 0.4, 0.4,
-0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
-1.25, -1.0, -2.0, 1.0, 1.0, 0.4,
-0.25, -1.0, -2.0, 1.0, 0.4, 0.4,
-0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
-1.25, -1.0, 0.0, 0.4, 0.4, 1.0,
-0.25, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
var n = 18; //点的个数
//创建缓冲区对象
var verteColorBuffer = gl.createBuffer();
if (!verteColorBuffer) {
console.log("Failed to create thie buffer object");
return -1;
}
//将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, verteColorBuffer);
//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
var FSIZE = verticesColors.BYTES_PER_ELEMENT;
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;
}
//将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0);
//连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
if (a_Color < 0) {
console.log("Failed to get the storage location of a_Position");
return -1;
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
gl.bindBuffer(gl.ARRAY_BUFFER, null);//取消绑定的缓冲区对象
return n;
}
层级关系
正常情况下 webgl 会根据先设置的图形挡住后面的图形,这是为了提高渲染效率,所以不会是离我们视点近的物体遮挡距离远的物体。
webgl 提供了隐藏面消除来解决这个问题。开启隐藏面消除,消除之后就会按照位置放置遮挡位置而不是先后顺序
gl.enable(gl.DEPTH_TEST)
gl.clear(gl.COLOR_BUFFER_BIT);
绘制立体图形
一个立方体由六个面组成,每个面是由2个三角组成,总共应该是由12个三角形组成,12个三角形又是由 8 个顶点组成,所以如果要完全由三角形去拼接的话,顶点的缓冲区就会很大,并且很多都是重复的点,所以绘制立方体时先将所有的点和颜色先存储在一个缓冲区,再用另一个数组存储索引的缓冲区,用索引构建所有的面,将一个顶点描述的数组,改成了一个数组+索引描述的两个数组,缓冲区复杂度会减小,可读性增加。
initVertexBuffers(gl){
var verticesColors = new Float32Array([
// Vertex coordinates and color
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v0 White
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0, // v1 Magenta
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, // v2 Red
1.0, -1.0, 1.0, 1.0, 1.0, 0.0, // v3 Yellow
1.0, -1.0, -1.0, 0.0, 1.0, 0.0, // v4 Green
1.0, 1.0, -1.0, 0.0, 1.0, 1.0, // v5 Cyan
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, // v6 Blue
-1.0, -1.0, -1.0, 0.0, 0.0, 0.0 // v7 Black
]);
// 顶点索引
var indices = new Uint8Array([ //(Uint8Array)是无符号8位整型数
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);
// 将顶点索引数据写入缓冲区对象
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_MvpMatrix;
varying vec4 v_Color;
void main() {
gl_Position = u_MvpMatrix * a_Position;
gl_PointSize = 10.0;
v_Color = a_Color;
}
`;
var FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
var gl = canvas.getContext('webgl');
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
this.gl = gl;
var n = this.initVertexBuffers(gl)
//设置视点、视线、上方向
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30, 1, 1 ,100);
mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);
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);
光照
我们所看到的物体需要具备立体效果需要有光源,造成阴影,才会有所呈现。光源分为三类:平行光、点光源、环境光
- 平行光:光线与光线之间是平行的
- 点光源:点光源光是从一个点向周围的所有方向发出的光
- 环境光:那些经光源发出后,被墙壁等物体多次反射,然后照到物体表面的光。
反射的类型分为漫反射和环境反射
- 漫反射:漫反射的反射光在各个方向上市均匀的,反射光的颜色取决于入射光的颜色、表面的基底色、入射光与表面形成的入射角
- 环境反射:认为被光照的物体的方式各方向均匀、强度相等,所以反射方向也是均匀的。取决于入射光的颜色和方向
法向量:垂直于表面的方向又称法线或者法向量,一个表面具有两个法向量,正面和背面
整个光照的计算就是不同种类的光照,根据不同的规则算出来的,平行光就是会取法向量与平行光之间的夹角。
<漫反射光颜色> = <入射光颜色> x <表面基底色> x (<光线方向与法线方向的夹角>) <环境反射光颜色> = <入射光颜色> x <表面基底色> <表面的反射光颜色> = <漫反射光颜色> + <环境反射光颜色> 如果单纯使用漫反射就不会那么自然,因为我们现实中看到的光照都会有周围环境对于光照的漫反射之后的照射,不可能存在空间中只有一个单独的物体。所以光照是需要漫反射 + 环境光反射进行叠加的 ### 漫反射光源 ``` var VSHADER_SOURCE = ` attribute vec4 a_Position; attribute vec4 a_Color; attribute vec4 a_Normal; uniform mat4 u_MvpMatrix; uniform mat4 u_NormalMatrix; uniform vec3 u_LightDirection; varying vec4 v_Color; void main() { gl_Position = u_MvpMatrix * a_Position; vec4 normal = u_NormalMatrix * a_Normal; float nDotL = max(dot(u_LightDirection, normalize(normal.xyz)), 0.0); v_Color = vec4(a_Color.xyz * nDotL, a_Color.a); } `; var FSHADER_SOURCE = ` #ifdef GL_ES precision mediump float; #endif varying vec4 v_Color; void main() { gl_FragColor = v_Color; } `; var gl = canvas.getContext('webgl'); initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE) this.gl = gl; var n = this.initVertexBuffers(gl) gl.clearColor(0, 0, 0, 1); gl.enable(gl.DEPTH_TEST); var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');//模型视图投影矩阵 var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix'); var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection'); if (!u_MvpMatrix || !u_NormalMatrix || !u_LightDirection) { console.log('Failed to get the storage location'); return; } var lightDirection = new Vector3([0.5, 3.0, 4.0]);//设置光线方向(世界坐标系下) lightDirection.normalize();//归一化 gl.uniform3fv(u_LightDirection, lightDirection.elements); var mvpMatrix = new Matrix4();//视图投影矩阵 mvpMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100); mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); var currentAngle = 0.0; //目前的旋转角度 var modelMatrix = new Matrix4(); //模型矩阵 var vpMatrix = new Matrix4();//模型视图投影矩阵 var normalMatrix = new Matrix4();//法线变换矩阵 var tick = function () { currentAngle = animate(currentAngle);//更新当前旋转角度 //计算模型矩阵 modelMatrix.setRotate(currentAngle, 0, 1, 0); //围绕y轴旋转 vpMatrix.set(mvpMatrix).multiply(modelMatrix); gl.uniformMatrix4fv(u_MvpMatrix, false, vpMatrix.elements); // Pass the matrix to transform the normal based on the model matrix to u_NormalMatrix normalMatrix.setInverseOf(modelMatrix); normalMatrix.transpose(); gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.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); requestAnimationFrame(tick, canvas); // Request that the browser ?calls tick }; tick(); initVertexBuffers(gl){ var vertices = new Float32Array([ // Vertex coordinates 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 front 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-v3-v4-v5 right 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-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 down 1.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([ // Colors 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 // v4-v7-v6-v5 back ]); var normals = new Float32Array([ // Normal 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 front 1.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 right 0.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 left 0.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 down 0.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([ // Indices of the vertices 0, 1, 2, 0, 2, 3, // front 4, 5, 6, 4, 6, 7, // right 8, 9,10, 8,10,11, // up 12,13,14, 12,14,15, // left 16,17,18, 16,18,19, // down 20,21,22, 20,22,23 // back ]); // Write the vertex coordinates and color to the buffer object if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position')) return -1; if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color')) return -1; if (!initArrayBuffer(gl, normals, 3, gl.FLOAT, 'a_Normal')) return -1; // Create a buffer object var indexBuffer = gl.createBuffer(); if (!indexBuffer) { console.log('Failed to create the buffer object'); return false; } // 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; } function initArrayBuffer(gl, data, num, type, attribute) { var buffer = gl.createBuffer(); // Create a buffer object if (!buffer) { console.log('Failed to create the buffer object'); return false; } // Write date into the buffer object gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); // Assign the buffer object to the attribute variable var a_attribute = gl.getAttribLocation(gl.program, attribute); if (a_attribute < 0) { console.log('Failed to get the storage location of ' + attribute); return false; } gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0); // Enable the assignment of the buffer object to the attribute variable gl.enableVertexAttribArray(a_attribute); gl.bindBuffer(gl.ARRAY_BUFFER, null); return true; } // Rotation angle (degrees/second) var ANGLE_STEP = 30.0; // Last time that this function was called var g_last = Date.now(); function animate(angle) { // Calculate the elapsed time var now = Date.now(); var elapsed = now - g_last; g_last = now; // Update the current rotation angle (adjusted by the elapsed time) var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0; return newAngle %= 360; } ``` 环境反射光颜色>漫反射光颜色>表面的反射光颜色>表面基底色>入射光颜色>环境反射光颜色>光线方向与法线方向的夹角>表面基底色>入射光颜色>漫反射光颜色>