Shading

Shading

着色 引入明暗和颜色的不同 对不同物体应用不同材质

A Simple Shading Model (Blinn-Phong Reflectance Model)

shadingpoint

shading是局部的,只考虑光源的影响 不考虑其它物体的存在 是对每个点而言的

  • Diffuse Reflection漫反射

Lambert漫反射模型
光真正接受的是光照进来的法线分量 $\cos \theta = l \cdot n$ (l,n均为单位向量)

光源到距离为r的shading point的强度I变为$\frac{I}{r^2}$

$L_d = k_d(\frac{I}{r^2})\max(0,n \cdot l)$

$L_d$是漫反射的光 $k_d$指的是漫反射的系数,为1则全部反射,为0则全部吸收 max是为了剔除夹角大于90°的光

  • Specular Term 高光项
    Phong反射模型
    specular

V close to mirror direction当视线和入射的镜面反射接近时 <=> half vector near normal 半程向量h和法线接近(点乘为0)
Blinn-Phong反射模型
$L_s = k_s(\frac{I}{r^2})\max(0,n \cdot h)^p$
$h = \frac{v + l}{\big\lvert \lvert v + l \rvert \big\rvert}$
$k_s$为镜面反射系数
本来要考虑多少入射被吸收(l·n) 但被简化
但只用夹角余弦太宽泛所以要有系数p p控制高光的大小 需要一个指数p加速衰减

  • Ambient Term 环境光

ambient

假设认为环境光都是相同的 确保没有地方是黑的 精确要全局光照的知识

$L_a = k_aI_a$

blinnphong

$L = L_a + L_d + L_s = k_aI_a + k_d(\frac{I}{r^2})\max(0,n \cdot l) + k_s(\frac{I}{r^2})\max(0,n \cdot h)^p$

Shading Frequencies

