找回密码
 立即注册
查看: 247|回复: 0

图形学01-渲染管线概述

[复制链接]
发表于 2022-12-24 17:53 | 显示全部楼层 |阅读模式
写在前面:本文仅限个人学习使用 图源网络 侵权立删 如有错误请指正
1.什么是渲染?
渲染是在电脑绘图时,以软件由模型生成图像的过程。模型是用语言或者数据结构进行严格定义的三维物体或虚拟场景的描述,它包括几何,视点,纹理,照明和阴影等信息。图像是数字图像或者位图图像,渲染用于描述:计算机视频编辑软件中的效果,以生成最终视频的输出过程。
渲染是三维计算机图形学中的重要的研究课题之一,并且在实践领域与其他技术密切相关,在图形流水线中,渲染是最后一项重要步骤,通过它得到模型与动画的最后显示效果。
现在已经有各种不同的渲染工具产品,有些集成到更大的建模或者动画中,有些是独立产品,有些是开源的产品,从内部来看,渲染工具是根据各种学科理论,经过仔细设计的程序,其中有:光学、视觉感知、数字以及软件开发。
三维计算机图像预渲染或者实时渲染的速度都非常慢。预渲染的计算强度很大,需要大量的服务器运算完成,通常被用于电影制作;实时渲染常用于三维视频游戏,通常透过图形处理器(GPU)完成这个过程。

2.实时渲染
数据与资源(模型贴图什么的)->渲染管线->render Target
因为考虑到软件和硬件的使用不同,渲染管线会存在很大的差别,所以不存在统一的渲染管线的说法。

3.以unity为例



一般讲的是GPU端的渲染管线,但CPU段的渲染逻辑也十分重要。



