\color{blue}{ch2}
坐标图
canvas的坐标横轴为x轴(正方向朝右),纵轴为y轴(正方向朝下)
webgl的右手坐标
对应坐标
绘图简单步骤
- 获取canvas:
var canvas = doucment.getElementById('webgl')
- 获取上下文:
var gl = getWebGLContext(canvas)
- 初始化着色器
initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE)
- 设置背景色:
gl.clearColor(0.0, 0.0, 0.0, 1.0)
- 清空canvas:
gl.clear(gl.COLOR_BUFFER_BIT)
- 绘图:
gl.drawArrays(gl.POINTS, 0, 1)
使用变量
- 声明:
attribute vec4 a_Position;\n
- 将变量赋值给gl_Position:
gl_Position = a_Position;\n
- 向变量传输数据:
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)
要点
- WebGL程序包括运行在浏览器中的Javascript和运行在WebGL系统的着色器程序俩个部分
- vec4 四个浮点数 组成的矢量(也称为齐次坐标是四维的,通常把最后一个变量设置为1.0代表三维)
- 顶点着色器两个内置变量:
gl_Position, gl_PointSize
- 片元着色器内置变量:
gl_FragColor
- attribute变量: 传输与顶点相关的数据, unifrom变量传输对所有顶点相同的数据(或与顶点无关)
\color{blue}{ch3}
绘制多点(使用缓冲区)
- 创建缓冲区对象:
var vertexBuffer = gl.creteBuffer()
- 绑定缓冲区对象:
gl.bindBuffer(gl.ARRAY_BUFFER, vextexBuffer)
- 将数据写入缓冲区:
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
- 将缓冲区对象分配给一个attribute变量(多点一次性分配)):
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
- 开启attribute变量(为了使顶点着色器能够访问缓冲区数据):
gl.enableVertexAttribArray(a_Position)
要点
-
new Float32Array([])
类型化数组:BYTES_PER_ELEMENT
数组中每个元素所占的字节数
WebGl绘制基本图形
gl.drawArrays(gl.TRIANGLES, 0, n)
\color{blue}{ch4}
变换矩阵步骤
- 为旋转矩阵创建Matrix对象:
var xformMatrix = new Matrix4()
- 将xformMatrix设置为旋转矩阵:
xformMatrix.setRotate(ANGLE, 0, 0, 1)
- 将旋转矩阵传输给顶点着色器:
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix.elements)
- varying变量的作用是从顶点着色器向片元着色器传输数据
要点
-
gl.uniformMatrix4fv()
的最后一个参数一定要是类型化数组 - 将变换全部复合在一起的矩阵称为模型矩阵
- 当有多种数据传输时,可以使用多个缓冲区存不同数据或者使用一个缓冲区进行数据的交错组织
一个缓冲区处理多种数据
传输颜色
varying vec4 v_Color;\n
+
.......
v_Color = a_Color;\n
+
.......
gl_FragColor = v_Color;\n
+
调用片元着色器
片元着色器计算出该片元的颜色,并写入颜色缓冲区,直到第15部最后一个片元被处理完成,浏览器就会显示处最终的结果
\color{blue}{ch6}
要点
-
vec4 v4 = vec4(1.0, 2.0, 3.0, 0.0)
可以通过v4.x, v4.y, v4.z
访问 - 存储限定字
Attribute
变量: 只能出现在顶点着色器中,“逐顶点” -
uniform
变量: 逐片元或顶点共用 -
varying
: 从顶点着色器向片元着色器传输数据, 但变量一定是同名和同类型的varying变量
\color{blue}{ch7}
视图矩阵
视图矩阵表示观察者的状态:
观察者的视点
,观察点
,上方向
视图矩阵步骤
uniform mat4 u_ViewMatrix;\n
用来接收视图矩阵
.......
gl_Position = u_ViewMatrix * a_Position;\n
视图矩阵与顶点坐标相乘
.......
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix)
获取存储位置var viewMatrix = new Matrix4()
viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0)
设置视点,视线和上方向gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements)
传输
要点
- 将平移,缩放等基本变换矩阵或它们的组合称为模型矩阵
<视图矩阵> x <模型矩阵> x <原始顶点坐标>
- 也可以将视图矩阵和模型矩阵相乘的结果成为
模型视图矩阵
:var modelViewMatrix = viewMatrix.multiply(modelMatrix)
-
<投影矩阵> x <视图矩阵> x <模型矩阵>
称为模型视图投影矩阵
鼠标或键盘控制思路
- 在main()里注册事件, 参数有:事件, gl, 顶点数, 视图矩阵储存位置, 视图矩阵
document.onkeydown = function(ev){keydown(ev, gl, n, u_ViewMatrix)}
- 在main()里画初始的(还没调用keydown函数):
draw(gl, n, u_ViewMatrix, viewMatrix)
- 在keydown改变
viewMatrix.setLookAt()
参数, 最后调用draw画图 - 执行draw:传递viewMatrix,
gl.drawArrays(gl.TRIANGLES, 0, n)
可视空间
- 盒子空间, 由
正射投影
产生: 大小与其所在的位置没有关系(不能产生深度感), 用到的矩阵是 正射投影矩阵(OrthoView
)
- 金字塔可视空间, 由
透视投影
产生: 一般真实的世界用的都是透视投影, 用到矩阵是 透视投影矩阵(projMatrix
)
处理对象的前后关系
- WebGL会按照缓冲区的顺序绘制图形,而且后绘制的图形覆盖先绘制图形
- 可以利用WebGL提供的
隐藏面消除
功能消除 - 步骤:
开启隐藏面消除功能
gl.enable(gl.DEPTH_TEST)
在绘制之前,清除深度缓冲区gl.clear(gl.DEPTH_BUFFER_BIT)
- 深度缓冲区就是用来存储深度信息的,所以在绘制一帧之前,都必须清除缓冲区(包括颜色和深度):
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
画3D图使用索引
- 使用
gl.drawElements()
代替gl.drawArrays()
- 上面两种方法的区别在于
gl.ELEMENT_ARRSY_BUFFERT
它管理着具有索引结构的三维模型数据
-
索引值是整型数,使用Uint8Array
- 当多次调用缓冲区时,可以把缓冲区封装成一个函数,
initArrayBuffer(gl, data, num, type, attribute)
其中
data
为顶点或者颜色坐标,type
表示坐标的类型,attribute
代表变量名,num
代表每个顶点的占位数
\color{blue}{ch8}
反射类型
- 漫反射(diffuse)
<漫反射光颜色> = <入射光颜色> x <表面基底色> x cosa
- 环境反射(ambient)
<环境反射光颜色> = <入射光颜色> x <表面基底色>
物体最终颜色
<表面的反射光颜色> = <漫反射光颜色> + <环境反射光颜色>
平行光下的漫反射
- 入射角cosa =
<光线方向> · <法线方向>
,两者的点积,两个向量进行归一化 - 平面上任意一点都具有相同的法向量, 所以
顶点坐标个数
与顶点颜色个数
与法线量个数
要保持一致 - 归一化(
normalize
)光线以Float32Array类型的形式储存在lightDirection对象的elements属性中
运动物体光照
- 对顶点坐标进行变换的模型矩阵
变换后的法向量: 用
法向量
乘以模型矩阵的逆转矩阵
\color{green}{modelMatrix}
求逆转置步骤:
- 求原矩阵的逆矩阵
normalMatrix.setInverseOf(modelMatrix)
- 将上一步的求得的逆矩阵进行转置
normalMatrix.transpose()
这里的用normalMatrix
来存储
点光源的漫反射
- 在物体不同位置(不同顶点)的入射光的方向不同
- 计算顶点的入射光的方向步骤:
- 计算模型变换后的顶点坐标(
世界坐标
)var vertexPosition = u_ModelMatrix * a_Position
- 光线方向是由点光源坐标减去顶点坐标,在进行归一化
vec3 lightDirecti on = normalize(u_LightPosition - vec3(vertexPosition))
要点
- 为了更逼真:逐片元光照
\color{blue}{ch9}
要点
- 模型矩阵(\color{red}{modelMatrix}): 变换原始顶点坐标(旋转,平移,缩放)
- 视图矩阵(\color{red}{viewMatrix}): 观察者状态
lookAt
- 投影矩阵(\color{red}{projMatrix}): 可视空间包括
setPerspective, setOrtho
- 一组顶点多次复用时,平移和旋转都有类似的光标概念
层次结构模型
- 例:单关节模型
- 初始化顶点坐标和颜色缓冲区
n = initVertexBuffer(gl)
- 注册键盘事件,参数:
ev, gl, n(顶点索引数组个数), viewProjMatrix(投影矩阵) , u_MvpMatrix(投影视图模型矩阵), uNormalMatrix
- 触发键盘事件, 改变全局变量
g_arm1Angle
和g_joint1Angle
, 从而改变全局模型矩阵g_modelMatrix
, 多次调用- 重新计算视图投影模型矩阵
viewProjMatrix
- 例: 多关节模型
- 初始化顶点坐标和颜色缓冲区
n = initVertexBuffer(gl)
- 注册键盘事件,参数:
ev, gl, n(顶点索引数组个数), viewProjMatrix(投影矩阵) , u_MvpMatrix(投影视图模型矩阵), uNormalMatrix
- 触发键盘事件, 改变全局变量
g_arm1Angle
和g_joint1Angle
,g_joint2Angle
,g_joint3Angle
, 从而改变全局模型矩阵g_modelMatrix
- 多次调用draw画组件,在画手指之前缓存
g_modelMatrix
,pushMatrix
存储矩阵的栈,将矩阵推入栈
\color{blue}{ch10}
纹理图像步骤
- 顶点着色器中接收顶点的纹理坐标, 光栅化后传递给片元着色器
var verticesTexCoords = new Float32Array([...])
创建顶点坐标和纹理坐标
var a_TexCoord = gl.getAttribLocation()
分配并开启
- 片元着色器根据片元的纹理坐标,从纹理图像中抽取纹素颜色,赋给当前片元
initTextures()
函数负责配置和加载纹理:
var texture = gl.createTexture()
创建纹理对象来管理纹理
var u_Sampler = gl.getUni...
变量用来接收纹理图像
image.onload = function () { loadTexture(gl, n, texture, u_Sampler, image)}
来等图片加载完成执行
loadTexture()
用来配置纹理:
gl.pixelStorei()
反转转y坐标gl.activeTexture()
激活纹理单元, --管理gl.bindTexture()
绑定纹理对象, --操作gl.texParameteri()
配置纹理对象的参数gl.texImage2D()
将纹理图像分配给纹理对象gl.uniform1i()
将纹理单元传递给片元着色器