UnityShader模板

本文转自:【Unity3D Shader编程】之八 Unity5新版Shader模板源码解析&径向模糊屏幕特效的实现_浅墨_毛星云的博客-CSDN博客

UnityShader的三种形式 - 知乎

shader基本语法、变量类型、shader的三种形式、subshader、fallbac。。。 - 百度文库

Shader|三种主要UnityShader形式_该用户还未想到好的昵称的博客-CSDN博客_unity有几种shader

一、概述

        在Unity5~Unity2018 project窗口中右键>Creat>Shader,可以发现Unity提供了四种UnityShader模板:Standard Suface Shader、Unlit Shader、Image Effect Shader、Compute Shader。

        这些UnityShader是Unity为了控制渲染层提供的一层抽象,减轻了开发者的学习门槛,且统一用ShaderLabUnity提供的一种编写UnityShader的一种说明性语言)来编写的。Unity会根据不同的平台把UnityShader编译成真正的代码和Shader文件。

上述四种UntiyShader中,Compute Shader会编译为一种特殊的shader,可以利用GPU的并行性来进行一些与常规渲染流水线无关的计算,书中没有做过多的介绍。去官方文档查了下,看的不是很明白,暂且放一放,先搞其他三种。

Unity官方说明书 - Manual: Writing Shadershttps://docs.unity3d.com/2018.4/Documentation/Manual/ShadersOverview.html

UnityShader可以用三种不同的方式编写:表面着色器(Surface Shaders)顶点和片元着色器(Vertex and Fragment Shaders)固定函数着色器(Fixed Function Shaders)。剩下的三种UnityShader模板(Standard Suface Shader、Unlit Shader、Image Effect Shader),我们就按照编写方式来分类了解。

  • 表面着色器(Surface Shaders)
    表面着色器是Unity自己创造的一种着色器代码类型,其实此种方式在最后还是会转换为顶点/片元Shader,只是Unity在背后处理了许多的光照细节,因此渲染的代价较大,我们右键Creat出的Standard Suface Shader模板就是按照表面着色器的方式编写。
    官方文档说明如下:如果着色器需要受到光照和阴影的影响,则表面着色器是您的最佳选择。表面着色器以简洁的方式使编写复杂的着色器变得容易 - 这是与 Unity 光照管线进行交互的更高级别抽象。大多数表面着色器自动支持前向和延迟光照。您可以用几行 Cg/HLSL 编写表面着色器,随后就能在此基础上自动生成更多代码。如果您的着色器与光照没有任何关联,请不要使用表面着色器。对于后期处理效果或许多特殊效果着色器,表面着色器是次优选项,因为它们会无缘无故进行大量光照计算
  • 顶点和片元着色器(Vertex and Fragment Shaders)
    顶点和片元着色器是UnityShader中最灵活的一种编写方法,同时也是最复杂的。其实令外两种编写方式Unity都会在悄咪咪的编译为此种方式。我们右键Creat出的Unlit Shader、Image Effect Shader两种模板实际上都是基于这种方式编写的,只不过Image Effect Shader是为我们提供了一个实现各种屏幕后处理效果的基本模板,而Unlit Shader提供的是一个不包含光照的顶点和片元着色器的基本模板。
    官方文档说明如下:如果您的着色器不需要与光照交互,或者如果您需要无法由表面着色器处理的非常奇特的效果,则需要使用顶点和片元着色器。以这种方式编写的着色器程序是创建所需效果的最灵活方法(甚至表面着色器也会自动转换为一堆顶点和片元着色器),但这需要付出代价:必须编写更多的代码并且更难以让它与光照互动。这些着色器也是以Cg/HLSL编写。
  • 固定函数着色器(Fixed Function Shaders)
    固定管线的编程方式已经已经逐渐被抛弃了,主要为了支持一些不支持可编程管线的老旧设备,且在Unity5.2之后,所有的固定函数着色器都会被Unity'编译为对应的顶点/片元着色器,因此此处不再赘述,由需要的童鞋可以看上边的官方文档。

