JoshWindsor 发表于 2022-12-2 17:14

Unity Shader(零)-顶点着色器和片元着色器

前言

Unity Shader我认为在游戏开发的流程中是比较难学的一部分,实在是太多劝退的理由,比如图形学晦涩难懂,VS对于Shader没有自动补充,报错没有没有明显提示,遇到问题很难找到人询问。
即便是了解多年,我也始终不求甚解徘徊于入门水平。终于在又一次沟通需求的时候没有听懂TA说得话,所以打算重学Unity Shader。
Unity渲染管线

Shader主要是对顶点着色器和片元着色器进行编写,那么我们首先要知道这两位好兄弟在渲染管线中起到什么样的作用,那么首先要是知道Unity渲染管线是个什么。



简要来说三个大模块:CPU应用程序阶段、GPU渲染管线、帧缓冲区。
CPU应用程序阶段

CPU应用程序阶段大致作四件事情:剔除、排序、打包数据、绘制调用。

[*]剔除

[*]视锥体剔除:根据FOV、FarClip、NearClip构成的视锥体Bounding。
[*]层级剔除:Camera上的LayerMask组件,指定层级可以被相机剔除。





[*]排序

[*]RenderQueue:渲染队列,半透明的RenderQueue,一般比不透明的RenderQueue大,不透明相同的RenderQueue则按照摄像机距离从前到后进行排序。半透明相同的RenderQueue则按照摄像机距离从后到前进行排序。




[*]打包数据

[*]模型信息:顶点信息、法线信息、UV、切线、顶点色。
[*]灯光信息:灯光数据、材质信息。
[*]变换矩阵:模型空间矩阵、Fov参数。



[*]绘制调用:数据准备完毕,CPU要告诉好基友GPU,你要开始干活了,也就是DrawCall的来源。


GPU应用程序阶段

终于到了顶点着色器和片元着色器闪亮登场的时刻了,不过不要急,先看一下GPU应用程序阶段发生了什么吧。



GPU应用程序阶段就是把CPU打包发送给GPU的顶点数据通过可编程实现的顶点着色器和片元着色器,最后输出为硬件可识别的2D像素。
大体也是可以分为四个阶段:顶点着色器、光栅化、片元着色器和输出合并。


[*]顶点着色器:顶点坐标从模型空间转化为裁剪空间,展开来说就是MVP矩阵变换,顶点着色器进行的业务处理有:

[*]矩阵变换的计算
[*]计算光照公式生成逐顶点颜色
[*]生成/变换纹理坐标





[*]光栅化:对应的图元,将图元转化成一组二维片段。而这些转化的片段将由着色器处理,这些二维片段就是屏幕上可绘制的像素。
[*]片元着色器:程序是用来描述片段上执行操作(如颜色混合)的片元着色器程序源代码,它可以于图/视频/图形中每个像素的颜色填充。在片元着色器的业务处理有:

[*]计算颜色
[*]获取纹素
[*]往像素点中填充颜色值

一般在游戏里面我们能够或许颜色的方式主要有纹理(包括纹理采样、纹理过滤、MipMap、纹理寻址、纹理压缩)和光照(常说的直接光照和间接光照)两种。



Unity内的LightProbe

光照的计算又遵从光照模型。





常见的三种光照模型


[*]输出合并:经过模板测试和深度测试后,混合在一起,最后把数据输出到帧缓冲区。
Shader入门

Unity创建Shader种类


[*]Standard Surface Shader
标准表面着色器,是一种基于物理的着色系统(使用了Physically Based Rendering(简称PBR)技术,即基于物理的渲染技术),以模拟现实真实的方式来模拟材质与灯光之间的关系,可以很轻易的表现出各种金属反光效果,同时此种Shader的书写逻辑也更符合人类的思维模式。

[*]Unlit Shader
Vertex/Fragment Shader,也就是最基本的顶点片断着色器,不受光照影响的Shader,多用于特效、UI上的效果制作。

[*]Image Effect Shader
也是顶点片断着色器,只不过是针对后处理而定制的模版。

[*]Compute Shader
Compute Shader是运行在图形显卡上的一段程序,独立于常规渲染管线之外的,它可以直接将GPU作为并行处理器加以利用,从而使GPU不仅具有3D渲染能力,还具有其他的运算能力。

[*]Shader Variant Collection
Shader变体收集器,是对Shader变体进行打包用的容器。
Shader属性声明

在Properties{}中定义着色器属性,在这里定义的属性将被作为输入提供给所有的子着色器。每一条属性的定义的语法是这样的:
_Name("Display Name", type) = defaultValue[{options}]

[*]_Name - 属性的名字,简单说就是变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容
[*]Display Name - 这个字符串将显示在Unity的材质编辑器中作为Shader的使用者可读的内容
[*]type - 这个属性的类型,可能的type所表示的内容有以下几种:

[*]Color - 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
[*]2D - 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
[*]Rect - 一个非2阶数大小的贴图;
[*]Cube - 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
[*]Range(min, max) - 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等);
[*]Float - 任意一个浮点数;
[*]Vector - 一个四维数;

[*]defaultValue 定义了这个属性的默认值,通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要某些特定的参数值来达到需要的效果,虽然这些值可以在之后在进行调整,但是如果默认就指定为想要的值的话就省去了一个个调整的时间,方便很多)。

[*]Color - 以0~1定义的rgba颜色,比如(1,1,1,1);
[*]2D/Rect/Cube - 对于贴图来说,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个
[*]Float,Range - 某个指定的浮点数
[*]Vector - 一个4维数,写为 (x,y,z,w)

所以,写一个最简单的示例:
Properties{
      _MainTex("Main Texture", 2D) = "white"{}
      _DisplacementTex("Displacement Texture", 2D) = "white"{}
      
      _Magnitude("Magnitude", Range(0, 1)) = 0
      _Tween("Tween", Range(0, 1)) = 0
      _Color("Color", Color) = (1,1,1,1)
      //Properties
      // {
      //_Color("我是Color", Color) = (1,1,1,1)
      // }
      // 变量名:_Color
      // 显示名称:我是Color
      // 类型:Color
      // 默认值:(1,1,1,1)
    }
页: [1]
查看完整版本: Unity Shader(零)-顶点着色器和片元着色器