Games202(1`前3章和作业0)
Games202_Real-Time High Ouality Rendering 实时高质量渲染
第一章_课程总览
本次总共有14节课程,和6个作业:
- 实时软阴影
- shadow mapping阴影贴图和解决失真
- PCF阴影抗锯齿
- PCSS软阴影
- VSSM软阴影
- SDF shadows有向距离场软阴影
- 实时 Global Illum 全局光照
- 基于图像的照明:LBL
- 预计算:PRT
- 阴影贴图:RSM
- 3D空间:LPV,VXGI
- 屏幕空间:SSAO,SSDO,SSR
- 基于物理的光照模型
- RTR中PBR材质:Microfacet Model, Disney principled BRDF;
- 线性变换的余弦:LTC;
- 非照片真实感的渲染:NPR
- 实时光线追踪
- temporal accumulation时间积累
- Gaussian filtering高斯滤波
- joint bilateral filtering联合双边滤波
- SVGF时空方差引导滤波
- RAE循环自动编码器
- 工业界技术
- cascaded/tiled/deferred shading,
- particles,
- engines,
- etc.
作业框架
介绍
作业框架会使用webgl API:
WebGL 它是一套针对web网页实现交互式3d图形的跨平台api,为了实现这样的目的,webgl是是基于 OpenGL ES 2.0 (Opengl的子集,具有较少的功能)的 API,在调用它时,实际上调用底层的opengles,使得在支持 HTML5 的 canvas 标签的浏览器中, 可以完成渲染,
WebGL 程序包括: JavaScript 写的控制代码,html与网页交互,glsl着色器代码,webglapi图形接口
(以往的例如opengl包括:c/c++写控制代码,glsl着色器代码,openglapi图形接口)
作业框架path搭建
作业0给出了两种方法,搭建本地服务器,我们使用推荐的方法下载vscode插件_LiveServer本地服务器
在我们的作业框架0,Ctrl+Shift+P调出命令行窗口,LiveServer:OpenwithLiveServer命令浏览器将自动打开指定地址的本地服务器
作业0文件结构
第3方库都存放在lib文件夹下面:
- imgui.js,轻量级用户界面库
- loader.js,模型加载,可以加载obj,mtl
- three.js,是对webglapi的封装,使得webgl简单易用
- ……
在src文件夹下存放源文件:
//网格体
- pointlight点光源:点光源的属性,顶点数据是立方体,材质是EmissiveMaterial
- loadsmodel:
- 接受一个mtl和obj的文件,
- 同样包含了网格体需要的各种属性,并添加了meshrender
//材质
- material材质基类:(包含uniform变量,材质属性,shader src)
- 函数:compile编译,返回链接的着色器
- lights:光照模型材质
- EmissiveMaterial材质继承自 Material:包含材质属性,选用的着色器
//网格
- mesh网格:(存储网格体的顶点数据)
- 构造函数:顶点数据
- cube顶点数据
//渲染
- meshrender网格渲染器:(这里存储一切渲染网格体的数据:顶点数据,材质属性,着色器……)
- 构造函数:
- 初始化和创建4个缓冲区用来存储顶点数据
- if检查如果有某类数据,bind后传入缓冲再解绑
- 设置材质属性,编译着色器
- 函数:draw渲染网格体
- 创建MVP矩阵
- 设置VAO
- bind上下文
- 传入uniform变量
- 调用drawElements开始执行渲染流水线
- 构造函数:
- WebGLRenderer主渲染器:
- 各类网格体数组
- 函数:网格体添加到数组,对于light是在这里为它添加meshrender组件
- 函数:render开始渲染所有网格体
- 设置webgl指令
- for循环所有的网格体,启用shaderprogram,传入uniform,调用meshrender.draw渲染
//着色器
- loadshader:
- 函数:通过THREE.FileLoader()加载指定目录的文件
- shader着色器类:
- 接受vs和fs
- 编译函数
- 链接函数
- internalshader:保存实际的vs,fs代码
//纹理
- texture纹理类:
- 接受图片路径,
- 为它分配内存,
- 设置一些纹理属性
- bind它
//程序入口
- engine入口点:
- 初始化 WebGL 上下文
- THREE库创建摄像机,并设置属性
- 设置窗口回调函数
- 创建场景、渲染器,将场景添加到渲染器
- GUI库创建用户界面
- 主循环中:调用渲染
//网页映射
- index.html前端:
- 由html包括所有的代码
- head部分包括:设置画布风格style、link链接资源文件、指定src源文件
- body部分:创建canvas画布<canvas id=
作业问题
如果渲染不出人物,需要指明使用的纹理资源:
1
<link rel="preload" href="/assets/mary/MC003_Kozakura_Mari.png" as="image" type="image/png" crossorigin />
展开
代码框架渲染结果:
Blinn_Phong光照模型:
为了使用光照模型我们需要新增和修改:
- 在internalshader添加新的vs,fs着色器
- 添加Blinn_Phong材质文件
- 修改loadobj文件,为模型使用新材质,myMaterial =
- 修改html文件,新增文件路径
第二章_CG基础概念
渲染流水线,opengl,glsl,渲染方程,通过类比了解它们的概念
这里简述一下图形API:
CPU与GPU的通信,需要4点:将需要传递的数据放在RAM上,通过IO操作将一些控制码写入GPU对外开放的寄存器,数据地址也写入GPU的专用寄存器,给GPU一个开始信号
显卡驱动程序就是编码了上述的各种细节,但是这种细节不同厂家不同,甚至不同型号芯片设计架构也不同,
为了使得图形程序能够跑在不同的硬件平台和操作系统(针对跨平台API)上,在显卡驱动程序上又创建了抽象层(每个驱动针对不同芯片,api使得支持所有芯片),为不同的操作系统和硬件提供统一接口,这就是图形API
……略
第三章_阴影
shadow mapping阴影纹理
第一次从光源位置渲染,仅需要一张深度图,第二次从摄像机方向渲染,可视点会转换到光空间,和阴影纹理比较深度值,作为是否是阴影的依据
失真
自遮挡:
由于屏幕和纹理都有分辨率,因此场景中的一部分区域都会映射到同一个像素中,跟这个像素存储的深度值(中心采样点出发的)比较,当同一平面(深度相差非常小)的一个区域内,会有区域深度值 > 阴影纹理对应像素的深度值,因此会出现黑白的失真
当平面相对光倾斜角度越大,失真越明显
要解决这个失真有两种方法:
- 偏移量basic,原理是让深度变化相差不大的保持一致性,原来是>深度值的判定为阴影,现在是>深度值+basic一定偏移后的才判定为阴影
- 还有一种添加basic的方法(second-depth shadow mapping ),通过两次pass,取首次深度和次深度的中间深度,根据这个深度判定,相当于自动添加basic
悬浮:
当添加了basic后,很有可能出现悬浮问题,目前还没有什么好的解决方法
锯齿:
PCF(Percentage Closer Filtering)近距离过滤百分比
pcf是用来解决阴影的锯齿问题的
对于摄像机的每个像素,都去从shadowmap查找周围3*3的临近像素的深度值,取平均的结果,
PCSS(Percentage Closer Soft Shadow)
pcss是由pcf衍生的,当filter size很大,就可以实现软阴影
但是我们要进一步优化一下,filter size不是固定的,它需要根据遮挡物的和阴影接收物的距离决定filter size == penumbra,当距离越远,filter应该越大,越接近于软阴影,那么filter size如何确定呢?
此公式描述了filter size 过滤大小,可以想象当blocker上下移动时,filter的大小,它符合相似三角形,wlight / dblock == wpenumbar / d(receiver - blocker)(下半部分)
首先我们要求出filter size,需要知道3个参数:
- 已知wlight,
- 需要知道dblocker光到遮挡物的距离,但是遮挡物和光都并非一个点(就比如两个平面的距离,我是以什么确定呢,以中心点还是其他?),因此需要取一定范围内的平均blocker值,即光平面的4个顶点,连线shader point的锥形体积内均可能存在block物体
- 还需要知道dreceiver光到shader point的距离(可以通过矢量减法得到)
根据公式就可以算出filter size,然后正常进行pcf即可
– 那么如何求在锥形体积内dblocker的平均值呢?
可以查找shadowmap中的值(因为它就保存了光源到最近物体即block的距离),但是SM上的所有像素也不是全部都是block,只有遮挡住p点的才作为block
但是从shadowmap的查找范围又是多大呢?
- 第一种可以简单的从shadowmap上取固定大小,如果shadowmap的像素可以遮挡住物体,判定为block,如果不能那就不是遮挡物不用考虑
- 另一种更好的方法是假设shadowmap位于light平面的近平面上,从而当光距离变化,shadowmap的采样区域也会随之变化(比如光平面在原理shaderpoint时,遮挡区域会更小),正好符合了视锥体的区域,对shadowmap的范围取平均……
但是可以想象到这样的方法对性能影响非常大