图形学基础 - 深度缓存 & Blinn-Phong着色模型 & 着色频率

目录

深度缓存 Z-buffer

深度信息

Z-buffer算法

Shading 着色

Blinn-Phong着色模型

需要定义的新东西

额外说明

1.漫反射着色-Diffuse

2.高光着色-Specular

3.环境光-Ambient

着色频繁

常见的三种

预备知识

顶点法线

差值处理


深度缓存 Z-buffer

光栅成像时,我们怎么处理场景中间物体相互叠加的情况呢?


深度信息

我们知道场景里面的物体都是由三角形构成的,每一个三角形在最后成像时会占据很多个像素, 对于每一个三角形,三角形可以覆盖到的每一个像素投影到三角形上所走的距离就是深度信息

所以每次我们把距离像素最近的那一个物体的这个像素渲染出来就行了,使用深度信息寻找距离这个像素(采样点)最近的物体

Z-buffer算法

Z-buffer算法就是在渲染每一个图的时候都在更新每一个像素的深度信息,使这个像素始终记录着最浅的深度信息,保证接下来显现的是最近的物体的像素

生成一张图片的时候,我们往往同时进行两个事情

  1. 一个是渲染图片的颜色
  2. 同时我们还要更新每一个像素(采样点)的深度信息,记录最近的

怎么维护?首先我们保证得到了每一个三角形对于每一个采样点的深度信息

  1. 就是先初始每一个像素深度为正无穷
  2. 在光栅开始时,遍历每一个三角形和每一个三角形的所有区域
  3. 如果这个区域距离像素更近,就更新这个像素的深度数组,同时渲染的颜色改为这个区域的颜色
  4. 如果比记录最近的远,就什么都不做

两个三角形类模拟一下这个过程,其中三角形的每一个区域的深度信息都已知:

最后生成的深度二维数组zbuffer[ ][ ]如果绘制成一张图片就是下图右边,深度大的白色, 进的是黑色,在我们深度二维数组更新完毕后,左边的图其实也就生成好了的,是同步进行的,这样再送这张图上屏幕,就没有卡顿了(双缓冲原理)

最后,光栅化就可以显示正常覆盖的图片了


Shading 着色

进入着色前,我们学了什么?

首先我们有相机和物体,然后通过第一节的变换我们可以将相机移到原点,并且始终看向的是-z方向,同时一起变换物体,把物体使用投影变换投影到(-1 , 1)的区域内,在使用第二节光栅化和深度缓冲的知识将物体投影成屏幕上的像素,投影时就是进行采样,进行MSAA抗锯齿

所以现在我们需要知道每一个像素的颜色是什么,这个颜色对于光源来说,我们看到的明暗的是什么样子的

Blinn-Phong着色模型

这个模型着色解决了三问题:

  • 漫反射的着色
  • 高光着色
  • 全局保底亮度

需要定义的新东西

这时候就需要着色了,使用Blinn-Phong反射模型,是一个着色模型,使用这个模型之前,需要知道一些物理量:

  1. 着色点:这个是着色的基本单位,是一个单位面积,这个点有颜色信息(纹理),光滑程度
  2. 法向量:这是单位向量,垂直于平面
  3. 观测方向:也是个单位向量,由着色点指向摄像机
  4. 光源方向:也是个单位向量,由着色点指向光源

额外说明

这个着色模型着色的时候,我们只考虑目前的这个着色点,周围没有其他物体,光线是没有遮挡的。只考虑光源和物体表面方向或者材质的作用,所以是没有因为遮挡出现的影子,但是平面因为自己方向问题造成的明暗是可以着色出来的

(上图没影子,但是有明暗)

1.漫反射着色-Diffuse

着色点接受的光的能量会因为角度有差别

物体为什么有明暗?是因为物体上的每一个着色点所在平面的方向与光源存在夹角,所以,着色点接受的光线能量大小是直射时的能量乘以着色点法线与光源向量的夹角的余弦值

光源的能量在传播是也会有消减

光源的能量在传播是也会有消减,假如光源的能量是 I , 那么经过距离R ,剩下的能量是\frac{I}{R^{2}}

当我们考虑这两个因素,就知道光源到达我们这个着色点的能量大小,和物体可以接受多少能量了,这样,我们可以以这个能量大小作为我们的亮度值,结合着色点的颜色和粗糙度,来漫反射这个着色,计算出不管我们从哪一个方向看,这个点的着色(着色就是这个点的亮度或者颜色)

