文章

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图形接口)

WebglAPI参考语法

作业框架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 />

展开

代码框架渲染结果:

1736590293180

Blinn_Phong光照模型:

为了使用光照模型我们需要新增和修改:

  • 在internalshader添加新的vs,fs着色器
  • 添加Blinn_Phong材质文件
  • 修改loadobj文件,为模型使用新材质,myMaterial =
  • 修改html文件,新增文件路径

1735971174925

第二章_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如何确定呢?

1735978969942

此公式描述了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

1735978995872

但是从shadowmap的查找范围又是多大呢?

  • 第一种可以简单的从shadowmap上取固定大小,如果shadowmap的像素可以遮挡住物体,判定为block,如果不能那就不是遮挡物不用考虑
  • 另一种更好的方法是假设shadowmap位于light平面的近平面上,从而当光距离变化,shadowmap的采样区域也会随之变化(比如光平面在原理shaderpoint时,遮挡区域会更小),正好符合了视锥体的区域,对shadowmap的范围取平均……

但是可以想象到这样的方法对性能影响非常大

本文由作者按照 CC BY 4.0 进行授权
本页总访问量