二、Shader模板源码解析

2.1 标准表面着色器(Standard Surface Shader)模板源码解析

        在Unity中,我们若要实现新的表面着色器时,可以根据这个模板,进行一步添加子着色器和新的参数与特性。

        这个Shader模板的脉络很清晰,先是定义一些属性,然后在SubShader中设置渲染模式,层次细节LOD的值,然后开启一个CG编程语言模块,写一些编译指令#pragma,声明一下变量让属性值在CG块中可见,定义输入结构,然后填充一下表面着色函数即可。注意:专门强调一句,SurfaceShader不能使用Pass,一使用就报错,我们直接在SubShader中实现和填充代码就可以了。

        Standard Surface Shader模板详细注释的Shader代码如下:

Shader "浅墨Shader编程/Volume8/Surface Shader模板"
{
       //------------------------------------【属性值】------------------------------------
       Properties
       {
              //主颜色
              _Color("Color", Color) = (1,1,1,1)
              //主纹理
              _MainTex("Albedo (RGB)", 2D) = "white" {}
              //光泽度
              _Glossiness("Smoothness", Range(0,1)) = 0.5
              //金属度
              _Metallic("Metallic", Range(0,1)) = 0.0
       }
 
       //------------------------------------【唯一的子着色器】------------------------------------
       SubShader
       {
              //【注意:Surface Shader不能使用Pass,直接在SubShader中实现即可】
 
              //渲染类型设置:不透明
              Tags{"RenderType" = "Opaque" }
 
              //细节层次设为:200
              LOD200
 
              //===========开启CG着色器语言编写模块===========
              CGPROGRAM
 
              //编译指令:告知编译器表明着色函数的名称为surf
              //Standard表示光照模型为Unity标准版光照模型
              //fullforwardshadows表示在正向渲染路径中支持所有阴影类型
              #pragma surface surf Standard fullforwardshadows
 
              //编译指令: 指定着色器编译目标为Shader Model 3.0
              #pragma target 3.0
 
              //变量的声明
              sampler2D _MainTex;
 
              //表面输入结构体
              struct Input
              {
                     float2 uv_MainTex;//纹理坐标
              };
 
              //变量的声明
              half _Glossiness;
              half _Metallic;
              fixed4 _Color;
 
              //--------------------------------【表面着色函数】-----------------------------
              //输入:表面输入结构体
              //输出:Unity内置的SurfaceOutputStandard结构体
              //SurfaceOutputStandard原型如下:
              /*
                     struct SurfaceOutputStandard
                     {
                            fixed3 Albedo;                  // 漫反射颜色
                            fixed3 Normal;                  // 切线空间法线
                            half3 Emission;                 //自发光
                            half Metallic;                           // 金属度;取0为非金属, 取1为金属
                            half Smoothness;             // 光泽度;取0为非常粗糙, 取1为非常光滑
                            half Occlusion;                 // 遮挡(默认值为1)
                            fixed Alpha;                      // 透明度
                     };
              */
              //---------------------------------------------------------------------------------
              void surf(Input IN, inout SurfaceOutputStandard o)
              {
                     //【1】漫反射颜色为主纹理对应的纹理坐标,并乘以主颜色
                     fixed4c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
                     //【2】将准备好的颜色的rgb分量作为漫反射颜色
                     o.Albedo= c.rgb;
                     //【3】金属度取自属性值
                     o.Metallic= _Metallic;
                     //【4】光泽度也取自属性值
                     o.Smoothness= _Glossiness;
                     //【5】将准备好的颜色的alpha分量作为Alpha分量值
                     o.Alpha= c.a;
              }
 
              //===========结束CG着色器语言编写模块===========
              ENDCG
       }
       //备胎为漫反射
       FallBack"Diffuse"
}