Unity渲染管线内置渲染管线渲染流程图。蓝色代表CPU做的一些东西,绿色指GPU做的东西。(如果多个摄像机出现,每个摄像机都会把流程跑一遍,最后效果的处理是:unity里每个新相机出现之后都会进行一次清除(clear flag)也可以选择不清除,就会变成(Don't clear)就会两个相机的物体相互叠加。)

4.简化流程
分为CPU应用端渲染逻辑(剔除,模型数据等信息打包)->GPU渲染管线(画出来)->Frame Buffer(帧缓冲区)



5.CPU应用端渲染逻辑(开发者控制)
应用阶段包括以下三个阶段:
1.把数据加载到显存中。
所有渲染所需的数据都需要从硬盘(Hard Disk Drive,HDD)中加载到系统内存(random access memory,RAM)中。然后网格和纹理等数据又被加载到显卡上的储存空间——显存(Video Random Access Memory,VRAM)中。这是因为,显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接访问的权利。



真实渲染中需要加载到显存的数据比图所示复杂许多。例如:顶点的位置信息,法线方向,顶点颜色,纹理坐标等。当把数据加载到显存后,RAM中的数据就可以移除了。但对于一些数据来说,CPU仍然需要访问他们(例如,我们希望Cpu可以访问网格数据用来做碰撞检测),那么我们就不希望这些数据被移除,因为从硬盘加载到RAM的过程十分耗时。
2.设置渲染状态
渲染状态就是这些状态定义了场景中的网格是如何被渲染的。例如,使用哪个Vertex shader,光源属性,材质等。如果我们没有更新渲染状态,那么所有的网格都将使用同一种渲染状态。
3.调用Draw call
Draw call 就是一个命令,它的发起方是CPU,接收方是GPU。这个命令仅仅会指向一个被渲染的图元(primitives)列表,而不会包含任何材质信息——这是因为我们已经在上一个阶段中完成了。当给定了一个draw call时,GPU会根据渲染状态(例如材质,纹理,着色器)和所有的输入的顶点数据来计算,最终输出成屏幕上显示的那些漂亮的像素。而这个计算过程,就是GPU流水线。



视锥体剔除是摄像机根据Field of view的参数,远近裁面这四个参数构成的立方体,视锥体剔除是指模型和视锥体做一个碰撞检测,如果有相交的部分会被绘制,如果没有相交就不会。但是同时碰撞检测也会非常消耗性能,所以做一个简化处理,AABB包围盒。用包围盒跟他进行碰撞检测。

层级剔除(Layer Culling Mask)Unity : inspector-Layer-TransparentFX-camera-Culling Mask把TransparentFX关掉就可以了

排序:在unity里渲染最重要的是确定渲染队列(Render Queue)我们可以在材质球中选择Render Queue的数值。数值越小,表示物体越先被渲染。如果相等的话怎么处理,默认给材质赋shader的时候,假设物体是不透明物体,RenderQueue是2000,如果是半透明的物体,RenderQueue是3000unity把RenderQueue<2500的认为是不透明序列,>2500的是透明序列。渲染的顺序会根据物体距摄像机的距离排序,从前到后渲染。透明序列则会根据距离摄像机的距离从后到前渲染。必须严格按照。半透明渲染是最头疼的问题。
打包数据



什么是模型?



8 vertices 八个顶点
6 vertex normals六条法线(同一方向的是表示一条)
texture coords UV (因为UV是平面的,所以第三个信息可以删掉)
下边的1/1/1那个列表是索引列表,分别对应。下边的12行对用了十二个三角面的意思。
最后打包完成后调用两个指令,setpasscall和drewcall。



6.GPU渲染管线(分为几何阶段和光栅化阶段)








shader就是GPU上的一段程序,所有的顶点都会调用一遍顶点shader,顶点shader最主要的任务就是要完成投影成像的转换的过程,把模型空间下的顶点坐标转换到裁剪空间,以便下一步硬件可以将点映射到屏幕上边。3,最重要的是输出在裁剪空间下的顶点坐标,也可以做自定义的参数,以便后边的片元shader做更复杂的处理。硬件会做一些图元装配以及光栅化的处理,在三角形的内部生成片元,每一个片元都会携带顶点阶段输出的数据,进入片元shader(Fragment Shader),每一个shader都会调用片元shader进行处理。

顶点shader
顶点着色器(Vertex shader)是完全可编程的,它通常用于实现顶点的空间变换,顶点着色器等功能。
曲面细分着色器(Tessellation shader)是一个可选的着色器,它用于细分图元。
几何着色器(Geometry shader)同样是一个可选的着色器,它可以被用于执行逐图元(per-Primitive)的着色操作,或者被用于生产更多的图元。
顶点着色器是流水线的第一个阶段,它的输入来自于CPU,顶点着色器的处理单位是顶点,输入进来的每个顶点都会调用一次顶点着色器。它本身不可以创建或者销毁任何顶点,而且无法得到顶点与顶点之间的关系。例如:我们无法得知两个顶点是否属于同一个三角网格。但是正因为这样的相互独立性,GPU可以利用本身特性并行化处理每一个顶点,这意味着这一阶段的处理速度会很快。它的主要工作是:坐标变换和逐顶点光照。除此之外,它还可以输出后续阶段所需的数据。坐标变换:一个最基本的顶点着色器必须完成的一个工作是,把顶点坐标从模型空间转换到齐次裁剪空间。o.pos = mul (UNITY_MVP, v.position);
类似这句代码的功能是,把顶点坐标转换到齐次裁剪坐标系下,通常由硬件做透视除法后,最终得到的归一化设备坐标。(Normalized Device Coordinates,NDC)
顶点shader就是模拟拍照的过程,通过顶点shader处理后,视锥体被转换变形成一个比例为2*2*的立方体(CVV)顶点shader不会产生2D图像,只是让他变形,拍扁的是硬件。模型软件有很多不同的坐标系,需要有一个共同的坐标系来统一,就是引擎内部的坐标,叫做世界空间坐标系。(World space)通过一个矩阵来转换,这个矩阵就是(Model Matrix)是unity可以帮助生成的。矩阵可以帮忙完成一个点一些点的平移、旋转,缩放等的空间变换操作。然后就是一个视图矩阵,(View Matrix)把坐标传递到相机空间的位置。第三步摁下快门,就是投影成像的过程,把三维世界的物体按照进大远小的透视规则,映射到视网膜前面,映射到相机的平面。只是进行了变形处理,相应的变换过程,投影矩阵。最后就处于裁剪空间的。三个矩阵也可以合在一起,叫MVP矩阵,有了MVP矩阵,就可以一步将模型空间转换到裁剪空间。

图元装配以及光栅化阶段(硬件操作阶段)
剪裁(Clipping):删除不再摄像机视野内的顶点剪裁掉,并剔除某些三角图元的面片。这个阶段是可配置的。例如:我们可以使用自定义的裁剪平面来配置裁剪区域。也可以通过指令控制裁剪三角图元的正面还是背面。
对那些部分在摄像机视野范围内的图元进行一个处理,这就是裁剪。
屏幕映射(screen mapping)这一阶段不可配置和编程,它负责把每个图元的坐标转换到屏幕坐标系中。
屏幕映射是把每个图元的任务是把每个图元的x和y坐标转换到屏幕坐标系(Screen Coordinate)下。屏幕坐标系是一个二维坐标系,它和我们用于显示画面的分辨率有很大关系。屏幕映射不会对输入的z坐标做任何处理。实际上,屏幕坐标系和z坐标一起构成了一个坐标系,叫窗口坐标系(Window Coordinates)这些值会一起被传递到光栅化阶段。注意opengl和dx的区别,前者把左下角当最小值,后者把左上角当最小值。如果图形倒转,有可能是此差异造成。
裁剪操作是应用在三角面的,背面剔除阶段,顺时针分布排列背面,逆时针分布排列正面。视口转换转换成屏幕坐标。图元装配前边的操作都是在对顶点操作。图元装配连线成封闭三角形,我们叫图元。

光栅化(重头戏)
三角形设置(triangle setup)和三角形遍历(Triangle Traversal)也是固定函数(Fixed-Function)的阶段。
片元着色器(Fragment shader)是完全可编程的,它用于实现逐片元(Per-Fragment)的着色操作。
逐片元操作(Per-Fragment Operations)阶段负责执行很多重要的操作,例如修改颜色,深度缓冲,进行混合等,它不是可编程的,但具有很高的可配置性。
通过图元内部差值计算生成片元。光栅化就是把3d的信息生成2d的信息,光栅化直接忽略了z轴信息,只用xy的信息。z值也会同样产生差值信息然后储存在每一个片元上边。所以NDC坐标的z值就变成了每个片元的深度值。根据z值判断哪一个应该放在前边。帮助我们处理前后遮挡的关系。。光栅化会产生锯齿,跨像素的时候



这些数据可以在顶点shader输出的时候输出的自定义数据。生成片元之后,每个片元都会逐个调用一次片元shader,这些是并行操作的一个过程。

片元shader


纹理技术,纹理图像在计算机里就是二维数组,用纹素地址储存的颜色值。纹理采样,纹理坐标就是uv坐标,对用的unity里有纹理过滤机制失真的机制,取相近值(point)做比如正方形纹理,轮廓很清晰的就有用。一般情况下都会使用双线性插值(Bilinear)纹理过滤机制就是解决小图像映射到大块区域引起纹理失真的效果。
大图像映射到小图像引起的失真,用 Mipmap来调节,会产生很多不同的大小,根据映射区域的大小来选择对应的纹理图(不是很严谨,但可以这样理解)对应unity,纹理链等级越高,图像尺寸越小,越模糊。    纹理寻址:如果查找的区域超过纹素地址,就用到了纹理寻址,对应unity的Clamp模式,GL_CLAMP_TO_BORDER 模式,一旦超过周围会变成纯色GL_CLAMP_TO_EDGE  一旦超过会复制他边缘像素。还有repeat模式,GL_REPEAT 重复复制GL_MIRRORED_REPEAT重复复制镜像。对应unity。纹理压缩格式:


我们储存的图片是不会被直接使用的,我们会转换成平台支持的格式,比如pc平台,支持DXT,BC等。ASTC是最新的压缩格式,ETC2是安卓常用的压缩格式,PVRTC是苹果平台很老的压缩格式。
光照计算:


现在还是直接光照在游戏里占主导。直接光照的实时计算。BRDF的函数太复杂,所以简化的最出名就是phong 光照模型,phong光照模型认为,物体的光照由diffuse漫反射光,specular高光,ambient环境光组成。漫反射:我们认为光照射到一点是均匀反射的,强度由点的法线向量和太阳光的夹角组成。



两个向量的点乘是cos值,就可以判断夹角的大小。
镜面反射:



射入后反射成等角分布,phong光照模型的光照大小是取决于摄像机viewDir和反射光线reflectDir的夹角,
环境光:



环境光是间接光照,比如有Light map,Reflection Probe反射球,其实是基于图像的一种照明,IBL(Image Based Lighting)可以实现镜面反射的环境光。实现的原理是球谐光照SH,可以实现一些低频的漫反射的环境光。max(N(法线方向)·L(光照方向),0.0)+pow(max(R(反射光照)·V(视线方向),0.0),smoothness(光滑度)}+ambient(纯色或其他)=phong



基本框架(用来分析光照,也可以写shader光照部分的思路)



直接光漫反射(看看有哪些光照模型可以用,比如phong光照模型的漫反射计算公式)
直接光镜面反射(有哪些公式可以用,比如PBR光照模型的GGX高光方程)
间接光漫反射(球谐公式SH,提供低频的漫反射)
间接光镜面反射(反射球来捕捉周围的环境,结合IBL技术,提供间接光的镜面反射)
如果学到了更高级的技术,还可以扩充基本框架。比如学了环境遮挡(AO)的技术,SSR屏幕空间反射,比如SSS此表面散射 只要按照这样的思路去思考,把效果补全,效果都不会太差的。

输出合并阶段
最重要的任务就是处理遮挡的关系,处理半透明的计算。


片元数据(color depth)->Alpha测试->模板测试(Stencil Test)->深度测试(Depth test)->混合(Blending)->帧缓冲区(color buffer depth buffer stencil buffer )alpha值,如果某些数值低于alpha值的话就把他丢弃掉。深度测试,怎么判断片元在前边,在前边就要被渲染。将两个最终的位置混合就会得到一个结果。帧缓冲区是一个临时的画布。
帧缓冲区。如果比缓冲区小的话就能通过测试。(深度值)更小的深度值更能通过测试,因为他代表了更靠近相机。
深度测试有两个选项是可以控制的,在shader里控制,ZWrite深度写入和ZTest深度测试。zwrite如果被关闭也不会被写入深度缓冲区,但是不影响颜色缓冲区的写入。
unity官方文档会写将深度测试放在顶点shader之后,这种说法也是正确的,提前深度测试early-z这种行为是为了优化。跟硬件有关,如果硬件支持就支持,不支持就没有。
输出合并:


Blending渲染颜色之前会拿到颜色的颜色值,然后对比在相同位置上面的颜色缓冲区Color值,两个颜色值做混合计算然后得出新的值。 半透明混合公式:Alpha Blend=SrcColor*SrcAlpha+DestColor*(1.0-SrcAlpha)蓝色是可以改变的部分 SrcColor对应当前渲染的颜色值,DestColor对应位置颜色缓冲区里的颜色值。shader:Blend SrcAlpha OneMinusSrcAlpha 有了这段代码就可以对混合控制。Srcalpha就是渲染对应当前片元的alpha值 后边的英文代表1-SrcAlpha
实现柔和的叠加模式的公式:Soft Additive=SrcColor*SrcAlpha+DestColor*1.0
shader:Blend SrcAlpha One
最终的效果是一个叠加变亮的效果。
半透明混合一定要谨慎从后到前混合(片元级别很难保证是完全从后到前,所以会发生很多错误,很难避免),关闭ZWrite
7.管线总览



GPU管线总览

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 15:02 , Processed in 0.089951 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表