计算机图形学 | 奇妙的真实感——片元着色
华中科技大学《计算机图形学》课程
MOOC地址:计算机图形学(HUST)
计算机图形学 | 奇妙的真实感——片元着色
9.1 图形渲染与视觉外观
图形渲染的光栅化阶段
光栅化阶段:
片元着色器:
片元操作:
基于视觉外观的渲染
视觉物理现象
视觉感知的物理过程:
- 太阳光与其他光源(天然或人造光)发出光
- 光与场景中的物体相互作用,部分被吸收
- 部分散射开来,向新的方向传播
- 最终,光被人眼感知
视觉现象
详情见于9.2节:奇妙的颜色。
光照计算
详情见于9.3节:光照明模型。
- 不同的物体表面材质
- 不同的光源
- 更复杂的情况:物体甚至可能是透明或者半透明的
- 更复杂的情况:物体之间还有相互作用
着色方式
详情见于9.3节:光照明模型。
- 平滑着色(Flat shading):简单来讲,就是一个三角面用同一个颜色。
- 高洛德着色(Gouraud shading):每顶点求值后的线性插值结果通常称为高洛德着色。
- 冯氏着色(Phong shading):冯氏着色是对着色方程进行完全的像素颜色求值。
表面细节
详情见于9.4节:让人头疼的纹理。
纹理决定了表面细节。
- 颜色纹理
- 几何纹理
有光有影才有真实感
详情见于9.5节:加入阴影会怎样?
9.2 奇妙的颜色
什么是颜色
美国光学学会(Optical Society of America)的色度学委员会曾把颜色定义为:颜色是除了空间的和时间的不均匀性以外的光的一种特性,即光的辐射能刺激规网膜而引起观察者通过规觉而获得的景象。
我国国家标准GB5698-85,颜色的定义为:颜色是光作用于人眼引起除形象以外的规觉特性。
英国科学家牛顿在1666年发现,太阳光经三棱镜折射后投射到白色屏幕上,显现出一条象彩虹一样美丽的彩色光带(称为光谱,spectrum),证明了白光是所有不同波长可见光的组合。
色散现象:
- 不同波长折射系数不同
- 折射后投影在屏上的位置也不同
可见光谱
人眼能看到的光波为波长约380-780纳米的光波。
人们色彩感觉形成的四大要素:
- 光源
- 彩色物体
- 眼睛
- 大脑
其中,光源辐射和物体反射属于物理学范畴,眼睛和大脑是生理学研究的内容。
颜色视觉的生理基础
颜色视觉原理(Color Vision Theory):
- Young -Helmholtz的三原色学说
- Hering的对立颜色学说(四色学说)
- 阶段学说
Young -Helmholtz的三原色学说
在人眼规网膜上存在感受红、绿、蓝色的锥状细胞,分别对红、绿、蓝三种光最敏感。
一切颜色特性都由这些锥状体的响应量比例来表示。
三者共同作用,使人产生不同的颜色感觉。
Hering的对立颜色学说(四色学说)
由视觉现象总结出的规律。
在人眼视网膜上存在三组对立规素:红-绿、黄-蓝、白-黑。
所有的颜色特性都由这三组对立颜色的响应量比例来表示,绿和黄—蓝两组对立颜色响应值的组合决定其色调,黑—白响应值决定其亮度。
阶段学说
人眼视觉现象
颜色辨别
人眼能分辨100多种颜色,对490nm的青绿色和590nm的橙黄色的光波变化最敏感,在可见光谱的两端最不敏感。
颜色对比
相邻区域的不同颜色相互影响,是由是规觉暂留效应引起的。
视觉暂留:规觉暂留效应即规觉暂停现象(Persistence of vision,Visual staying phenomenon,duration of vision)又称“余晖效应”,1824年由英国伦敦大学教授皮特‘马克’罗葛特在他的研究报告《移动物体的规觉暂留现象》中最先提出。
颜色对比的分类:
- 同时对比:对比区域的颜色不同,效果不同。
- 继时对比:当看了一种色彩再看一种色彩时,会把前一种色彩的补色加到后一种色彩上。
- 边界对比:当亮度发生跃变时,可看到有一种边缘增强的感觉。明度高的越高,明度低的越低;越接近边界线,影响越强烈,这也叫做马赫带效应(Mach Band Effect)。
- 色相对比:互补色、同类色、对比色、邻近色。
- 明度对比:因明度差异形成的对比,同一明度的色彩,在白底上显得暗,而在黑色背景上却显得更亮。
- 纯度对比:在纯度低的背景色上的会显得鲜艳一些,在纯度高的背景色上会显得灰浊。
颜色错觉
同化现象
当配色的色相、明度接近时,同化现象的效果越明显。
色彩的醒目性
交通标志上醒目的配色:
色彩的进退
色彩的冷暖
色彩的胀缩
色彩的软硬
色彩的情绪
颜色的表示
颜色纺锤体(颜色锥体)
颜色视觉模型:颜色三特性的空间表示
- 明度(亮度):垂直轴线表示白黑亮度变化
- 色调:水平圆周上的不同角度点,代表了不同色调的颜色
- 饱和度:从圆心向圆周过渡表示,同一色调下饱和度的提高。某个平面圆形上的色调和饱和度不同,而明度(亮度)相同。
CIE色度图
CIE(Commission Internationale de L‘Eclairage):国际照明委员会,根据其法语名称简写为CIE。
CIE 1931色度图是用标称值表示的CIE色度图。其中,x表示红色分量,y表示绿色分量,E点代表白光,它的坐标为(0.33,0.33),边界上的数字表示光谱色的波长。
所有单色光都位于舌形曲线上,这条曲线就是单色轨迹,曲线旁标注的数字是单色(或称光谱色)光的波长值;而自然界中各种实际颜色都位于这条闭合曲线内。
颜色模型
- RGB颜色模型
- CMY颜色模型
- HSV颜色模型
- HSL颜色模型
其中,RGB颜色模型和CMY颜色模型面向设备,HSV颜色模型和HSL颜色模型面向用户。
RGB颜色模型
采用三维直角坐标系,R-Red G-Green B-Blue。
- 构成一个RGB颜色立方体
- 通常使用于彩色光栅图形显示设备中
- 真实感图形学中的主要的颜色模型
CMY颜色模型
以红、绿、蓝的补色青(Cyan)、品红(Magenta)、黄(Yellow)为原色构成的颜色模型。
- 常用于从白光中滤去某种颜色,又被称为减性原色系统,在白色中减去某种颜色来定义一种颜色
- 用于印刷行业中
HSV颜色模型
HSV(HSB)颜色模型:一个基于颜色六边形的六棱锥。
归纳的要点如下:
- H(Hue):色调,用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算
- S(Saturation):饱和度,表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色不白色混合的结果。通常取值范围为0%~100%,值越大,颜色越饱和。光谱色的白光成分为0,饱和度达到最高。
- V(Value或Brightness):明度,表示颜色明亮的程度。
HSV模型对应画家的配色的方法:
- 在一种纯色中加入白色以改变色浓(白色越多S越小)
- 加入黑色以改变色深(黑色越多V越小)
- 同时加入不同比例的白色,黑色即可得到不同色调的颜色
应用:调色板
HSL颜色模型
HSL(HSI)颜色模型:
- H(Hue):色调,使用不水平轴之间的角度来表示,范围从0o到360o,从蓝色开始
- S(Saturation):饱和度,说明颜色的相对浓度
- L(Lightness)或者I(Intensity):亮度,在L=0处为黑色,在L=1处为白色,灰度沿着L轴分布
采用近似的圆柱坐标系:
- S=1,L=0.5 纯色彩
- S=0 仅有灰度
颜色模型间的相互转换
OpenGL中的颜色
两种颜色存储方式:
- 颜色值:像素点附加颜色信息之后,就必须为每一个像素点额外分配一个内存空间保存该点的颜色信息,对于RGB或者RGBA颜色模式,保存的数据直接代表其颜色值。
- 颜色索引:对于颜色索引模式,保存的是该颜色在颜色索引表中的位置,通过查颜色索引表对应到相应的颜色。颜色索引模式的优点是占用空间小,运行速度快,缺点是显示效果稍差。
随着硬件的提速升级,目前一般采用直接存储颜色值的方式。
RGB颜色模式
RGB模式中,RGB分别表示红绿蓝三色的分量。
每个分量在0.0~1.0之间。
通过设置RGB不同的比例,可以获得任意的颜色
RGB三个分量不颜色的对照:
RGBA颜色模式
RGBA模式中:
- RGB分别表示红绿蓝三色的分量
- A(实际上是α系数,Alpha Coeefficient)表示颜色的透明度
通过设置RGBA不同的值,可以获得任意的颜色。
OpenGL一般采用RGBA颜色模式。
颜色混合(Color Blending
当A(实际上是α系数,Alpha Coeefficient)不为1.0f,即颜色有一定透明度时,可以进行颜色混合。
RGBA源颜色值(Rs,Gs,Bs,As),RGBA目标颜色值(Rd,Gd,Bd,Ad),源调和因子(Sr,Sg,Sb,Sa),目标调和因子(Dr,Dg,Db,Da)。
新的调和颜色计算:SrRs+DrRd,SgGs+DgGd,SbBs+DbBd,SaAs+DaAd。
HDR(High-Dynamic Range,高动态范围图像)
特点:
-
RGBA分量都是在0.0f到1.0f之间。
-
记录大范围的颜色值,再向0~1之间映射。
决定颜色值的因素
- 颜色
- 光照
- 材质
- 纹理
- 遮挡
- 透明
9.3 光照明模型
光照模型的发展
1967年,Wylie等人第一次在显示物体时加迚光照效果,认为光强不距离成反比。
1970年,Bouknight提出第一个光反射模型:Lambert 漫反射+环境光。
1971年,Gouraud提出漫反射模型加插值的思想。
1975年,Phong提出图形学中第一个有影响的光照明模型。
Phong模型
简单光照明模型模拟物体表面对光的反射作用。
特点:
- 光源为点光源
- 反射作用分为:环境光(Ambient Light)、漫反射(Diffuse Reflection)、镜面反射(Specular Reflection)。
环境光(Ambient Light)
在没有光源的地方,景物没有受到光源的直接照射,但其表面仍具有一定的亮度,使它们可见。这是因为光线在场景中经过复杂的传播之后,形成了弥漫于整个空间的光线,称为环境光。
- 环境光是指光源间接对物体的影响
- 光在物体和环境之间多次反射,最终达到平衡
- 同一环境下的环境光光强分布均匀
- 近似表示:Ie=Ia∙Ka。其中,Ia为环境光强度,Ka为物体对环境光的反射系数
理想漫反射和镜面反射相关的点光源
点光源是位于空间某个位置的一个点,向周围所有的方向上辐射等光强的光,记其光强为Ip。
在点光源的照射下,物体表面的不同部分亮度不同,亮度的大小依赖于它的朝向以及它不点光源之间的距离。
理想漫反射(Diffuse Reflection)
在一个粗糙的、无光泽的表面呈现为漫反射。当光线照射到这样的表面上时,光线沿各个方向都作相同的反射,所以从任何角度去看这种表面都有相同的亮度。
漫反射的特点:光源来自一个方向,反射光均匀地射向各个方向,不视点无关。
由 Lambert 余弦定律,漫反射光强为Id=IpKd×cosθ。
其中,Ip是入射光的强度,Kd是不物体有关的漫反射系数,0< Kd <1,θ是L和N的夹角0≤θ≤π/2L是P点指向光源的方向矢量N是物体表面在P点的法矢量。
多个点光源(m个):
镜面反射(Specular Reflection)
镜面反射遵循反射定律,反射光位于表面法矢的两侧。
对于理想的高光泽度反射面,反射角等于入射角时候,光线才会被反射。这时,如果观察者正好处在P点的镜面反射方向上,就会看到一个比周围亮得多的一个高光点。
非理想的反射面,只要其表面是光滑的,在点光源的照射下,也会产生一块特别亮的区域,称为高光点。尽管这时镜面反射光的强度会随α角的增加而急剧地减少,但观察者还是可以在α角很小的情况下接受到这种改变了方向的一部分镜面反射光。
多个点光源(m个点光源):
综合表示
假定只有一个光源,Phong光照明模型的综合表述:由物体表面上一点P反射到规点的光强I为环境光的反射光强Ie、理想漫反射光强Id和镜面反射光强Is的总和。
I=IaKa+IpKd(L∙N)+IpKs(R∙V)n
Phong模型的实现
公式:I=IaKa+IpKd(L∙N)+IpKs(R∙V)n
近似处理
为了减少计算量,假设:
- 光源在无穷远处,L为常向量
- 视点在无穷远处,V为常向量
- H为L不V的二分向量H=(L+V)/2
- 用(H•N)近似(R•V)
节省计算时间的原因:
- 在L和V为常量,对所有的点只需计算一次H的值
- H的计算比R的计算简单
近似计算后的高光区域更大:
多个光源(m个点光源)
光强计算假定只有一个点光源,若在场景中有m个点光源,则可以在任一P点上叠加各个光源所产生的光照效果:
光的衰减
在同一光源的照射下,距光源比较近的景物看起来会亮一些,而距光源较远的景物看起来会暗一些,这是因为光在传播的过程中,其能量会収生衰减。
令距离为d,最简单的衰减因子为1/d2。d很小时,1/d2产生过大的强度变化;d很大时,1/d2变化太小。
实际上采用一个不d相关的函数:
于是:
Phong光照明模型的RGB颜色模型形式
假定选择RGB颜色模型:
RGB公式:
如何消除或者减弱马赫带效应?
方法一:多边形细分
方法二:明暗处理
典型方法:Gouraud明暗处理
多边形内部各点颜色的计算:对顶点颜色双线性插值。
典型方法:Phong明暗处理
多边形内部各点法矢量的获得:对顶点法矢量双线性插值
Phong明暗处理效果好于Gouraud明暗处理,但是计算量也更大。
OpenGL中的实现
基于Phong模型模拟平行光、点光源和聚光灯效果:
平行光
平行光:不加衰减因子。
无穷远处照射过来可以看作是平行光而且没有衰减。
代码实现:
// 计算定向光
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
if(!light.on) {
return vec3(0.0);
}
// 漫反射
vec3 lightDir = normalize(-light.direction);
float diff = max(dot(normal, lightDir), 0.0);
// 镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// 在漫反射光下物体颜色
vec3 diffuseColor = vec3(material.diffuse);
// 计算环境光,漫反射光和镜面光
vec3 ambient = light.ambient * diffuseColor;
vec3 diffuse = light.diffuse * diff * diffuseColor;
vec3 specular = light.specular * spec * vec3(material.specular);
return ambient + diffuse + specular;
}
点光源
点光源:保留衰减因子。
有穷远处照射过来,有衰减。
代码实现:
// 距离和衰减
float d = length(light.position -fragPos);// 距离
float attenuation = 1.0 / (light.c + light.l * d + light.q * d * d);// 衰减
......
// 可以实现距离越近就会越亮,距离越远就越暗的效果
return (ambient + diffuse + specular) * attenuation;
聚光灯
聚光灯:光锥的内圆锥和外圆锥。
代码实现:
// 聚光强度
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff -light.outerCutOff;
// 这里为了使聚光效果看起来边缘更加圆滑,我们需要模拟聚光有一个内圆锥和外圆锥,通过内外圆锥之间的余弦值差来计算聚光强度(intensity),clamp函数把聚光强度约束在了[0,1]之间。
float intensity = clamp((theta -light.outerCutOff) / epsilon, 0.0, 1.0);
......
// 漫反射和镜面反射需要乘以聚光强度
return (ambient + (diffuse + specular) * intensity) * attenuation;
全局光照
全局光照的概念
定义:全局光照(Global Illumination,简称GI), 或被称为 Indirect Illumination,间接光照。
全局光照 = 直接光照 + 间接光照
Whitted光透射模型
Whitted光透射模型 = Phong模型 + 透射光强 + 反射光强(对其余物体反射光的反射)
光照明模型的发展:
光线追踪算法
全局光照的主要算法流派:
- Ray tracing 光线追踪;
- Path tracing 路径追踪;
- Photon mapping 光子映射;
- Point Based Global Illumilation 基于点的全局光照;
- Voxel-based Global Illumilation 基于体素的全局光照;
- Ambient occlusion 环境光遮蔽;
而其中的每种流派,又可以划分为多种改进和衍生算法,有些又相互关联。如路径追踪,就是基于光线追踪,结合了蒙特卡洛方法而成的一种新的派系。
光线投射
光线投射(Ray Casting),作为光线追踪算法中的第一步,其理念起源于1968 年,由 Arthur Appel 在一篇名为《Some techniques for shading machine rendering of solids》的文章中提出。具体思路:从每一个像素射出一条射线,然后找到最接近的物体挡住射线的路径,而视平面上每个像素的颜色取决于从可见光表面产生的亮度。
加上光线追踪
1979 年,Turner Whitted 在光线投射的基础上,加入光不物体表面的交互,让光线在物体表面沿着反射、折射等方式上继续传播,直到不光源相交。
这一方法后来也被称为经典光线跟踪方法、递归式光线追踪(Recursive Ray Tracing)方法,或Whitted-style 光线跟踪方法。
光线追踪的定义:追踪光线路径,然后模拟光线不虚拟对象相互作用的方式。主要思想:从视点向成像平面上的像素发射光线,找到不该光线相交的最近物体的交点
- 如果该点处的表面是漫反射表面,则计算光源直接照射该点产生的颜色
- 如果该点处表面是镜面或折射面,则继续向反射或折射方向跟踪另一条光线如此递归下去,直到达到结束条件(光线不光源相交、逃逸出场景或者达到设定的最大递归深度)。
光线追踪过程
如果该点处的表面是漫反射表面,则计算光源直接照射该点产生的颜色。
如果该点处表面是镜面或折射面,则继续向反射或折射方向跟踪另一条光线。
结束条件
- 到光源
- 逃逸出场景
- 递归到一定深度
存在的问题
传统的逆向光线追踪算法有两个突出的缺点:
- 表面属性的单一:完全看不见折射光?完全没有镜面反射?
- 不考虑漫反射:漫反射成为结束条件
改进方法
通过模型的修正来缓解这两个问题,将一个表面的属性认为可以是混合的。
比如:它有20%的成分是镜面反射,30%的成分是折射,50%的成分是漫反射折射。
改进后算法变为:从视点发出一条光线,光线与物体表面相交时根据表面的材质属性继续采样一个方向。
改进后的新问题:计算量很大。
解决办法:蒙特卡罗方法。通过概率理论,进行近似简化。
衍生出路径追踪
路径追踪的定义:为光线追踪的衍生技术,同样是追踪光线路径,但是一种引入了蒙特卡洛方法的全局光照技术。
路径追踪 = 光线追踪 + 蒙特卡洛方法
效果演示
光线追踪:
路径追踪:
全局光照:
9.4 让人头疼的纹理
纹理的概念
用简单光照明模型生成真实感图象,由亍表面过亍光滑单调,反而显得不真实。现实物体表面有各种表面细节,这些细节就叫纹理。
纹理:体现物体表面的细节。
纹理类型:
- 颜色纹理:物体表面(平面戒者曲面)花纹、图案
- 几何纹理:基于物体表面的微观几何形状
纹理的定义和映射
生成纹理的一般方法,是预先定义纹理模式,然后建立物体表面的点与纹理模式的点之间的对应。
当物体表面的可见点确定之后,以纹理模式的对应点参与光照模型进行计算,就可把纹理模式附到物体表面上。这种方法称为纹理映射(Texture Mapping)。
纹理模式定义:
- 图象纹理:将二维纹理图案映射到三维物体表面,绘制物体表面上一点时,采用相应的纹理图案中相应点的颜色值。
- 函数纹理:用数学函数定义简单的二维纹理图案,如方格地毯。戒用数学函数定义随机高度场,生成表面粗糙纹理即几何纹理。
纹理映射:
- 建立纹理与三维物体之间的对应关系
- 扰动法向量
纹理模式定义方法:纹理空间。
纹理定义在纹理空间上的函数,纹理空间通常是一个单位正方形区域 0 ≤u ≤ 1,0≤ v ≤1之上。
纹理映射方法:建立物体空间表面和纹理空间之间的对应关系。
根据物体空间的表面坐标(x,y,z)计算其纹理空间坐标(u,v)值,对物体表面坐标(x,y,z)用u、v进行参数化,然后反求出参数u、v用物体表面坐标(x,y,z)的表达,根据纹理空间定义的纹理(u,v)得到该处的纹理值,并用此值取代光照明模型中的相应项,实现纹理映射。
实例:圆柱面映射
假定有一个半径为r,高为h的圆柱。
基于OpenGL的颜色纹理
纹理映射的一般方法:
-
设置纹理属性加载纹理生成纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); -
加载纹理
unsigned char *data = stbi_load(“res/texture/contain.jpg”, &width, &height, &nrchannels, 0); -
生成纹理
glGenTextures(1, &texture1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); -
绑定纹理
glBindTexture(GL_TEXTURE_2D, texture1);
立方体贴图
立方体贴图包含6个2D纹理,每个2D纹理是一个立方体(cube)的一个面。
几何纹理的概念及算法
几何纹理方法:对物体表面几何性质作微小扰动,产生凹凸不平的细节效果,给物体表面图象加上一个粗糙的外观。
物体表面上的每一个点P(u,v),都沿该点处的法向量方向位移F(u,v)个单位长度,新表面位置:P’(u,v) = P(u,v) + F(u,v) * N(u,v)。
几何纹理算法:
年代 | 算法 | 思想 |
---|---|---|
1978 | Bump Mapping(凹凸贴图) | 计算顶点光强时,不是直接使用原始法向量,而是加上一个扰动 |
1984 | Displacement Mapping(移位贴图) | 直接作用于顶点,根据DisplacementMapping中相对应的像素值,使顶点沿法向移动,产生真正的凹凸表面(除了移位贴图以外,表面并没有变得真正的凹凸不平) |
1996 | Normal Mapping(法线贴图) | 通过height map获得法向量信息,而且对应的RGB值表示法向量的XYZ,利用这个信息计算光强,产生凹凸阴影的效果 |
2001 | Parallax Mapping(视差贴图) | 通过视线和height map的计算,陡峭的视角就给顶点更多的位移,平缓的就给较小的位移,通过视差获得更强的立体感 |
2005 | Relief Mapping(浮雕贴图) | 更精确地找出观察者视线与高度的交点,实现更精确的位移 |
从Phong模型可见,法向量N不同,光照计算结果就不同。
光强计算假定只有一个点光源,若在场景中有m个点光源,则可以在任一P点上叠加各个光源所产生的光照效果:
法线贴图
现实中的物体表面并非是平坦的,而是表现出无数(凹凸不平的)细节。
每个fragment 使用了自己的法线,我们就可以让光照相信一个表面由很多微小的(垂直于法线向量的)平面所组成,物体表面的细节将会得到极大提升。这种每个 fragment 使用各自的法线,替代一个面上所有 fragment 使用同一个法线的技术叫做法线贴图。
问题一:height map的使用
高度图
高度图上存储的是RGB值,但每个颜色通道实际上是表面的法线坐标:
- R红色通道对应x方向
- G绿色通道对应y方向
- B蓝色通道对应z方向
通过height map获得法向量信息,即对应的RGB值表示法向量的XYZ,利用这个信息计算光强,产生凹凸阴影的效果。
问题二:切线空间的引入
一个平面上顶点的法向量方向会根据height map取值
多个不同朝向的平面呢?需要将相对于某个平面的法向量变换到全局中!
每个平面都有一个自己的切线空间:
TBN矩阵的计算:
基于OpenGL的法线贴图
在OpenGL中的实现TBN矩阵: