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

UnityShader渲染管线

[复制链接]
发表于 2021-11-22 13:21 | 显示全部楼层 |阅读模式
本文主要讲渲染流水线的全过程和各种模版测试、透明度测试等的发生顺序,是一篇渲染管线的笔记。
游戏里的渲染流水线(渲染管线)分为几个阶段,不同的文献里可能有不同的分法,但都大同小异,大概可以分为应用阶段、几何阶段、光栅化阶段。
在开始之前,先复习下OpenGL和DirectX、NVIDIA相关的知识:


应用程序是直接和图像编程接口进行交互,调动他们提供的接口发出 DrawCall 指令,而目前的图像编程接口常用的有 GLSL、HLSL、和CG 三种高级语言,他们是 CPU 和显卡驱动进行交互的中间语言,会被显卡驱动翻译为 GPU 听得懂的机器语言,从而命令GPU开启渲染。
乐乐的《UnityShader入门》对三大语言的介绍大致如下:
GLSL语言的编译是由显卡驱动来完成的,只要显卡驱动支持对GLSL的编译它就可以运行,因此GLSL不是依赖操作系统层级的,而是依赖硬件的,这使得它有良好的跨平台特性。世界上有许多显卡制造商,他们对GLSL的编译实现不尽相同,所以可能会出现编译结果不一致的情况
HLSL是微软来控制着色器的编译,就算使用了不同的硬件,同一个着色器编译的结果也是一样的(版本相同的情况下),不过支持HLSL就仅有微软自家的产品:Windows、Xbox等
CG是真正意义的跨平台,他会依据平台的不同,而编译成相应的中间语言,CG和微软进行了合作,所以CG语言和HLSL语言非常相似,可以无缝移植成HLSL代码,缺点是可能无法发挥出OpenGL的最新特性。
UnityShader是用自创的ShaderLab语言编写的,这种语言只是为了方便我们进行Shader的编写,我们可以在ShaderLab内部嵌套 CG/HLSL 语言来编写顶点、片元着色器。这些 CG/HLSL 代码是嵌套在Pass中的这写指令之间的:
CGPROGRAM
    //CG/HLSL代码
ENDCG如果你想用GLSL语言来写也可以,那就是把代码嵌套在 GLSLPROGRAM ... ENDGLSL 之间,不过这样的话,你的游戏就得放弃PC、Xbox这样仅支持DirectX的平台。
显卡制造商为了让他们制作的显卡可以同时和OpenGL\DirectX合作,就必须提供支持 GLSL、HLSL/CG 接口的显卡驱动
应用阶段

应用阶段的主要职责是输出渲染所需的几何信息(渲染图元,可以是点、线等)和渲染设置给GPU,并调用DrawCall指令开启渲染:


几何阶段

几何阶段主要负责将三维的模型顶点坐标转换为屏幕空间的二维顶点坐标,输出各顶点的深度值等信息到光栅化阶段:


这里我们要重点关注一下顶点着色器中,把顶点从模型空间转换到裁剪空间的过程,这里使用MVP矩阵完成这个操作,完成后的顶点只是转换到了裁剪空间里,还不是屏幕空间,这一步只是为后面的裁剪步骤做准备。
顶点转换到裁剪坐标系后,接着会由硬件做透视除法(齐次除法),最后得到归一化的设备坐标(Normalized Device Coordinates, NDC),这个坐标是归一化的,如果顶点在视锥体内的话(也就是可以被看见的话)他的xy取值范围应该是 -1~1 ,OpenGL中z的取值范围应该是 -1~1 ,DirectX中z的取值范围是 0~1,如果一个顶点转换到NDC之后某个分量超出了取值范围,说明顶点在视锥体之外,那么裁剪步骤就会把这个顶点给裁剪掉,不参与后续的渲染。
屏幕映射步骤输入的是三维的NDC空间坐标,输出是二维的电脑屏幕的坐标。要注意:OpenGL的屏幕坐标原点左下角:


而DirectX的屏幕坐标原点是左上角:


微软的许多窗口都是用左上角为原点的坐标系统,因为这样和我们的阅读方式:从左到右、从上到下的习惯是匹配的,有关顶点从模型空间转NDC空间的过程我后续会出一篇文章详解,可以关注下~,当然网上也有很多解析文章,搜MVP矩阵的推导就可以看到。
光栅化阶段

光栅化步骤主要负责:每个渲染图元(点、线、面)中的哪些像素应该被绘制在屏幕上,也需要对上一个阶段得到的逐顶点数据进行插值赋值给每个像素,再逐像素处理光照。


透明度测试和提前深度测试

如果你在片元代码里调用了 clip 函数,那么就是开启了透明度测试,不过 clip 函数传递进参数的可以不是颜色的透明度,只要是一个 float 值就行。
提前深度测试可以把被不透明物体遮挡的片元提前舍弃掉,提高渲染的效率,但我们都知道一个片元如果通过了深度测试,默认情况下他是会向深度缓冲区写入自己的深度的,试想一下:如果开启了透明度测试的物体 B 在另一个物体 A 之前,假设物体 B 先渲染,并且假设进行了提前深度测试:


由于物体B第一个渲染,他发现深度缓冲里没有值,所以通过了提前深度测试并写入了自己的深度值,这导致后面的物体 A 在进行深度测试的时候,无法通过测试,抛弃了这部分的片元。如果物体B像一个正常的不透明物体那样渲染这没什么问题,但是问题是现在的物体B会进行透明度测试,如果物体B的一部分没有通过透明度测试,被裁剪,那么将会出现错误的现象:


因此提前深度测试和透明度测试是互相冲突的,当你开启了透明度测试,也就意味着这个片元不能进行提前深度测试。由于提前深度测试对渲染性能有所提升,所以说透明度测试会导致渲染性能下降。
屏幕后处理技术是指当这一帧的画面渲染完毕的时候,在输出到电脑屏幕之前,我们可以对这个画面进行一些特殊的处理,比如泛光效果等,值得注意的是虽然此时处理的图元是一个完整的长方形的二维的图画(可以理解为一个png)但是其中每个像素点都是记录了一些信息的,比如深度等。
关于双缓冲技术这里引用一下乐乐的《UnityShader入门精要》:


到此,我们的游戏物体就从内存里渲染到屏幕上啦!
文中提到的各种配置指令比如 ZTest、Cull、Blend 或者 clip 函数等,如果有点想不起来的话可以看看我写的shader词典:https://zhuanlan.zhihu.com/p/426530377
感谢您看到这(点个喜欢呀~),以下是一些参考资料:
May老师的视频:https://www.bilibili.com/video/BV1L54y1s7xw?p=2
RTR4 第二章 图形渲染管线:https://zhuanlan.zhihu.com/p/208987296
GPU渲染流水线简介:https://zhuanlan.zhihu.com/p/61949898
OpenGL官方中文文档:https://learnopengl-cn.github.io/
如有错漏还请赐教!

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-11-16 00:16 , Processed in 0.093732 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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