unity shader入门精要 前3章总结

第1章

1.1

程序员三大浪漫;学shader要了解整个渲染流程

1.2

基础篇:2、3、4章:渲染流水线、unity shader基础和所需的数学基础
初级篇:5、开启unity shader学习之旅;6、unity中的基础光照;7、基础纹理;8、透明效果;
中级篇:9、更复杂光照;10.高级纹理;11.画面动;
高级篇:12.屏幕后处理效果;13.使用深度和法线纹理;14.非真实感渲染;15.噪声;16.渲染优化;
扩展篇:17.unity表面着色器;18.基于物理的渲染;19.unity5更新;20.more

第2章

2.1 综述

2.1.1 流水线

生产流程分为4步,工序1完成后,交给工序2,工序1又开始加工下一个,实现了并行;流水线系统的决定最后生产速度的是最慢的一道工序所需的时间。

2.1.2渲染流水线

由一个三维场景出发,生成一张二维图像。即从一系列顶点数据、纹理等信息出发,转换成图像,由cpu、gpu共同完成。
应用阶段:输入:场景数据(摄像机、视锥体、模型、光源),剔除,每个模型的渲染状态;输出:渲染所需的几何信息,即渲染图元(点、线、面),传递给几何阶段。
几何阶段:把顶点坐标变换到屏幕空间中,再交给光栅器处理。输入:渲染图元(顶点数据),输出:屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下个阶段。gpu上。包括顶点着色器、曲面细分着色器、几何着色器、裁剪、屏幕映射。
光栅化阶段:使用上阶段的数据来产生屏幕上的像素,并渲染出最终的图像。gpu上。主要任务:决定每个渲染图元的哪些像素应该被绘制在屏幕上。这阶段主要对上个阶段的逐顶点数据插值,再进行逐像素处理。包括三角形设置、三角形遍历、片元着色器、逐片元操作。

2.2 cpu与gpu通信

把数据加载到显存

渲染的数据由硬盘到系统内存。然后,网格和纹理等数据(顶点位置、法线方向、顶点颜色、纹理坐标等)又被加载到显存(Gpu访问显存比较快)。

设置渲染状态

渲染状态:定义了场景中的网格是怎么样被渲染的。使用哪个顶点着色器、片元着色器、光源属性、材质等。
准备好后,cpu就需要调用一个drawcall来告诉gpu去渲染。

调用drawcall

drawcall是一个命令,由cpu给gpu,只会指向一个需要被渲染的图元,因为渲染状态已经设置完了。给的Dc后,gpu就会根据渲染状态和顶点信息进行计算,最终输出漂亮的像素。

2.3 gpu流水线

概述

gpu流水线:输入:顶点数据。顶点着色器:顶点空间变换、顶点着色等功能。曲面细分着色器:可选,用于细分图元。几何着色器:可选,用于执行逐图元的着色操作或被用于产生更多的图元。裁剪:将不在摄像机范围内的顶点裁剪掉,剔除某些三角形的面片,该阶段可配置。屏幕映射:不可配置和编程,将每个图元的坐标转换到屏幕坐标系中。
光栅化概念中的三角形设置、三角形遍历、也都是固定函数阶段;片元着色器是完全可编程的,用于实现逐片元的着色操作。逐片元操作阶段负责执行更多的操作,如修改颜色、深度缓冲、进行混合等,不可编程但具有很高的可配置性。

顶点着色器

输入来自cpu,处理的顶点,每个顶点都会调用vertex shader,每个顶点互相独立。主要完成顶点变换(从模型空间到齐次裁剪空间,再通过硬件做透视除法,最终得到归一化的设备坐标NDC)、逐顶点光照。NDC在openGl里z范围为-1到1,在directX中是0到1。

曲面细分着色器

几何着色器

裁剪

将摄像机所看的范围外的物体剔除,由于我们已知在NDc下的顶点位置,只需要将图元裁剪到单位立方体内。这一步不可编程。