解释物理意义:

  • Ld就是出去的漫反射的能量(颜色)
  • I/r^2 就是光源到达这个点之后剩下的能量
  • n l 是cosθ , 取max是为了反向来的光值为0
  • kd 是一个镜面反射系数,就是它决定反射多少出去,0表示全部吸收是黑色, 1 表示全部反射,就是漫反射的能量自己一点也不私吞。他也可以设置成RGB,形成颜色

注意,不管我们从哪一个方向看这个点,着色都是不变的,就是计算出来的这个Ld

这样我们就知道了对于每一个粗糙表面着色点的漫反射的着色值了

2.高光着色-Specular

之前我们知道,由公式:

我们知道,kd这个系数是决定物体的亮度或者颜色,因为它是控制反射出去的能量的大小或者颜色的,下图就是kd越大,说明反射越强,物体越亮

现在我们来考虑高光,什么时候你可以看到高光?就是你眼睛在往一个特定范围的方向看着色点的时候,你可以看到光源的高光(如下图)。观察方向和光源反射的方向接近的时候可以观察到

怎么描述观测方向和光源反射方向接近呢?我们可以做v I 的角平分线h, 当观测方向和光源反射方向接近的时候,就会和法线 接近,所以可以是用 h n 的接近程度来决定反射多少高光出去

所以我们得到高光反射的着色公式:

解释物理意义:

  • Ls就是出去的高光的能量(颜色)
  • ks 同样是一个反射系数,高光一般是很亮的白色,所以这个一般是一个很接近一的数
  • p 的作用就是为了将cos的有效值集中在角度很小的时候,保证我们的 h n 非常接近的时候才会看到很亮的高光,一般是取200

看下图,当我们加上漫反射和高光,系数kd 和  指数 p 的作用很慢,明显了:

这样着色中就实现了高光的着色

3.环境光-Ambient

为了保证物体渲染出来黑的地方不是太黑,我们整体提高一个亮度,就是给一个最下限的亮度,这个叫环境光,环境光的大小La就是:

解释物理意义:

  • La就是环境光的能量(颜色)
  • ka 同样是一个反射系数,看这个物体黑不黑体了
  • Ia就是 环境光的亮度的能量值

结合上面的三种着色,对于一个物体,我们就可以着色出他的样子了

先是环境光,加上漫反射的颜色和明暗加上高光,就得到了由Blinn-Phong着色模型渲染出来的一个物体了


着色频率

常见的三种

着色的单位不是点吗?这个点的选取可以决定着色频率,目前有三种着色频率:

1.把每一个构成物体的三角形作为一个着色点,直接按照三角形的法线和方向计算出着色

2.把每一个顶点作为着色点,按照顶点的法线,计算出着色,然后,顶点之间的三角形的填充是按三个顶点的着色渐变填充

3.把每一个像素作为着色单位。按照三角形三个顶点的法线,中间每一个像素都渐变式的生成一个法线,然后每一个像素按照这个法线计算一个着色

预备知识

顶点法线

那么顶点的法线怎么定义呢?

就是构成这个点的三角新的法线的求和,在除以长度和。当然可以加权求平均

公式:

差值处理

怎么平滑填充三角形?怎么平缓过度的得到三角形里面像素的法线呢?这需要用到差值处理,后面在补充

  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用OpenGL实现Blinn-Phong光照模型的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <GL/glut.h> // 材质参数 GLfloat mat_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat mat_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_shininess[] = { 50.0 }; // 光源参数 GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; // 窗口大小 int window_width = 800; int window_height = 600; void init(void) { // 初始化材质参数 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); // 初始化光源参数 glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); // 启用光照和深度测试 glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } void display(void) { // 清除颜色缓冲区和深度缓冲区 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 绘制一个立方体 glutSolidCube(2.0); // 交换前后缓冲区 glutSwapBuffers(); } void reshape(int width, int height) { // 设置视口大小 glViewport(0, 0, (GLsizei)width, (GLsizei)height); // 设置投影矩阵 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // 设置模型视图矩阵 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 更新窗口大小变量 window_width = width; window_height = height; } int main(int argc, char** argv) { // 初始化GLUT库 glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(window_width, window_height); glutInitWindowPosition(100, 100); glutCreateWindow("Blinn-Phong Lighting Model"); // 调用初始化函数 init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); // 进入主循环 glutMainLoop(); return 0; } ``` 此代码实现了一个简单的立方体和Blinn-Phong光照模型。其中,材质参数和光源参数可以根据需要进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值