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

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

[复制链接]
发表于 2022-12-2 17:14 | 显示全部楼层 |阅读模式
前言

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)
    }

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-6-2 05:10 , Processed in 0.134740 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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