/** 关键字:WebGL 模型矩阵 视图矩阵 逆转置矩阵 逐片元 **/ //顶点着色器 var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + //顶点数据 'attribute vec4 a_Normal;\n' + //法线数据 'uniform mat4 u_MvpMatrix;\n' + //模型视图投影矩阵 'uniform mat4 u_ModelMatrix;\n' + // 模型矩阵 'uniform mat4 u_NormalMatrix;\n' + // 模型的逆转置矩阵,用于计算变化后的法线。 'varying vec4 v_Color;\n' + //顶点颜色 'varying vec3 v_Normal;\n' + //变化之后的顶点的法线 'varying vec3 v_Position;\n' + //世界坐标系中的顶点位置 'void main() {\n' + ' vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n' + // 球体颜色 ' gl_Position = u_MvpMatrix * a_Position;\n' + //球的位置 ' v_Position = vec3(u_ModelMatrix * a_Position);\n' + //计算顶点在世界坐标系中的位置。 ' v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +//计算变化之后的顶点法线 ' v_Color = color;\n' + //顶点颜色 '}\n'; // 片元着色器 var FSHADER_SOURCE = '#ifdef GL_ES\n' + 'precision mediump float;\n' + '#endif\n' + 'uniform vec3 u_LightColor;\n' + // 灯光颜色 'uniform vec3 u_LightPosition;\n' + // 光源的位置 'uniform vec3 u_AmbientLight;\n' + // 环境光颜色 'varying vec3 v_Normal;\n' + //顶点法线 'varying vec3 v_Position;\n' + //顶点位置 'varying vec4 v_Color;\n' + //顶点颜色 'void main() {\n' + //再次对法线归一化,因为被插值了。 ' vec3 normal = normalize(v_Normal);\n' + //计算光的方向并归一化 ' vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +//计算 //用点积计算光与法线夹角的cos值。 ' float nDotL = max(dot(lightDirection, normal), 0.0);\n' + //计算漫反射光 ' vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;\n' + //计算环境光 ' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' + //最终的颜色 ' gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' + '}\n'; 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; } //创建球的顶点缓存 var n = initVertexBuffers(gl); if (n < 0) { console.log('Failed to set the vertex information'); return; } // Set the clear color and enable the depth test gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); // Get the storage locations of uniform variables var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix'); var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix'); 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'); if (!u_ModelMatrix || !u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) { console.log('Failed to get the storage location'); return; } // Set the light color (white) gl.uniform3f(u_LightColor, 0.8, 0.8, 0.8); // Set the light direction (in the world coordinate) gl.uniform3f(u_LightPosition, 5.0, 8.0, 7.0); // Set the ambient light gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2); var modelMatrix = new Matrix4(); // Model matrix var mvpMatrix = new Matrix4(); // Model view projection matrix var normalMatrix = new Matrix4(); // Transformation matrix for normals // Calculate the model matrix modelMatrix.setRotate(90, 0, 1, 0); // Rotate around the y-axis // Calculate the view projection matrix mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); mvpMatrix.lookAt(0, 0, 6, 0, 0, 0, 0, 1, 0); mvpMatrix.multiply(modelMatrix);//对球来说,旋转是多余的,没啥效果。 // Calculate the matrix to transform the normal based on the model matrix normalMatrix.setInverseOf(modelMatrix);//求逆 normalMatrix.transpose();//转置 // Pass the model matrix to u_ModelMatrix gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); // Pass the model view projection matrix to u_mvpMatrix gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); // Pass the transformation matrix for normals to u_NormalMatrix gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements); // Clear color and depth buffer gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 绘制三角形构成的立方体 gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); } function initVertexBuffers(gl) { // Create a sphere var SPHERE_DIV = 13; var i, ai, si, ci; var j, aj, sj, cj; var p1, p2; var positions = []; var indices = []; // Generate coordinates for (j = 0; j <= SPHERE_DIV; j++) { aj = j * Math.PI / SPHERE_DIV; sj = Math.sin(aj); cj = Math.cos(aj); for (i = 0; i <= SPHERE_DIV; i++) { ai = i * 2 * Math.PI / SPHERE_DIV; si = Math.sin(ai); ci = Math.cos(ai); positions.push(si * sj); // X positions.push(cj); // Y positions.push(ci * sj); // Z } } // Generate indices//产生上面这些数据的索引。 for (j = 0; j < SPHERE_DIV; j++) { for (i = 0; i < SPHERE_DIV; i++) { p1 = j * (SPHERE_DIV+1) + i; p2 = p1 + (SPHERE_DIV+1); indices.push(p1); indices.push(p2); indices.push(p1 + 1); indices.push(p1 + 1); indices.push(p2); indices.push(p2 + 1); } } // Write the vertex property to buffers (coordinates and normals) // Same data can be used for vertex and normal // In order to make it intelligible, another buffer is prepared separately if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1; if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3)) return -1; // Unbind the buffer object gl.bindBuffer(gl.ARRAY_BUFFER, null); // Write the indices to the buffer object var indexBuffer = gl.createBuffer(); if (!indexBuffer) { console.log('Failed to create the buffer object'); return -1; } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); return indices.length; } function initArrayBuffer(gl, attribute, data, type, num) { // Create a buffer object var buffer = gl.createBuffer(); 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); return true; }
相关资料
相关资料: 第二章 零、顶点着色器:控制位置和大小。 片元着色器:控制颜色。 一、顶点着色器内置变量: 1、gl_Position, 2、gl_PointSize 二、片元着色器内置变量 FragColor(片元着色器唯一的变量)。 三、gl.drawArrays(mode,first,count) mode:gl.POINTS, gl.LINES,gl.LINE_STRIP((1,2),(2,3) ...), gl.LINE_LOOP((1,2),(2,3) ...首尾相连), gl.TRIANGLES, gl.TRIANGLE_STRIP((0,1,2),(2,1,3),(2,3,4)...), gl.TRIANGLE_FAN((1,2,3),(1,3,4),(1,4,5))。 first:从哪个点开始。 count:使用多少个点。 注意:当该函数被执行时,顶点着色器被执行count次。每次处理一个点。 四、webGL使用的是右手坐标系:x向右、y向上、z向屏幕外。 五、从js向顶点着色器传变量的方式有两个: 1、attribute(存储限定符) 传输那些和顶点相关的数据。(只有顶点着色器能使用) 使用的过程: a)在顶点着色器中声明attribute类型的变量, b)赋值给gl_Position,c), c)在js变量中获取这个变量的存储地址: gl.getAttribLocation(gl.program, 'a_Position'), d)在js中给这个变量赋值: gl.vertexAttrib3f(a_Position, 0.0, 0.5, 0.0)。 2、uniform(存储限定符) 传输哪些对于所有顶点都相同(和顶点无关)的数据。(顶点着色器和片元着色器都可以用) 使用的过程和attribute的套路差不多。 第三章 绘制和变换三角形 一、创建缓存区对象的过程: 1、准备好数据-> var vertices = new float32Array([0.0,0.5,-0.5,-0.5,0.5,-0.5]); //三个点 2、创建一个缓存区对象-> var vertexBuffer = gl.createBuffer(); 3、[将缓存区绑定到目标]-> gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); 4、将数据写到缓存区里-> gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); 5、将缓存区的数据赋给顶点着色器-> gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0); 6、[让分配的变量可用]。gl.enableVertexAttribArray(a_Position); 矩阵:每次变化,都是在顶点着色器中逐顶点改变图形的位置。 每一次新的变换,都得求一个新的等式放到顶点着色器中实现,为了不这样来回更改顶点着色器,我们使用新的工具 --变化矩阵。将多个变化矩阵一乘(变换效果叠加)得到一个总变化矩阵,再传入顶点着色器(矩阵*矢量特性)。 变化矩阵非常适合操作计算机图形。 二、旋转矩阵 // Note: WebGL is column major order 列主序!!!!! var xformMatrix = new Float32Array([ cosB, sinB, 0.0, 0.0, -sinB, cosB, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); //行主序 [ cosB, -sinB, 0.0, 0.0, sinB, cosB, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] 三、平移矩阵 var xformMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, Tx, Ty, Tz, 1.0 ]); //行主序 [ 1.0, 0.0, 0.0, Tx, 0.0, 1.0, 0.0, Ty, 0.0, 0.0, 1.0, Tz, 0.0, 0.0, 0.0, 1.0 ] 四、缩放矩阵 var xformMatrix = new Float32Array([ Sx, 0.0, 0.0, 0.0, 0.0, Sy, 0.0, 0.0, 0.0, 0.0, Sz, 0.0, 0.0, 0.0, 0.0, 1.0 ]); //行主序 [ Sx, 0.0, 0.0, 0.0, 0.0, Sy, 0.0, 0.0, 0.0, 0.0, Sz, 0.0, 0.0, 0.0,0.0,1.0 ] 第四章 高级变换与动画基础 一、模型矩阵:一个模型可能经过多次变换,将这些变化全部复合成一个等效的变换(矩阵之间相乘)就得到了模型变换。 相应的,模型变化的矩阵称之为模型矩阵。 二、一般情况下,平移旋转和旋转平移所得到的效果是不一样的(比如沿x轴平移和沿x旋转,效果应该是一样的)。 第五章 颜色和纹理 顶点着色器(逐顶点操作)-->装配(什么形状)-->光栅化(插值,什么颜色)-->片元着色器(逐片元操作)。 一、多种顶点数据传入顶点着色器的方法: 1、建立多个缓存区分别传入。 2、建立一个缓存区,将数据交叉传入(利用gl.vertexAttribPointer (a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);)。 二、varying(可变量):从顶点着色器向片元着色器传输数据。在WebGl中,如果顶点着色器和片元着色器中有类型和名称都相同的 varying变量,那么顶点着色器赋值给该变量的值会被自动传入片元着色器。 只能是float以及与之相关的顶点(vec2,vec3,vec4)矩阵(mat2,mat3,mat4)。 三、片元坐标:光栅化的片元都带有坐标信息的,gl_FragCoord.x,gl_FragCoord.y。 四、纹理映射的四个步骤: 1、准备好纹理图像。(图片的坐标系左上角为原点,WebGL则是左下角为原点) 2、为几何图形配置纹理映射方式。 3、加载纹理图像,对其进行一些配置,以在webGl中用它。 4、在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元。 五、使用纹理的详细步骤 1、设置纹理坐标 将顶点坐标、纹理坐标都存到缓存区别开启。 2、配置和加载纹理 创建了纹理对象,绑定了onload事件。 3、为WebGL配置纹理。 开启0号纹理单元 绑定纹理对象 配置纹理对象的参数 将纹理图像分配给纹理对象 将0号纹理传递给片元着色器中的取样变量。 4、从顶点着色器向片元着色器传递纹理坐标。 5、从片元着色器中获取纹理像素颜色。 第七章 进入三维世界 "根据自定义观察者的状态,绘制观察者看到的景象"(将视图矩阵传入顶点着色器) 和 "使用默认观察者的状态,对三维对象进行变换(平移、旋转等),再绘制观察者看到的景象"( 模型矩阵传入顶点着色器)效果是一样的。(反正都要在顶点着色器中乘一个矩阵) WebGL默认视点:在(0,0,0)点,看向Y轴负半轴,即朝屏幕里面。 视图矩阵:视点、观察目标点、上方向。 用视图矩阵乘以顶点坐标会把顶点变化到合适的位置,使得观察者(以默认的状态)观察新位置的顶点。就好像 在观察者处在(视图矩阵描述的)视点上观察原始顶点一样。 (最终的结果是 :视点还在原点,只是物体的为矩阵到合适的位置) 模型矩阵:平移、缩放、旋转等基本变化矩阵或它们的组合。 模型视图矩阵:视图矩阵*模型矩阵。 正射投影矩阵:Matrix.setOrtho(left,right,bottom,top,near,far)。 透视投影矩阵:Matrix.setPerspective(fov,aspect,near,far) fov:垂直视角,可视空间顶面与地面的夹角。 aspect:近裁剪面的宽高比(宽度/高度)。 near,far:近裁剪面和远裁剪面的位置(相对于视点,必须大于零)。 世界物体-->裁剪(投影矩阵) -->规范立方体-->降维-->屏幕上。 模型视图投影矩阵:投影矩阵*视图矩阵*模型矩阵(投影矩阵的顺序位置不能移动,为什么?) 第八章 光照 看到的光(物体反射的光)取决于以下两个因素: 入射光(方向和颜色)。 物体表面的类型(基色和反射特性)。 反射特性: 漫反射:反射光在各方向上均匀的,是一种理想模型。 漫反射反射光的颜色:入射光颜色*表面基色*cosB,B是入射光与表面法线的夹角。 =入射光颜色*表面基色*(光线方向.法线方向) 环境反射:反射光在各方向上均匀的,强度相等的。 环境反射光颜色:入射光颜色*表面基色。 看见的光 = 漫反射反射光的颜色 + 环境反射光颜色 计算变化之后的法向量:之前的法向量*模型矩阵的逆转置矩阵。 逆转置矩阵:逆矩阵的转置。