屏幕映射

将每个图元的坐标转换到屏幕坐标系下。OpenGl里(0, 0),在左下角,directX里该点在左上角。

光栅化和三角形设置

光栅化第一个阶段。从上一个阶段输出的信息是屏幕坐标系下的顶点位置以及和他们相关的额外信息,如深度值、法线方向、视角方向等。光栅化重要目标:计算每个图元覆盖了哪些像素,这些像素的颜色。
三角形设置阶段会计算光栅化一个三角网格所需的信息,即要计算每条边上的像素坐标。为了能够计算边界像素的坐标信息,就需要知道三角形边界的表达方式,而计算三角网格表示数据的过程就叫做三角形设置。

三角形遍历

检查每个像素是否被一个三角网格所覆盖,如果被覆盖就会生成一个片元。这样一个检查哪些像素被覆盖的过程就是三角形遍历,也被称为扫描变换。这个阶段会根据上阶段的结果判定一个三角网格覆盖了哪些像素,并使用三角网格3个顶点的顶点信息对整个覆盖区域的像素进行插值。这一步输出是一个片元序列。
片元并不是真正的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色,包括但不限于屏幕坐标、深度信息,以及其他顶点信息,如法线、纹理坐标等。

片元着色器

可编程着色器阶段,前面的光栅化阶段实际上并不会影响屏幕上每个像素的颜色,而是会产生一系列的数据信息,表述一个三角网格是如何覆盖每个像素的。每个片元就是存储着一系列数据的,真正对像素影响的是片元着色器。片元着色器输入是对顶点信息插值的结果,输出是一个或者多个颜色值。
渲染技术中比较重要的技术是纹理采样。片元着色器也是仅仅可以影响单个片元。但片元着色器可以访问到导数信息。

逐片元操作

逐片元操作是openGl的说法,directX上叫做输出合并阶段,这个阶段有几个人物
(1)决定每个片元的可见性,包含许多测试工作,如深度测试,模板测试等;
(2)如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存在于颜色缓冲区中的颜色进行混合,合并。
片元->模板测试->深度测试->混合->颜色缓冲区
模板测试:模板缓冲和颜色缓冲、深度缓冲几乎是一类东西。如果开启了模板缓冲,gpu会读取模板缓冲中该片元位置的模板值,进行对比,比较然后舍弃某些不符合条件的片元。不管一个片元有没有通过模板测试,我们都可以根据模板测试和下面的深度测试结果来修改模板缓冲区,这个修改操作也是由开发者指定的。开发者可以设置不同结果下的修改操作,例如,在失败时模板缓冲区保持不变,通过时将 模板缓冲区中对应位置的值加1等。模板测试通常用于限制渲染的区域。另外,模板测试还有一些更高级的做法,如渲染阴影、轮廓渲染等。
深度测试:可以配置。如果开启了深度测试,gpu会把该片元的深度值和已经存在于缓冲区中的深度值比较,如果是大于等于,则舍弃。和模板不同,一个片元没有通过深度测试,则没有权利更改深度缓冲区的值。
合并:不透明物体,可以关闭混合操作。
上面给出的测试顺序不是唯一的,比如在unity的渲染管线中,深度测试在片元着色器之前,这种叫Early-Z技术。
经过测试后,GPu会使用双重缓冲,前置缓冲、后置缓冲。

2.4 困惑的几个问题

openGl和Directx