2.2 无灯光着色器(Unlit Shader)模板源码解析

        Unlit Shader,简单来说,就是直接采用漫反射纹理,不考虑场景中的任何灯光效果。使用无灯光着色器的话,也就不能使用任何镜面或者法线效果了。Unlit系的Shader基本原理和其他Shader无异,但是计算量更小,更快速,更高效。

        而在Unity内置的各种着色器中,有如下的四种是Unlit系的:

        好的,已经稍微解释了下什么是Unlit Shader。下面一起看一下Unity为我们提供的无灯光着色器模板的代码: 

Shader "浅墨Shader编程/Volume8/无灯光着色器(Unlit Shader)模板"
{
       //------------------------------------【属性值】------------------------------------
       Properties
       {
              //主纹理
              _MainTex("Texture", 2D) = "white" {}
       }
 
       //------------------------------------【唯一的子着色器】------------------------------------
       SubShader
       {
              //渲染类型设置:不透明
              Tags{ "RenderType"="Opaque" }
 
              //细节层次设为:100
              LOD 100
 
              //--------------------------------唯一的通道-------------------------------
              Pass
              {
                     //===========开启CG着色器语言编写模块===========
                     CGPROGRAM
 
                     //编译指令:告知编译器顶点和片段着色函数的名称
                     #pragma vertex vert
                     #pragma fragment frag
 
                     //着色器变体快捷编译指令:雾效。编译出几个不同的Shader变体来处理不同类型的雾效(关闭/线性/指数/二阶指数)
                     #pragma multi_compile_fog
 
                     //包含头文件
                     #include"UnityCG.cginc"
 
                     //顶点着色器输入结构
                     struct appdata
                     {
                            float4 vertex : POSITION;//顶点位置
                            float2 uv : TEXCOORD0;//纹理坐标
                     };
 
                     //顶点着色器输出结构
                     struct v2f
                     {
                            float2 uv : TEXCOORD0;//纹理坐标
                            UNITY_FOG_COORDS(1)//雾数据
                            float4 vertex : SV_POSITION;//像素位置
                     };
 
                     //变量声明
                     sampler2D _MainTex;
                     float4 _MainTex_ST;
                    
                     //--------------------------------【顶点着色函数】-----------------------------
                     //输入:顶点输入结构体
                     //输出:顶点输出结构体
                     //---------------------------------------------------------------------------------
                     v2f vert (appdata v)
                     {
                            //【1】实例化一个输入结构体
                            v2f o;
                            //【2】填充此输出结构
                            //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口
                            o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);
                            //【3】用UnityCG.cginc头文件中内置定义的宏,根据uv坐标来计算真正的纹理上对应的位置(按比例进行二维变换)                 
                            o.uv= TRANSFORM_TEX(v.uv, _MainTex);
                            //【4】用UnityCG.cginc头文件中内置定义的宏处理雾效,从顶点着色器中输出雾效数据
                            UNITY_TRANSFER_FOG(o,o.vertex);
 
                            //【5】返回此输出结构对象
                            return o;
                     }
                    
                     //--------------------------------【片段着色函数】-----------------------------
                     //输入:顶点输出结构体
                     //输出:float4型的像素颜色值
                     //---------------------------------------------------------------------------------
                     fixed4 frag (v2f i) : SV_Target
                     {
                            //【1】采样主纹理在对应坐标下的颜色值
                            fixed4 col = tex2D(_MainTex, i.uv);
 
                            //【2】用UnityCG.cginc头文件中内置定义的宏启用雾效
                            UNITY_APPLY_FOG(i.fogCoord,col);           
 
                            //【3】返回最终的颜色值
                            return col;
                     }
 
                     //===========结束CG着色器语言编写模块===========
                     ENDCG
              }
       }
}

        不难分析得到,无灯光着色器是一种顶点&片段着色器,这边模板给出的是单子着色器,单通道的写法。

        并且,无灯光着色器中使用了一些UnityCG.cginc头文件中内置的宏,比如说TRANSFORM_TEX、UNITY_TRANSFER_FOG、UNITY_APPLY_FOG。接下来分别把这三个宏简单解释一下。

2.2.1 TRANSFORM_TEX宏

        TRANSFORM_TEX宏的定义为:

