WebGL基础简明教程2-基础知识
来源:互联网 发布:下周主要财经数据 编辑:程序博客网 时间:2024/05/19 16:51
上一篇我们介绍了使用WebGL的基础,包括顶点着色器、片元着色器、初始化WebGL,初始化着色器以及变换、动画、颜色、纹理等,这一部分的内容我们就来进入三维的世界。和上一篇文章一样,我们的这篇只做个大概的介绍,详细的内容部分请参阅《WebGL编程指南》一书。
代码存储在我的GitHub中。
https://github.com/zrysmt/data-viz/tree/master/webgl/demo
首先我们来绘制一个三维的实例。
示例程序:https://zrysmt.github.io/demo/webgl-demo/demo/10-HelloCube.html.
实例的源码程序:https://github.com/zrysmt/data-viz/blob/master/webgl/demo/HelloCube.js
其实这里我们应该注意到示例和源码的结构对应,下面的一些我可能只给出一个url地址。
1.视图、投影和索引矩阵
- #### 从main函数开始
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; } // Set the vertex coordinates and color var n = initVertexBuffers(gl); if (n < 0) { console.log('Failed to set the vertex information'); return; } // Set clear color and enable hidden surface removal gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); // 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; } // Set the eye point and the viewing volume var mvpMatrix = new Matrix4(); mvpMatrix.setPerspective(30, 1, 1, 100); mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); // Pass the model view projection matrix to u_MvpMatrix gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.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);}
- #### 顶点着色器和片元着色器
和上一篇有一些的不同
var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_MvpMatrix;\n' + //模型矩阵,设置视图/投影 'varying vec4 v_Color;\n' + //传值给片元着色器 'void main() {\n' + ' gl_Position = u_MvpMatrix * a_Position;\n' + ' v_Color = a_Color;\n' + '}\n';// Fragment shader programvar 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';
关于attribute uniform varying
attribute:只能是全局的,只能出现在顶点着色器,标识逐顶点信息;
uniform:只能是全局的,可以在顶点着色器和片元着色器上,如果两个地方均定义,那么这变量被两个着色器共享了;
varying:只能是全局的,负责从顶点着色器向片元着色器传输数据。
- #### 视图矩阵(view matrix)
视点、观察点和上方向决定视图矩阵
var mvpMatrix = new Matrix4(); mvpMatrix.setLookAt(3, 3, 7, 0, 0, 0, 0, 1, 0)
- #### 投影矩阵
投影的作用就是使得距离近的看的比较大,距离远的看的比较小
示例
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
三角形与可视空间的位置
其实投影矩阵的核心就是两种变换,1)按比例缩放;2)平移
- #### 消除被遮挡的面
gl.enable(gl.DEPTH_TEST);
清除颜色和深度buffer
// Clear color and depth buffer gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- #### 重要的公式
我们总结下一个重要的公式,WebGL绘制物体的位置为:
<投影矩阵> X <视图矩阵> X <模型矩阵> X <顶点坐标>
- #### 绘制立方体
function 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 ]); // Indices of the vertices var indices = new Uint8Array([ 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); // 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;}
立方体的结构
v6----- v5 /| /| v1------v0| | | | | | |v7---|-|v4 |/ |/ v2------v3
首先顶点坐标和顶点坐标的颜色存储在verticesColors
矩阵中。
而WebGL的绘制是按照三角形的形式一个一个绘制的,那么意味着一个立方体的一个面有两个三角形,一个立方体总共需要2*6 = 12个三角形,一个三角形需要3个顶点,那么一共需要绘制 3*12 = 36个顶点。我们知道一个立方体只需要8个顶点就可以了。为此我们在这里使用了索引矩阵(indices),索引矩阵也要写入缓存中。
这样我们就能完成绘制了
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
注意这里的n = indices.length
2.光照
光照有三种形式的:
物理表面反射光纤:分为漫反射和环境反射
- #### 环境反射
<环境反射光颜色> = <入射光颜色> X <表面基底色>
- #### 平行光下的漫反射
<漫反射光颜色> = <入射光颜色> X <表面基底色> X cosθ
即:
<漫反射光颜色> = <入射光颜色> X <表面基底色> X (<光线方向> · <法线方向>)
我们只用漫反射的效果的时候
示例
由于只考虑漫反射,右边的部分几乎是黑色了,这时候我们来考虑下环境光。
环境反射下的表面的反射光颜色
<表面的反射光颜色> = <漫反射颜色> + <环境反射光颜色>
示例
我们来看下着色器源码
var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + //表面颜色 'attribute vec4 a_Normal;\n' + // 表面法向量 'uniform mat4 u_MvpMatrix;\n' + 'uniform vec3 u_DiffuseLight;\n' + // 漫反射光颜色 'uniform vec3 u_LightDirection;\n' + // 漫反射入射光方向 (in the world coordinate, normalized)归一化后 'uniform vec3 u_AmbientLight;\n' + // 环境光颜色 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_MvpMatrix * a_Position;\n' + // 归一化方向量 ' vec3 normal = normalize(a_Normal.xyz);\n' + // <归一化的光线方向> 点乘 <归一化法线向量> ' float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' + // 计算漫反射颜色 ' vec3 diffuse = u_DiffuseLight * a_Color.rgb * nDotL;\n' + // 计算环境光颜色 ' vec3 ambient = u_AmbientLight * a_Color.rgb;\n' + // 两者相加得到物体最终的颜色 ' v_Color = vec4(diffuse + ambient, a_Color.a);\n' + '}\n';// Fragment shader programvar 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';
对于变化后的物体,物体的法向量也会改变
示例
<变换后的法向量> = <法向量> X <变化矩阵的逆转置矩阵>
normalMatrix.setInverseOf(modelMatrix); normalMatrix.transpose(); gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
在顶点着色器中变化原法向量
vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal))
最后我们来看下最终的漫反射光颜色的公式
“`
<漫反射光颜色> = <入射光颜色> X <表面基底色> X (<光线方向> ·(<模型矩阵逆转置矩阵> X <法线方向>))
- #### 点光源
光线方向 = 归一化(点光源方向 - 顶点坐标)
```// 顶点处的光线方向 = 点光源的光坐标 - 顶点坐标 ' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n'
3.层次模型
有的物体的运动会带动别的物体,如上臂的运动会带动下臂和手掌的运动。
示例:https://zrysmt.github.io/demo/webgl-demo/ch09/JointModel.html
示例源码:https://github.com/zrysmt/data-viz/blob/master/webgl/ch09/JointModel.js
下边的(Arm1)会带动上边的(Arm2)运动,而上边的运动不会带动下边的。
思路:共用一个变换的模型矩阵:g_modelMatrix
,Arm1变化g_modelMatrix
也会变化,那么Arm2也会使用这个模型。谁能控制谁,关键是谁先写上去,谁后写上去,先写上去的控制后写上去的。
// Arm1 var arm1Length = 10.0; // Length of arm1 g_modelMatrix.setTranslate(0.0, -12.0, 0.0); g_modelMatrix.rotate(g_arm1Angle, 0.0, 1.0, 0.0); // Rotate around the y-axis drawBox(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); // Draw | // Arm2 g_modelMatrix.translate(0.0, arm1Length, 0.0); // Move to joint1 g_modelMatrix.rotate(g_joint1Angle, 0.0, 0.0, 1.0); // Rotate around the z-axis g_modelMatrix.scale(1.3, 1.0, 1.3); // Make it a little thicker drawBox(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); // Draw
4.几个高级功能的实例
- 鼠标控制物体的旋转
- 是否选中物体
- HUD,结合canvas绘制文本
- 雾化
- 绘制阴影
- 加载三维obj文件
- WebGL基础简明教程2-基础知识
- WebGL基础简明教程1-简介
- PHP简明教程-面向对象基础 2
- learing WebGL lesson 2 WebGL教程
- 简明Python3教程 6.基础
- 简明Python3教程 6.基础
- 简明教程 Unity碰撞基础
- 《简明Python教程》之基础
- WebGL教程
- XML简明教程(2)
- numpy简明教程(2)
- WebGL资料WebGL自修教程
- 简明Pyhton教程余下基础内容
- PHP简明教程-面向对象基础 1
- Python基础(简明Python教程)
- 简明C语言教程(五)预备计算机专业基础知识
- 简明C语言教程(六)预备编程基础知识
- 【WebGL初学系列之一】WebGl基础知识
- 238. Product of Array Except Self
- Cassandra 3.0 的新特性介绍: 物化视图
- js 面对对象编程
- okhttp网络框架的https协议之忽略证书验证的登录实例
- C# 内存单位计算 源码
- WebGL基础简明教程2-基础知识
- group by 与order by
- appcan移动开发中uexBaiduMap插件的悬浮效果
- 【php中的curl】php中curl的详细解说
- Ibatis细节
- 如何对故事布局谋篇
- 在deepin上面安装node
- kafka学习笔记:知识点整理
- JAVA 常用的加密算法之单向加密MD5和SHA