文章目录
画家算法
对于一个人他在绘图的过程中一般是先画远处的, 再用近处的物体覆盖, 计算机中也是这样
假如我有这样一个正方体, 怎么把它绘制到屏幕上呢? 也即如何把它光栅化, 我可以先画后面的边, 再画左边的, 再画右边的, 这里存在的问题是我无法知道绘图的顺序, 这个表示远近应该怎么解决呢?
- 这里一个朴素的思想是把所有的物体深度都排序, 然后按顺序绘制, 但是这里的前提是每个物体的深度都是规则的, 只有越来越大或者越来越小, 所以遇到下面的图就不行了, 下图中两两重叠, 不能单独说谁更近谁更远
### Z-Buffer
- 因为对空间中的三角形不容易排序, 那就对每个像素排序, 那么这个时候我们需要一个深度图或者说深度缓存, 它存了每个像素的深度信息
frame buffer就是生成的结果, 另一个就是深度buffer, 如下图:
- 深度越小越黑,反之变白
- 之前的时候我们规定了相机朝向是-z, 也就是z越小就越远, 数字大就离相机越近
现在在深度中我们同样有这个z表示距相机的距离, z越小代表越近
深度缓存算法
-
假设一开始所有像素的深度都是无限远的
-
对每个三角形的每个像素, 假如它比之前这个位置的深度小(深度小就代表距离近, z都是正的),就把它替换之前的
- 下面这个图就很直观的表明了我的整个过程, 图上的数字表示深度, 红蓝表示RGB值
- 这种算法是和绘制的顺序无关的
着色(Shading)
目前学到的有经过一些变换从三维顶点得到了二维顶点, 然后二维顶点可以连接成三角形进行光栅化
在图形学中着色可以理解成对不同物体应用不同的材质, 比如金属球, 木球, 它们和光线有不同的交互
Blinn-Phong Reflectance Model
- 这是最简单的着色模型
假设光源在右上, 有镜面导致的高光, 有漫反射, 有环境光
- 对于一个要着色的点我把它微分为平面, 当作平面来处理
v是观测方向, I 是光照方向, n 是法线方向, 既然指的是方向, 那么这几个都是单位向量
表面的参数比如有 shininess, color …
这里的着色不包括阴影, 不考虑其他物体的存在,只考虑一个点
漫反射
光线均匀的反射到各个方向上
不同的角度漫反射接收到的能量不同
光源假设为点光源, 辐射出的能量按球壳出现, 每个球壳的能量一致, 球壳越大, 单位面积能量越小, 大致是个平方关系
S = 4 Π r 2 S=4Π r^2 S=4Πr2
每个点的光强关系如下, I I I 是单位长度的光强, 接受的多少取决于角度的cos值 , 负数的时候无意义, 因为相当于光线从下面过来
- kd 是漫反射系数, 代表这个点吸收多少光, 因为不同的材质对光的吸收是不一样的, 假如是1就代表完全不吸收, 假如是0就全吸收了, 这个点就是黑色了
- 假如是个三通道的RGB值, 就可以定义一个三维的kd了
- 这里注意漫反射是和入射角有关的, 只不过和你的观察角度无关, 也就是和v无关
查看kd的影响:
Specular Term (镜面反射, 高光项)
- 什么时候能看到这个高光呢? 很容易想到, 当我的观察方向和出射方向差不多的时候可以看到. 也就是观察方向 v 和 反射方向 R 接近
- Blinn-Phong 模型做了改进, 不是衡量 v和R, 而是衡量 法线 n 和 半程向量 h, 所谓的半程向量如下, 就是光线入射方向和观察方向求和然后单位化为 单位向量, 就是一个中间方向
假如n 和 h接近就相当于 v和R接近, 接近程度用cos的幂表示
- 为什么用幂呢? 因为高光的话偏离一点就看不到了, 所以衡量接近程度需要用幂, 如下图:
这样就减少了角度, 看下面这个例子, 横向随着幂次p越来越大 ,镜面反射接近一个点
Ambient Term (环境光照项)
- 环境光是四面八方的, 每个点接收到的环境光是相同的, 每个像素点有个环境光系数
综合
- 现在综合环境光, 漫反射光, 镜面反射光得到了最后的结果
着色频率 (shading Frequencies)
1. flat shading
对于每个三角形求解其法线, 两个边叉乘就行, 每个三角形都是同样的像素
2. Gouraud shading (高洛德着色)
- 这里假设三角形每个顶点的法线可以求出来, 那么三角形中间的像素值通过插值得到
3. Phong shading
- 这是对每个像素求个法线, 是求出顶点的法线后对其余像素做插值求法线
- 对每个像素进行着色
4. 着色频率的探讨
- Phone 着色模型一定比Gouraud 方式要好吗 , 这不见得,比如下面每列是不同的着色方式, 会法线随着三角形面片的增加, flat shading 也可以有很好的效果
怎么求出顶点的法线呢
一个顶点会和周围的三角形有联系, 在这里, 一个顶点的法线是其他面片的法线的平均, 但是这里不是直接的平均, 而是按面积加权的平均
- 现在假设我已经知道了顶点的法线, 那么如何计算三角形内部像素的法线呢?下面的图上我知道了左右两边顶点的法线, 然后通过某种方法得到中间像素渐变的法线
需要借助重心坐标得出
不要忘了归一化, 因为法线是个方向
Graphics pipeline (Real time Rendering) 实时渲染管线
三维空间的点投影到平面, 然后形成三角形, 光栅化为不同的像素, 进行着色, 最后输出图片
- 在这里有两种定义是一样, 一个是定义模型的顶点和三角形, 一个是只定义顶点, 三角形在顶点投影后再计算, 这里三角形是不会变的,因为只是坐标变了相对位置没变
下面逐个解释
顶点处理对应的是坐标系的变换
- 下面是三角形光栅化, 也就是得到那些像素应该显示在屏幕上
- 经过光栅化产生一系列的像素或者说fragment 后,判断每个像素是否可见, 使用z-buffer
- 最后是着色, 发生在顶点和像素上, 假如是高洛德着色那就是顶点, Phong 着色就是对像素着色
Texture mapping (纹理映射)
- 看下面这个三角形, 每个顶点对应不同的属性, 就是说每个点的木头纹理不同, 做到这件事就是纹理映射
或者看下面这个图, 对于这个球来说它就是两个点光源相加, 但是有的地方红, 有的地方蓝,说明它有不同的漫反射系数, 定义任意一个点的不同属性比如颜色啥的, 就是这个texture , 比如3DMM中的RGB
理解怎么定义点的不同属性
- 具体是定义在哪呢? 是定义在物体的表面上, 物体表面又怎么去理解呢?
对于下面的图, 它可以展开成2维的, 也就是说对于一个三维的物体它可以展开为2维的表面, 这个就是texture, 把这个再包回去, 就是texture mapping
又比如下面的图, 假如我三维模型中的三角形可以在二维的texture中找到对应的三角形, 这就可以实现映射了
具体怎么把空间中的三角形映射到纹理上我们不去管,我们认为已经有了这样一个映射关系, 下面关注下纹理上的坐标是啥呢?
- 纹理上的坐标系是uv坐标系
- uv的范围约定在0~1之内
Shader Programs (着色编程)
shader 分为顶底shader 和像素或者说fragment shader, 这个shader是通用的
-
如果是顶点着色器就叫vertex shader, 如果是像素着色器就叫做fragment shader, 也有的翻译成片段着色器, 其实就是像素着色器
-
像素着色器其实是得到像素最后的颜色并输出
下面是一个简单的openGL 的 GLSL语言写的小着色器:
首先定义了texture, lightdir, uv, norm, 具体操作是先得到漫反射系数kd, 然后再求解夹角, 这里有个负号, 其实是定义光线入射方向时导致的, 之前提到的入射方向朝外, 在这里是向里, 所以加了个负号