如果开发者直接访问gpu,比较麻烦,可能需要和各种寄存器、显存打交道,还好有了图像编程接口,在这些硬件的基础上实现了一层抽象。
opengl和directX就是这些图像应用编程接口,这些接口用于渲染二维或三维图像。这些接口架起了上层应用和底层gpu的桥梁。显卡驱动就好像一个中介者,负责和双方(图像编程接口和gpu)打交道。因为显卡驱动的存在,几乎所有gpu都既可以和directx合作,又可以和opengl合作。一个显卡制作商为了和两者都合作,就必须提供支持两者接口的显卡驱动。
概括来说,我们的应用程序运行在cpu上,应用程序可以通过调用opengl或directX的图形接口将渲染所需的数据,如顶点数据、纹理数据、材质参数等数据存储在显存中区域。随后,开发者可以通过图像编程接口发出渲染命令(DrawCall),它们将会被显卡驱动翻译成gpu能够理解的代码,进行真正的绘制。

hlsl glsl cg

glsl优点是跨平台性,在windows、linux、mac甚至移动平台等多个平台上工作,这是由于opengl没有提供着色器编译器,而是由显卡驱动来完成着色器的编译工作。也就是说,只要显卡驱动支持对glsl的编译它就可以运行,这种做法好处在于由于供应商完全了解自己的硬件构造,知道怎样发挥最大作用。即glsl是依赖于硬件的,而非操作系统级别的。即glsl编译结果取决于硬件供应商。
hlsl是由微软控制着色器的编译,不同的硬件同一个着色器的编译结果是一样的。也因此支持hlsl的平台相对比较有限。
cg是真正意义上的跨平台,根据平台的不同,翻译成相应的中间语言。cg开平台性主要取决于与微软的合作,这也和hlsl比较像。所以可以无缝移植成hlsl代码,但缺点是可能无法完全发挥出opengl的最新特性。

什么是drawcall

drawcall:cpu调用图像编程接口,以命令gpu进行渲染的操作。一个常见的误区,Drawcall中造成性能问题的元凶是gpu,认为gpu上的状态切换是耗时的,其实,真正拖后腿的是cpu。
cpu和gpu是如何实现并行工作的:命令缓冲区,cpu向其中添加命令,gpu从中读取命令,使得两者可以并行工作。命令缓冲区中命令较多,除了drawcall,还有改变渲染状态等(改变着色器、使用不同的纹理等)。
为什么drawcall多了会影响效率:每次调用drawcall前,cpu需要向gpu发送许多,包括数据、状态和命令等。在这一阶段,cpu需要完成很多工作,如检查渲染状态等。而gpu渲染比较快,如果drawcall数量较多,cpu就会把大量时间花费在提交drawcall上,造成cpu的负载过大。
如何减少drawcall:批处理,我们知道,提交大量很小的drawcall会造成cpu性能瓶颈,那么一个优化的想法就是把很多小的drawcall合并成一个大的drawcall,这就是批处理的思想。
需要注意的是,由于我们需要在cpu的内存中合并网格,而合并的过程是需要消耗时间的,因此批处理更适合静止的物体,静态合批。当然也可以对动态的物体进行合批,但由于这些物体是不断运动的,因此每一帧都需要重新进行合并然后再发送给gpu,会影响空间和时间。为了减少drawcall:(1)避免使用大量很小的网格(是否可以合并);(2)避免使用过多的材质,尽量在不同的网格之间共用同一个材质。

什么是固定管线渲染

固定函数的流水线,也简称为固定管线。像开关一样。现在不建议继续用固定管线的渲染方式。

2.5 do you understand shader

shader所在的阶段就是渲染流水线的一部分,是:
Gpu流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码会在gpu上运行;
有一些特定类型的着色器,如顶点着色器、片元着色器等。
依赖着色器我们可以控制流水线的渲染细节,比如顶点变换、逐像素渲染等。

2.6 扩展阅读

《Real-time rendering》
《Batch Batch Batch what does it really mean?》
http://blog.csdn.net/poem_qianmo/article/details/69849858

第3章

