Alternate Realities大赛作品引发的思考(一)——用通俗的语言解释shader的渲染过程

背景

  • 本科学了四年的数字媒体技术,如今对流行的shader技术,合成(compositing),渲染还是一窍不通。
  • 基本写作业就是面向tutorial,没有办法响应需求,更没有办法形成自己的风格。
  • 思考有没有办法在短时间内对整个体系的掌握进行提升,于是就有了今天这个博客。
  • 目前我已经接触数媒,渲染领域不多了,主要在搞纯代码的开发,但是接下来有一两个游戏比赛,可能会涉及到技术美术。
  • 我还有没有办法快速的相应需求,有没有办法形成我们团队的风格,也就看这段时间博客的积累了。

目标

  • 重新思考面对当前的需求,我们对于计算机图形学的底层到底需要理解什么?
  • 完成Alternate Realities大赛的两个作品风格的复现:
    • 要求用自己的方式复现,多参考文字资料,尝试自己理解整个逻辑。
    • 拆解目标内容,细化到“水体”,“灯光”,“粒子系统”,“材质(反射)”,“合成”。
    • 不要求涉及动画,尽量少涉及建模。
  • 可以做到把目标场景迁移到unity中:
    • 尝试实时渲染。
    • 同样尝试通过shader解决一些和材质相关的问题。
  • 思考小型项目比较取巧的渲染技巧,例如高饱和度类型的,可以参考例如纪念碑谷,腾讯游戏上一届金奖,magicavoxel的渲染效果。

选择

  • 我在这里选择来自Caleb Worcester的作品,他的渲染作品对比起其他的组,相对元素较为简单,但是整体的观感非常舒适美观,这得益于:
    • 清晰的明暗结构。(shader构建的明暗非常清晰,观感简洁不冗余)
    • 颜色的选择。
    • 渲染以及去噪的效果难以置信的好。
  • 由评论区了解到,这个应该是由EEVEE渲染器渲染而成,这是来自blender的渲染器,所以我们先从blender入手来处理这个场景,然后再由把项目的思路迁移到unity中。

计算机图形学

关于过去

  • 我们本科修了两个计算机图形学,一个是普通,一个是高级。
  • 普通计算机图形学的是一个自己课件公式都推不清的老师讲的课,高级计算机图形学则是一直在用3dmax重复之前3D建模教的东西。
  • 学了这么多之后,你现在让我再理一遍管线,让我再写任何的shader,根据需求在任何的建模工具上实现一个效果,我都完全没有概念。

需求

  • 实际上,我们数媒学计算机图形学的核心需求是希望能够根据代码或者参数设计出自己想要的画面风格。
  • 就例如说,我希望场景里的光要像上面那副图一样,或者我场景里的花草要和原神的一样,场景里的人物要和旷野之息里的一样云云。

思路——如何从底层开始理解渲染

  • 我们这边抛开一切技术的角度,优先用直观的方式理解。
物体
  • 我们一个场景中的物体是有限的。
  • 我们以场景中的一个物体为例。
  • 一个物体由顶点,线和面构成。
  • 那么此时的物体只有点,线,面的概念,没有任何的颜色的概念存在于这个对象中。
  • 那这个时候我们需要通过顶点,线和面对这个物体进行着色,那么我们这个时候就需要一个着色函数(顶点, 线, 面),它理论上会让你得到你屏幕上的每一个像素的值。
