图形学
研究表达的科学. 利用计算机生成图像的科学–主要研究图形的表示, 生成处理和显示.
1984年, 4k显示器出现.
1995, 可编程着色器.
1996, 法线贴图(Normal Mapping) – 提升低精度模型的显示效果.
1999, Nvidia, Geforce 256; 1995 DirectX 1.0; 1997 OpenGL 1.1.
GPU: 显示处理单元.
Shader language: GLSL
向量
图形渲染引擎使用左手系.
向量, 用来描述的, 而不是真实存在的.
向量的长度通常读作向量的模
圆
使用弧度来描述角度.
1radian = 180°/Π
极坐标系, 由极点(pole)和射线(ray)组成的坐标系. 用角度/射线长度描述点.
向量的点乘和叉乘
叉积
叉积几何意义
行列式求值
三维叉积的意义
- 向量叉积指向向量a和b的垂直方向
- 值等于a和b形成的平行四边形面积
- a, b和c的叉积是a, b, c形成的平行六面体体积
向量点积
矩形的乘法
矩阵的加减法
矩阵的转置
矩阵的乘法
一个 m*n 的矩阵乘以一个 n*q 的矩阵, 可以得到一个 m*q 的矩阵.
注意观察规律.
对角线都是1的矩阵叫做单位矩阵.
单位矩阵乘以任何矩阵都保持该矩阵的值不变.
矩阵相乘不满足交换律, AB!=BA
满足分配律和结合律.
矩阵乘法的意义
- 揭示了线性变换的本质.
- 点P和矩阵M的乘积PxM是点P的一种线性变换(把空间的点映射到另外一个点).
例如: 把一个正方形映射成一个平行四边形.
矩阵的逆(Invese)
向量叉积的行列式表示
三角形网格
一种由顶点(vertex)集合和索引(index)集合描述(那三个点形成一个面)图形的方法.
为什么使用三角形?
- 三个点可以确定一个平面, 而且共面.
- 唯一(三个点确定一个三角形); 效率高, 方便描述
- 数学模型更加简单(判断投影是否在三角形内)
球面网格绘制
渲染图形–从顶点到视区
顶点: 在3D世界顶点通常用一个坐标表示.
显示的时候把三角形放在屏幕上就变形了
图形本质式生活在3d空间的, 3d坐标系的原点本质上可以随便指定, x,y和z轴的方向, 也可以随便指定–因为这是一个虚拟世界.
在虚拟世界中增加一只眼睛, 眼睛位置不同看到就不同.
- 表示物体的坐标系叫做世界坐标系.
- 眼睛所在的位置会形成相对于眼睛的坐标系, 叫做观察坐标系.
- 显示到屏幕上的内容叫做视口坐标系.
- 固一个顶点至少要经过两次变换.
WebGL
- Web Graphics Library
- 浏览器端2d/3d渲染引擎
- OpenGL ES标准(OpenGL的轻量级标准)
HTML & Canvas
Canvas是Html的一种元素, canvas是基于WebGL工作的.
应用
- 微信小程序
- 浏览器(建造养成类), 医疗辅助, VR等
- 桌面应用(Electron)
绘制2D图形
用三角形描述一个”F”. 用6个三角形, 也就是18个顶点, 但是会有重叠, 实际顶点数会少点.
顶点网格–Mesh
Model层设计
模型是对世界物体的一种抽象.
WebGL 图形渲染管道
图形渲染管道–Graphics Pipeline. 管道关注的是数据的流转, 数据的变换以及数据的处理.
绘制三维图形需要一系列的步骤, 把这些抽象成渲染管道(流水线)
Vertex Shader : 顶点着色器. 相当于挖了出来, 这种设计叫做IOC, 这部分交给程序员处理.
使用一门新的语言, 是因为在这个计算过程中, 有很多这些通用的计算能力, 而这些计算能力需要抽象封装.
经过顶点着色器, 顶点的坐标也就计算完成了, 也直到哪几个点组成三角形的, 下一个阶段就会进入 图元组装(Primitives Assembly).
图元组装就是把这些数据转换成 WebGL 理解的图形描述.
栅格化: 把这些三角形如何映射到像素点. 每一个像素叫做 Fragment.
Fragment Shader: 这个操作也是 IOC, 可以让开发者来进行编程上色. 这个也要用一门新的语言. 主要是为了提升效率, 利用显卡的资源. 显卡的 ALU 很多, 并行计算很快.
开发者的程序进行完之后, WebGL 就进行 Fragment Operation (着色操作).
形成 Frame Buffer(帧缓冲区). 在内存里, 它可以驱动显示器工作. 每个颜色需要4个数字, 然后 分辨率 x 4 字节.
GPU 之后就会读取进行处理.
顶点着色器
图元组装
栅格化
- Cull: 选择去掉一些图元
- Clip: 剪裁去掉图元的某些部分
片段着色器
计算图元每个像素的颜色
着色操作–深度探测
- 遍历所有图元中的像素, 计算深度
- 如果存在深度更小的(离用户更近的点), 可以采取一些措施
帧缓冲区
存储发送位图给显示器
Buffer 管理
通过 Buffer 传递数据给 GLSL.
传递方式
- Uniforms 直接传递, 可以传矩阵, 向量.
- Attributes 通过 Buffer. 顶点属性, 包括位置, 颜色等.
传递向量到着色器
- gl.uniform[1234][uif][v](location, data)
- ui: unsigned integer
- i: integer
- f: float
- location: 变量位置
- data: 与维度要一致, 如果是2就是2个变量的数组.
- 例如: gl.uniform2fv(location, data)
传递矩阵到着色器
- gl.uniformMatrix[234]x[234]fv(location, false, data)
- false : 目前是写死的, 预留的 api. 代表这个矩阵要不要转置.
- 例子
- gl.uniformMatrix2fv(location, false, data)
- gl.uniformMatrix2x3fv(location, false, data)
- gl.uniformMatrix3fv(location, false, data)
Buffer 缓冲区
- 分配的一块内存空间
- WebGL 中用于存储数据
- 类型
- Vertex Buffer, 顶点缓冲区
- Index Buffer, 对顶点进行索引
- Frame Buffer, 帧缓冲区, 驱动显示器工作
缓冲区相关操作
- gl.createBuffer()
- gl.bindBuffer(target, buffer)
- gl.bufferData(target, data, usage)
target/usage 参数
- target, 描述buffer被绑定到哪里
- gl.ARRAY_BUFFER, 顶点属性
- gl.ELEMENT_ARRAY_BUFFER, 索引
- usage, 提示 webgl 数据将如何使用
- gl.STATIC_DRAW, 数据通常不会发生变化
- gl.DYNAMIC_DRAW, 动态数据
绘制3D图形
顶点
6个面, 每个面4个点.
索引
透视矩阵
人的感觉是近大远小的, 两线相交的点叫灭点.
GLSL
- OpenGL 着色器语言(OpenGL Shading Language)
- 让开发者可以对渲染过程拥有更多的控制
两个IOC留给了开发者使用.
- 计算顶点位置(Vertex Shader)
- 平移, 旋转, 缩放
- 投影
- 为每个像素上色(Fragment Shader)
- 颜色
- 材质
- 光照
数据类型
- Attribute(属性)
- 表示数据(顶点, 索引, 颜色, 法向量等)
- 只在顶点着色器中使用
- Uniform(统一的)
- 通常是一个全局的向量(如颜色, 光照参数等), 或者全局的矩阵(如世界矩阵, 观察矩阵等等)
- 可以在顶点着色器和片段着色器中使用
- Varying(变化的)
- 通常用来将数据从 顶点着色器 传递到 片段着色器
二维空间变换
- 简单2D变换
- 剪裁空间
- 复合变换
使用同维度的矩阵不能平移. 所以 增加一个维度.
总结, 增加一个维度.
这种增加一个维度的做法, 叫做 齐次(Homogenous)坐标.
平移操作可以看作一个矩阵, 产生这个矩阵的函数, 功能上类似一个高阶函数, 可以记作: T(tx, ty).
剪裁空间
x 是原坐标.
剪裁空间范围在(-1, 1)之间. 将世界坐标 -> 剪裁空间.
映射或者说是缩放到2个单位.
旋转
任意点旋转: 圆心移到(x0, y0)然后进行旋转
投影和透视
将3D图像中的每一个点映射到一个2D平面.
TODO 这里的问题还需要深入学习.
透视投影 – Perspective Projection
透视: 一种绘制物体的空间位置关系的绘画技术.
三维变换流水线
模型坐标: 从模型本身的维度去思考.
观察坐标: 眼睛(摄像机)的位置, 决定了物体的成像.
观察产生投影. 把3D图形转换成2D图形.
透视投影的观察体, 在这两个剪裁面之间的平面才会显示.
计算过程–y方向
x方向
结果
三维变换和模型的封装
三维缩放. 和二维的缩放一样, 可以看作一个产生矩阵的函数S(Sx, Sy, Sz).
3D旋转
z轴旋转
观察–眼睛和摄像机
世界坐标系如何转换到观察坐标系 – 平移, 旋转让观察坐标系与世界坐标系重叠.
纹理
纹理贴图 – Texture Mapping
- 也称作材质贴图, 把2D图片中的点映射到3D图形.
mipmap
- 图片被预先处理成多个相差2的指数倍数的图片
- 加速渲染
- 减少锯齿
- 256 * 256 的图片有8个层级
- 所以图片肯定是2的指数
球面坐标系
例子: 将[0, Π], [0, 2Π]都分成100份, 形成多组值.
模型组合
Webgl/OpenGL中纹理的管理
createTexture() 直接创建, 不会返回这个 Texture 的句柄….
游戏系统的特点
- 动画需要在指定的时间内完成
- 需要一个主时间
- 渲染可以和时间无关(看显卡)
光照
- 光具有粒子性也有波动性.
- 光在传播中会有反射, 折射, 吸收, 散射等现象.
- 从能量角度看, 光由辐射现象.
基础模型
点光源, 方向光源.
还有漫反射和材质.
BRDF 和 BSSRDF
光打在的点上其实就是 fragment, 像素.
- 光照片段的颜色是片段本身的颜色和关照效果的叠加
- 关照效果light取[0, 1]之间的值
- fragColor.rgb *= light
点光源
数学模型
手电筒
光线追踪
光线追踪 – Ray Tracing
通过追踪光线经过的路径, 渲染图形.
反射, 折射, 散射
光线追踪示例
射线经过像素点, 当射线触碰到物体的时候, 就会发生散射, 产生新的射线, 射线又会碰到新的物件, 如果是碰到是光源, 就要加亮, 碰到的是物体, 就要产生阴影, 不断综合场景中的所有光线. 最终来决定这个像素的颜色. 这条射线叫做 primary ray, 基础射线, 每个像素点都有一条.
栅格化渲染, 先有顶点坐标, 再到片段坐标, 再渲染片段, 最终产生结果. 是渲染3d空间中的物体的网格.
游戏开发
Agent 提供AI, 某种意义上来说, 玩家也是一个 Agent.
多个 GLSL 之间切换.
切换 program
- gl.useProgram(program)
- attribute
- uniform
- texture
- 活动记录模式
把整个程序想象成一个记录.
Framebuffer
- 帧缓冲区(存储最后渲染出来的效果)
- 可以附加纹理(直接绘制到纹理)