unity shader概述

  • 材质和unity shader
  • unity中的材质
    inspector窗口中,单击预览窗口上面的一个小圆圈的图标,可以变换面板中使用的基础模型种类,unity支持球、立方体、圆柱体等;单击两个圆圈,可以变换面板上使用的光照。
  • unity中的shader
    unity提供了4种unity shader模板供我们选择:standard surface shader产生一个包含了标准光照模型(使用了unity 5中新添加的基于物理的渲染方法)的表面着色器模板
    unlit shader:产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器
    Image Effect Shader:为我们实现各种屏幕后处理效果提供一个基本模板。
    Compute Shader:会产生一种特殊的shader文件,这类shader旨在利用gpu的并行性来进行一些与常规渲染流水线无关的计算。
    shader也有inspector,导入设置面板,里面有是否会投射阴影、使用的渲染队列、lod值等。

unity shader结构

+ shader的名字 Shader “Custom/MyShader”文件路径、shader名称
+ 材质和unity shader的桥梁:properties
    properties
    {
        Name("display name", PropertyType) = DefaultValue
    }
    类型:Int Float Range(*, *) Color 默认值(1,1,1,1) Vector默认值4维数组 2D Cube 3D,他们的默认值是一个字符串后面跟一个花括号,字符串要么是空,要么是内置纹理名称,如white black gray或者bump。花括号的用处原本是用于指定一些纹理属性的。材质编辑面板也可以修改。即使我们不在property语义中声明这些属性,也可以直接在cg代码片中定义变量,propery只是为了在属性面板显示。
+ subshader
一个unity shader文件可以包含多个subshader语义块,但至少要有一个。加载shader时,会扫描所有的subshader,选择第一个能够在目标平台上运行的subshader,如果都不支持,就会使用fallback语义指定的unity shader。提供这种语义的原因在于,不同的显卡具有不同的能力。
subshader中定义了一系列pass以及可选的状态和标签设置,每个pass定义了一次完整的渲染流程。但如果pass数目过多,往往会造成渲染性能的下降。
状态设置
shaderlab提供了一系列渲染状态的设置指令,可以设置显卡的各种状态:
Cull  back front off
ZTest ZTEst less great |LEqual | GEqual | NotEqual | Always
ZWrite Off On
Blend SrcFactor DstFactor
Pass里可以单独进行这些设置
SubShader
{
    【Tags】
     【RenderSetup】
     pass
     {
     }
}

Tags键值对
Queue
RenderType
DisableBatching
IgnoreProjector
Pass的Tags
LightMode UsePass GrabPass

unity shader的形式

表面着色器

unity自己创造的一种着色器代码类型,unity背后做了许多事情,但渲染代价比较大。可以理解成,表面着色器是unity对顶点和片元着色器的更高一层的抽象。unity为我们处理了很多光照细节。
#pragma surface surf Lambert

顶点片元着色器

 #pragma vertex vert
 #pragma fragment frag

固定函数着色器

Pass
{
    Material
    {   _Diffuse [_Color]  }
    Lighting On
}

选择哪种shader形式

+ 如果想和各种光源打交道,表面着色器
+ 光照数目少,顶点 片元着色器
+ 自定义的,最好用顶点 片元着色器

答疑解惑

unity shader不是真正的shader

  • 传统shader中,只可以写特定类型的shader,比如顶点着色器、片元着色器等,而在unity shader中,可以在同一个文件中同时包含需要的顶点和片元着色器代码。
  • 传统shader中,无法设置一些渲染设置,例如是否开启混合、深度测试等,都需要开发人员在另外的代码中自行设置。而在unity shader中,通过一行指令就可以设置。
  • 传统shader中,需要编写冗长代码设置输入输出,小心处理这些输入输出的位置对应关系等。而在unity shader中,我们只需要在特定语句中声明一些属性,就可以依靠材质修改这些属性。而对于模型自带的数据,如顶点位置、纹理坐标、发现等,unity shader也提供直接访问的方法。
  • 除了这些优点,也有缺点,由于unity shader的高度封装性,我们可以编写的shaderleix和语法都被限制了。如曲面细分着色器、几何着色器等,unity shader的支持就比较少。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值