着色器
顶点着色器,光栅化和片元着色器
  • 此时,会有两种着色函数登场,一种是顶点着色器,它返回顶点的颜色值。
  • 先从顶点开始讲起,我们可以自己写一个顶点着色器,例如我现在的目标是,把整个球全都染成白色,那你的步骤会是:
    • 遍历球上的每一个点。
    • 把球上的每一个点都赋值RGB(255,255,255)。
  • 你可能会说,这也太慢了,假设我的点很多,那我从头遍历一遍岂不是要很久?
    • 这其实就是为什么渲染要大量使用GPU的原因。
    • GPU的功能是可以做很多重复简单的计算。
    • 那么,一个简单的思路是,我们把所有的点分成100份。
    • 然后每个GPU单元遍历属于它的那部分。
    • 每个GPU单元再对它进行赋值。
    • 现在几乎所有的着色器都支持GPU自动的进行计算。
  • 你现在有了顶点的值,这时你可以大摇大摆的拿着**顶点,线,面,顶点颜色,相机位置※**这些数据给电脑渲染你的图像了。
  • 电脑一看,还行,这些值够我渲了,它于是就对照着顶点的位置先渲染了起来,它首先根据顶点位置,相机位置做了一个矩阵变幻,然后根据你屏幕的视口,把那个顶点和你电脑屏幕的某个像素点对应上了,此时可能会是:顶点1号=>{像素点(1334,2107), RGB(255,255,255)}这种感觉。
  • 而此时你一看,屏幕上就几个点,中间东西全没有,你问电脑这是什么情况:电脑说,我这还得做个光栅化,然后再加上片元着色器中间的东西才能出来呀;
    • 于是你就在思考光栅化是什么:光栅化其实就是把之前的线数据和电脑屏幕的像素对应上,毕竟直线是理想的,我们电脑屏幕只能根据像素去模拟直线。
  • 光栅化完了之后,那我们就用片元着色器对剩下的像素进行着色,具体来说就是遍历每一个点,根据你写的片元着色器的规则进行上色,
    • 例如你只写一个在几个顶点直接插值的片元着色器,那么得到的物体,就是一个通过三个顶点颜色进行插值的三角面构成的物体。
      Alternate Realities大赛作品引发的思考(一)——用通俗的语言解释shader的渲染过程_第1张图片
需求1:我不想要全都是白色了,我想让我的物体有它应该有的纹理。
  • 这个其实从直觉上思考并不难。
  • 你需要把这个3维平面展开成一个2维的图形,然后对这个2维的图形中的每一个像素进行赋值。(这就是UV)
  • 之后等到片段着色器渲染的时候,就会有某种特殊的数学运算,可以将二维图形和三维物体的位置和画面中的像素对应。
需求2:我想要加光照
  • 这其实就是做数学题题了,但实际上你现在对怎么做已经会有一些概念了。
  • 一个非常朴素的思路是,你可以遍历每一个顶点,计算每一个顶点通过光照应该得到的颜色。
  • 具体可以参考这篇文章:https://www.zhihu.com/search?type=content&q=phong%20shading
问题1:那背光面的顶点是怎么知道自己被遮挡了呢?
  • 你说你遍历每一个点,但是背光面的顶点也被光线直接照射,那他们怎么知道自己被其他的面和物体阻挡了呢?
  • 其实计算机还真没办法知道某个点被某个面阻挡了这个事情,所以他们每个顶点每个顶点的渲染。
  • 然后只要遇到更靠近自己的像素,就把后面的像素替换掉,这样背光面渲染的颜色有问题也不会影响实际产出的图片。
  • 其实这就是zbuffer算法。
需求3: 我觉得我基本理解了,我现在想做出上面Caleb Worcester的效果。
  • 其实我们仔细观察这个图中的一些对象,如人物:
    Alternate Realities大赛作品引发的思考(一)——用通俗的语言解释shader的渲染过程_第2张图片

  • 我们再放大一些:
    Alternate Realities大赛作品引发的思考(一)——用通俗的语言解释shader的渲染过程_第3张图片

  • 我们可以看到人物明暗分界线的色调几乎没有,这意味着在着色器中,它的shader对光照设定了阈值,当光照大于某个阈值,就全部全部赋为某个值;当小于某个值,就全部赋为某个值。

  • 从程序实现的角度来说,你可以认为是遍历shader中所有的顶点,或者处理一遍所有的片元,当光的值超过x,你就给那个位置赋颜色p;当小于某个值w,就给那个位置赋颜色v。

  • 在很多大型的项目中,都有使用这种技巧来处理shader,这种手法称为toon shading。
    Alternate Realities大赛作品引发的思考(一)——用通俗的语言解释shader的渲染过程_第4张图片

  • 这里就有关于toon shading完整的代码教程: http://rbwhitaker.wikidot.com/toon-shader。

  • 注意一点,我们似乎能隐隐约约的从图像中看到一点光晕的效果(flare, bloom, glow),这同样也和shader有关,我们也需要实现。

你可能感兴趣的:(计算机图形学,图形学)