之前是着色的方法/模型 现在是着色的频率

  • Flat shading 平面着色
    求三角形法线 逐面
    计算很快 很明显的看到一块块面形状

  • Gouraud shading
    求顶点的法线,对顶点着色,三角形内部通过差值算出
    法线:顶点关联的面的法线的平均(或加权)再标准化 (不精确
    (按理来说Gouraud用的是双线性插值)

  • Phong shading
    逐像素 像素法线由顶点法线重心插值

几何模型相对复杂(面密度大)时shading频率可以小一些

Graphics (Real-time Rendering) Pipeline 图形管线

pipeline

顶点处理 -> 三角形处理 -> 光栅化 -> 片元处理 -> 逐片元操作
其中,Vertex和Fragment阶段是可编程的
GPUs:可并行的图形管线处理器

  • Vertex Processing 顶点处理: 对所有顶点MVP变换 得到二维平面坐标(zbuffer的z值)
  • Triangle Processing 三角形处理: 所有的顶点按照原几何信息,变成三角面
  • Rasterization 光栅化
  • Fragment Processing 片元处理(片元可能比像素更小 如MSAA采样点):
    进行shading
    此时Vertex Processing也会处理(需要顶点信息对三角形内的点属性插值/Gouraud shading中顶点处理阶段就算出每个顶点的颜色值)
    可以去做texture mapping,利用texture的信息来代替blinn-phong模型漫反射系数来当作颜色
  • Framebuffer Operations
    zbuffer 处理遮挡关系 将所有的像素颜色信息整合在一起,输送给显示设备显示
shader 自行编程控制顶点和像素(片元)如何进行着色代替原来固定的顶点处理和片元处理 GLSL  

Texture Mapping 纹理映射

定义任何一个点着色时的属性

将三维物体上的任意一个点都映射到一个二维平面之上

只需要将每个点的颜色信息即漫反射系数存储在二维的Texture纹理之上

纹理上有坐标u-v映射到三角形上 u,v在(0,1)内

只需要在三维world space中每个顶点的信息之中存储下该顶点在texture space的(u,v)坐标信息

特殊的纹理tile 重复拼接之后上下左右都是连续的 可以复制很多张贴

Barycentric coordinates 重心坐标

Interpolation Across Triangles
对于三角形所在平面上的任意一点的坐标,都可以用三角形的三个顶点坐标的线性表达式表
示(和重心区别)

展开得
barycentric

Barycentric Coordinates

另一种视角:
barycentric2
$p=a + \beta(b-a) + \gamma(c-a) = (1-\beta - \gamma)a + \beta b+ \gamma c$

重心坐标最重要的运用便是插值 无论是位置,颜色,深度,法线向量等等

投影后的重心坐标会改变 所以三维空间中的属性在三维空间中做插值再投影(如深度)

重心坐标一定要是原世界坐标空间中的重心坐标,但实际计算中一般会使用投影之后的二维平面来计算重心坐标(重心坐标往往都是在屏幕空间下所得到的),存在着一个误差需要校正

重心坐标任意属性的正确插值:

Z为世界坐标中的深度值 其它为屏幕坐标所得

推导:
https://www.zhihu.com/column/c_1249465121615204352
https://zhuanlan.zhihu.com/p/403259571

Applying Textures

1
2
3
4
for each rasterized screen sample (x,y):
(u,v) = evaluate texture coordinate at (x,y)
texcolor = texture.sample(u,v);
set sample's color to texcolor;

出现的问题

A pixel on a texture — a texel (纹理元素、纹素 纹理上的像素)
pixel是投影在屏幕上的 texel是贴在三维图形上的

  • 纹理太小

    把一张100x100的纹理贴图应用在一500x500的屏幕之上必然会导致走样失真
    屏幕空间的几个像素点对应在纹理贴图的坐标上都是集中在一个像素大小之内
    多个pixel映射到了同一个texel

    Texture Magnification 纹理放大

    • Bilinear Interpolation 双线性插值
      对非整数的纹理值取四个相邻的采样点 水平+竖直做两次插值,即双线性插值 Lerp
      Bilinear Interpolation
    • 对周围16个点做三次插值,双三次插值 Bicubic,运算量更大,结果更好
  • 纹理太大
    纹理贴图大小500x500,屏幕空间100x100,将屏幕空间的像素点均匀分布在纹理空间之中,那么1个屏幕空间像素点所占的平均大小就是5x5=25个纹理空间像素
    用一个点采样的结果代替纹理空间一片范围的颜色信息,必然会导致严重失真!(采样频率过低无法还原信号原貌)
    一个pixel对应了多个texel
    近处锯齿远处摩尔纹
    Supersampling超采样可以解决但计算量太大
    footprint
    Mipmap
    一个采样点的颜色信息不足以代表footprint里一个区域的颜色信息,可以求出这样一个区域里面所有颜色的均值
    点查询Point Query 迈向 范围查询Range Query (仅取正方形的范围)
    多级渐远纹理 一系列的纹理图像,后一个纹理图像的分辨率是前一个的二分之一 再选择最适合像素大小的一个
    mipmap
    多余存储空间为1/3
    Mipmap
    Computing Mipmap Level
    由相邻像素点(取当前像素点的右方和上方的两个相邻像素点)转换到纹理空间后求在Texture space的距离取最大值, level D = log2L 从多个纹理图像中选最合适的

    若D非2的整数倍:
    四舍五入 or 利用D值在向下和向上取整的两个不同level进行三线性插值
    trilinearinterpolation
    三线性插值,就是在向下取整的D level上进行一次双线性插值(前文提过),再在D+1 level之上进行一次双线性插值,这二者数据再根据实际的连续D值在向下和向上取整的两个不同level之间的比例,再来一次线性插值

    对于在纹理上一个像素方块形成不规则的形状(远处很模糊):

    • Anisotropic Filtering各向异性过滤 可以处理矩形的形状
      准备不同级别的纹理贴图时,不再是简简单单横纵纹素各减小一半进行分
      级,而是长减半宽不变 or 宽减半长不变 or 长和宽各减半三种情况各进行一次分级
      多余存储空间为3倍 算出水平方向的level D1,再算一个竖直方向的level D1,然后算根据这两个level去各项异性过滤的texture里面找一张最合适的
    • EWA filtering 使用很多的圆形来覆盖不规则区域,通过多次查询来覆盖对应区域
      Anisotropic Filtering

Applications of textures

纹理可理解为一块数据用于不同类型的查询

Normal Maps

  • 存储object space下的法线向量坐标
    一但该法线向量的三角形面发生了变形(如人物模型在做各种动画),该法线向量就不再正确

  • 存储切线空间之中的法线向量坐标
    切线空间(tbn)tangant轴(t)、bitangent轴(b)及法线轴(N)所组成的坐标系 z轴由原来该面上的几何法线n构成 x,y轴分别由该面所对应的贴图上U,V增加的两个方向构成
    真正存储的时候只需要t 和 n即可,第三轴可以直接叉乘得到。并且将t和n作为顶点的属性进行存储
    详解: https://zhuanlan.zhihu.com/p/139593847

Environment Map

假设光源无限远,只记录光照的方向信息,这种贴图被称作环境光贴图
Spherical Environment Map 环境光信息描述在球面纹理上(上方和下方均有较为严重的扭曲)
利用观察方向相对于法线的反射方向去查询环境映射的颜色值
Cube Map 天空盒 但会需要额外判断某一方向上的光照应该记录在立方体的哪个面上,
计算量更大
cubemap

Textures can affect shading 凹凸贴图 法线贴图 改变法线方向 给人凹凸的效果

Bump Mapping 凹凸贴图

Normal Maps直接存储了法线信息,而Bump Maps存储的是该点逻辑上(物理上没有)的相对高度(可为负值),该高度的变化实际上表现了物体表面凹凸不平的特质,利用该高度信息,再计算出该点法线向量,最后再利用该法线计算光照 多了从height到normal向量
由切线算法线
bumpmapping2d

p点原来的法线朝上,即n(p) = (0, 1)
上图蓝色曲线为使用法线贴图后的效果
通过dp = (c[h(p+1) - h(p)])求出两点的高度差。其中c为常数表示凹凸贴图的影响程度,h为p点、p+1点对应高度
因此切线可表示为(1,dp)
切线与法线为垂直的关系因此n(p) = (-dp, 1)

bumpmapping3d
所有计算出来的法线都是局部坐标即切线空间之下,因此还需要左乘[t b n]矩阵转到(世界)相机坐标系之下得到正确法向

Displacement mapping 位移贴图

和上一个相比改变了几何 去对模型的顶点做位移

Shadow Mapping 阴影贴图

阴影 => 摄像机能看到的地方,光源看不见

  1. 把光源当做一个摄像机去渲染整个场景从而得到从光源视角的深度Buffer,记为dmap(ShadowMap)
    shadowmap1
  2. 从设定好的摄像机位置去真正的渲染场景
    shadowmap2
  3. 将所有摄像机视角可见点,利用光源视角下的那一套投影矩阵,重新投影回光源,得到光源视角下的屏幕坐标,找到该屏幕坐标在dmap上的深度值
    • 如果和投影回光源的点的实际深度值相等,则说明此点可被光源照射,不在阴影中
    • 如果小于投影回光源的点的实际深度值,则说明此点不可被光源看见,即该点前方有物体遮挡,在阴影中
      shadowmap3

如果在阴影之中就不去计算Blinn-Phong中的镜面反射项与漫反射项

  • 细节:
    1. 浮点数难以判断相等,所以一般会有一个tolerance
    2. shadow maps查询时不采用双线性插值,只寻找最近的点,因为倘若插值发生在物体边缘时,与邻接点的深度差距很大,会导致插值结果会有很大的误差
    3. 属于硬阴影(棱角分明),只适用于点光源 软阴影为光源具有体积,导致,不同地方看到光源的比例不同
    4. 渲染出来的阴影比较脏
      原因:深度值的比较位浮点数比较,而判断浮点数相等势必会产生误差,虽然处理精度的方法有很多种,但并不能从本质上解决问题
    5. 走样
      原因:本身储存的深度图存在分辨率限制,与渲染时的分辨率搭配不好的话,就会产生走样

×

喜欢就点赞,疼爱就打赏