WebGL编程指南学习(7)
7. 层次模型
- 由多个简单的部件组成的复杂模型
- 为复杂物体(机器人手臂)建立具有层次化结构的三维模型
7.1 多个简单模型组成的复杂模型
层次结构模型
- 当情况较为简单时,实现“部件A转动带动部件B转动”可以很直接,只要对部件B也施以部件A的旋转矩阵即可
- 情况复杂一些时,需要考虑多个旋转矩阵。按照模型中各个部件的层次顺序,从高到低逐一绘制,并在每个关节上应用模型矩阵
例程:单关节模型
两个立方体部件组成的机器人手臂:arm1(上臂)和arm2(前臂)。使用左右方向键控制arm1(同时带动整条手臂)水平转动;使用上下方向键控制arm2绕joint1关节垂直转动
- 几何体:用立方体表示arm,arm1和arm2共用一个立方体
- 绘制层次模型
draw()
function draw(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix) {
// Clear color and depth buffer
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 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
}
draw()
内部调用drawBox()
函数,每调用一次,绘制一个部件。先调用者先绘制。
**绘制单个部件的步骤是:**1. 平移;2.旋转;3.绘制
- 绘制部件
drawBox()
绘制某个部件的顺序是:1. 计算模型视图投影矩阵;2. 计算法向量变换矩阵;3. 绘制立方体
例程:多节点模型
-
扩展例程,绘制一个具有多个关节的完整的机器人手臂,包括base,arm1,arm2,palm,finger1 & finger2
-
arm1和arm2的操作和之前一样;此外,可以使用x和z旋转joint2(腕关节);使用c和v键旋转finger1和finger2
-
和上一个例子的区别在于
keydown()
相应的按键更多draw()
绘制各部件的逻辑更复杂
使用栈存储当前层次的模型矩阵,有利于绘制相同层级的部件
popMatrix()
与pushMatrix()
也可以通过分别对每个部件设置顶点坐标,然后创建不同的缓冲区,再实际绘制的时候写入a_Position,这样做可以省去缩放、平移等模型矩阵变换操作
7.2 着色器和着色器程序对象
- 分析
initShaders()
的作用
initShaders()
的作用是编译GLSL ES代码,创建和初始化着色器供WebGL使用,具体步骤如下:
- 创建着色器对象
gl.createShader()
- 向着色器对象中填充着色器程序的源代码
gl.shaderSource()
- 编译着色器
gl.complileShader()
- 创建程序对象
gl.createProgram()
- 为程序对象分配着色器
gl.attachShader()
- 连接程序对象
gl.linkProgram()
- 使用程序对象
gl.useProgram()
着色器对象(Shader object)与程序对象(Program object)
- 着色器对象:管理一个着色器(顶点或片元)
- 程序对象:管理着色器对象的容器,一个程序对象必须包含一对儿顶点和片元着色器
创建着色器对象
gl.createShader(type)
type: gl.VERTEX_SHADER | gl.FRAGMENT_SHADER
可以使用gl.deleteShader(shader)
给它删掉
指定着色器对象中的代码
gl.shaderSource(shader, source)
JavaScript中source是字符串形式的代码
编译着色器
gl.compileShader(shader)
创建程序对象
gl.createProgram()
可以使用gl.deleteProgram(program)
删除之
一旦程序对象被创建以后,需要向其附上一对儿着色器:
分配着色器对象
gl.attachShader(program, shader)
可以使用gl.detachShader(program, shader)
接除分配
连接程序对象
分配了两个着色器以后,还需要将顶点着色器和片元着色器连接起来
gl.linkProgram(program)
使用gl.getProgramParameters(program, pname)
来检查是否连接成功
告知WebGL系统所使用的程序对象
gl.useProgram(program)
这个函数使得WebGL在回之前可以准备多个程序对象,然后在绘制的时候根据需要切换程序对象