#define TRANSFORM_TEX(tex,name) (tex.xy *name##_ST.xy + name##_ST.zw)

        其位于UnityCG.cginc(Unity5.2.1版本)的第266行。其可以根据uv坐标来计算真正的纹理上对应的位置(按比例进行二维变换),组合上上文中定义的float4 _MainTex_ST,便可以计算真正的纹理上对应的位置。

2.2.2 UNITY_TRANSFER_FOG宏        

        UNITY_TRANSFER_FOG宏的作用是从顶点着色输出雾数据。在UnityCG.cginc(Unity5.2.1版本)的第772行起,具体定义如下:

#if (SHADER_TARGET < 30) ||defined(SHADER_API_MOBILE)
              //手机端或者Shader Mode 2.0: 计算每个顶点的雾效因子
              #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord =unityFogFactor
       #else
              //Shader Mode 3.0和PC和游戏机: 计算每像素的雾距离,和每像素的雾效因子
              #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord = (outpos).z
       #endif

2.2.3 UNITY_APPLY_FOG宏

        UNITY_APPLY_FOG宏的定义稍微有些长,从UnityCG.cginc(Unity 5.2.1版本)的第787行起:

#if defined(FOG_LINEAR) || defined(FOG_EXP)|| defined(FOG_EXP2)
       #if(SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
              //mobile or SM2.0: fog factor was already calculated per-vertex, so just lerp thecolor
              #defineUNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_FOG_LERP_COLOR(col,fogCol,coord)
       #else
              //SM3.0 and PC/console: calculate fog factor and lerp fog color
              #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_CALC_FOG_FACTOR(coord);UNITY_FOG_LERP_COLOR(col,fogCol,unityFogFactor)
       #endif
#else
       #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol)
#endif
 
#ifdef UNITY_PASS_FORWARDADD
       #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0))
#else
       #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor)
#endif

        可以发现,UNITY_APPLY_FOG宏的作用是从顶点着色器中输出雾效数据,将第二个参数中的颜色值作为雾效的颜色值,且在正向附加渲染通道(forward-additive pass)中,自动设置纯黑色(fixed4(0,0,0,0))的雾效。其在定义中借助了UNITY_APPLY_FOG_COLOR宏,而我们也可以使用UNITY_APPLY_FOG_COLOR来指定特定颜色的雾效。

2.3 图像特效着色器(Image Effect Shader) 模板源码解析

        这里的图像特效一般指的就是屏幕图像特效,在Camera加上各种滤镜,比如说屏幕溅血,像素化,色调的调整,画面模糊等效果。其也是一个顶点&片段着色器,且一般主要的操作集中在片段着色函数中。Unity为我们提供的模板,经过详细注释后的源码如下:

Shader "浅墨Shader编程/Volume8/图像特效Shader模板"
{
       //------------------------------------【属性值】------------------------------------
       Properties
       {
              //主纹理
              _MainTex("Texture", 2D) = "white" {}
       }
 
       //------------------------------------【唯一的子着色器】------------------------------------
       SubShader
       {
              //关闭剔除操作
              Cull Off
              //关闭深度写入模式
              ZWrite Off
              //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTestOff)
              ZTest Always
 
              //--------------------------------唯一的通道-------------------------------
              Pass
              {
                     //===========开启CG着色器语言编写模块===========
                     CGPROGRAM
 
                     //编译指令:告知编译器顶点和片段着色函数的名称
                     #pragma vertex vert
                     #pragma fragment frag
                    
                     //包含头文件
                     #include"UnityCG.cginc"
 
                     //顶点着色器输入结构
                     struct appdata
                     {
                            float4 vertex : POSITION;//顶点位置
                            float2 uv : TEXCOORD0;//一级纹理坐标
                     };
 
                     //顶点着色器输出结构(v2f,vertex to fragment)
                     struct v2f
                     {
                            float2 uv : TEXCOORD0;//一级纹理坐标
                            float4 vertex : SV_POSITION;//像素位置
                     };
 
                     //--------------------------------【顶点着色函数】-----------------------------
                     //输入:顶点输入结构体
                     //输出:顶点输出结构体
                     //---------------------------------------------------------------------------------
                     //顶点着色函数
                     v2f vert (appdata v)
                     {
                            //【1】实例化一个输入结构体
                            v2f o;
 
                            //【2】填充此输出结构
                            //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口
                            o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);
                            //输入的UV纹理坐标为顶点输出的坐标
                            o.uv= v.uv;
 
                            //【3】返回此输出结构对象
                            return o;
                     }
 
                     //变量的声明
                     sampler2D _MainTex;
 
                     //--------------------------------【片段着色函数】-----------------------------
                     //输入:顶点输出结构体
                     //输出:float4型的像素颜色值
                     //---------------------------------------------------------------------------------
                     fixed4 frag (v2f i) : SV_Target
                     {
                            //【1】采样主纹理在对应坐标下的颜色值
                            fixed4 col = tex2D(_MainTex, i.uv);
                            //【2】将颜色值反向
                            col= 1 - col;
 
                            //【3】返回最终的颜色值
                            return col;
                     }
 
                     //===========结束CG着色器语言编写模块===========
                     ENDCG
              }
       }
}

2.4 Shader模板中文注释格式调整版替换

        其实可以将Unity5中自带的上述三个着色器模板,替换成上文中贴出来的、经过详细注释和格式调整的Shader模板,这样在每次新建Shader时,就已经得到了具有很高可读性的Shader模板了,非常便捷。

        一定要吐槽的是,Unity5.2.1自带的三个Shader模板的缩进和空格完全是混用的,导致在通过他们新建出来的Shader里面写代码的时候,格式非常混乱,十分影响新版Unity中Shader的编码体验。很明显,准备此Shader模板的Unity开发人员的编码习惯有点欠缺,得在这里点名批评,轻喷一下。

        浅墨在一发现他们格式有问题的时候就马上替换掉了,所以现在在Unity中写Shader代码的体验是非常棒的。这边也教大家如何替换掉自带的3个模板。

        Unity中Shader模板的位置是…Unity\Editor\Data\Resources\ScriptTemplates,比如说Unity安装在D:\ProgramFiles\路径下,整体路径就是:

D:\ProgramFiles\Unity\Editor\Data\Resources\ScriptTemplates。

        在此路径下的3个txt,即为对应的三个Shader模板文件:

        83-Shader__Standard SurfaceShader-NewSurfaceShader.shader.txt
        84-Shader__UnlitShader-NewUnlitShader.shader.txt
        85-Shader__Image EffectShader-NewImageEffectShader.shader.txt

        这边已经将调整好格式,详细注释的三种模板准备好了,下载之后,找到上面提到的…Unity\Editor\Data\Resources\ScriptTemplates目录。替换掉对应的3个txt文件即可。需要注意的是,如果你想自己DIY Shader模板,需要将txt保存为UTF-8编码格式,否则可能会出现乱码。

        替换的模板下载地址在这里:
Unity5 Shader模板中文注释替换.rar_免费高速下载|百度网盘-分享无限制百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com/s/1sjOORwh        另外还有一个小细节可以提一下。如果你安装了两个或者两个以上的Unity5.1之后版本的Unity,如果你替换你当前使用的Unity路径下的模板文件后,新建的模板文件没有改变的话,你试着将所有的Unity5.1之后版本的路径下的这三个模板文件都进行替换,应该就可以实现想要的替换效果。浅墨的机器上就是同时存在Unity5.2.1和Unity5.2.0,然后使用Unity5.2.1,替换掉Unity5.2.1路径下的三个模板文件后,并没有发生变换。之后我按图索骥,替换了Unity 5.2.0版路径下的三个模板文化,才使得替换的模板文件生效。这估计是Unity多版本共存时,自身的一个小bug。
————————————————
版权声明:本文为CSDN博主「浅墨_毛星云」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/poem_qianmo/article/details/49405909

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值