Shader笔记——3.Shader编写基础

UnityShader的基本结构

1.Shader语义块

所有Shader的定义都是以关键字Shader开头,后面跟着一个字符串指定这个Shader在监视面板中的层级和名字,之后,这个Shader的所有定义都必须在Shader关键字后花括号{}的内部

Shader "Custom/YourShaderName"{
    //此Shader的所有定义...
}

1.1 Properties语义块

在Properties语义块中列举了按行分离的各个属性。每个属性都以特定_开头作为该变量在Shader中的内部名称,列如:_Color,_MainTex,其后跟着括号,在括号中定义监视面板上所要显示的名称以及该属性的类型,最后在等号后给出该属性的默认值,例如:

ShaderTutPropertyDetail

Shader "Custom/YourShaderName"{
    Properties{
        //定义的属性暴露给Unity的Material监视面板
        //在Properties语义块中定义的Shader参数会被序列化为Material数据
        _WaveScale ("Wave scale", Range(0.02, 0.15)) = 0.07 // sliders
        _ReflDistort ("Reflection distort", Range (0, 1.5)) = 0.5
        _RefrDistort ("Refraction distort", Range (0, 1.5)) = 0.4
        _RefrColor ("Refraction color", Color) = (.34, .85, .92, 1) // color
        _ReflectionTex ("Environment Reflection", 2D) = "" {} // textures
        _RefractionTex ("Environment Refraction", 2D) = "" {}
        _Fresnel ("Fresnel (A) ", 2D) = "" {}
        _BumpMap ("Bumpmap (RGB) ", 2D) = "" {}
    }

    //code elide
}

1.2 SubShader语义块

Shader "Custom/YourShaderName"{
    Properties{
        _Color("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
    }
    SubShader {
        //子着色器SubShader,其中定义了一系列的标签设置[Tags]、渲染状态设置[RenderSetup]、Pass通道

        Pass{
            //Pass通道中也可以定义[Tags]、[RenderSetup]
            //区别是在SubShader进行的设置会作用于所有的Pass
            // ...
        }
    }

    SubShader {
        // code elide
    }
}

1.2.1 SubShader的标签设置

SubShader的标签[Tags]本质上是一个键值对(Key/Value Pair),它的键和值都是字符串类型,即每一个标签都有一个key对应一个value,key与key之间空格隔开。
例如:

    Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}

1.1 Queue渲染队列标签

在SubShader中使用“Queue”标签来控制渲染顺序,指定此时渲染的物体属于哪一个渲染队列,Unity在内部使用一系列的整数索引来表示每个渲染队列,索引数越小就越早渲染

渲染队列包括的值有:

  • Background:1000。这种类型的顺序会排在所有物体的前面。例如天空盒。
  • Geometry:2000。所有不透明物体及大多数物体使用这个类型。
  • AlphaTest:2450。使用Alpha Test的物体使用这个类型的渲染顺序。它被排在不透明物体的渲染后面。
  • Transparent:3000。排在Geometry和AlphaTest后渲染。所有alpha混合的物体都应该选择这个类型。
  • Overlay:4000。这一类是最后渲染的,比前面所有类型都要置后,例如镜头光晕。
  • 用户自定义值:比如”Queue”=”Geometry+10”。

例需要使用透明度测试实现透明效果:

    SubShader{
        Tags{"Queue" = "AlphaTest"
            "IgnoreProjector" = "True"
            "RenderType" = "TransparentCutout"
        }
        Pass{
            Tags{"LightMode" = "ForwardBase"}
            //code elide
        }
    }

例通过透明度混合实现透明效果:

    SubShader{
        Tags{"Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        Pass{
            Tags{"LightMode" = "ForwardBase"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            //code elide
        }
    }

1.2 RenderType渲染类型标签

RenderType标签可以让Unity把这个Shader归入到提前定义的组,以指明该Shader是用于哪个渲染队列

RenderType标签通常被用于着色器识别与替换功能,使Unity可以在运行时给特定Camera替换特定的RenderType下的所有Shader。与Camera.RenderWithShader或者Camera.SetReplacementShader函数配合使用

  • Opaque:常用于大部分不透明的物体
  • Transparent:常用于大部分透明的物体、包括粒子特效
  • Background:常用于天空盒
  • Overlay:GUI、常用于镜头光晕
  • 用户自定义RenderType字符串

1.3 DisableBatching标签
设置该物体上的Draw Call Batching,表明是否使用批处理。

其包括的值有:

  • True,设置当前物体Draw Call Batching关闭
  • False,默认值为False ,设置当前物体Draw Call Batching打开
  • LODFading,只有LOD Fading设置被开启时关闭该物体上的Draw Call Batching。

1.4 ForceNoShadowCasting标签
设置该Subshader的物体是否会投射阴影,默认为False的,即默认状态下物体会去绘制阴影部分的渲染。

1.5 IgnoreProjector标签
设置该SubShader的物体是否受Projector的影响,默认状态下是False,即在默认状态下物体能够响应Projector投射的阴影绘制。

1.6 CanUseSpriteAtlas标签
针对图集里的元素的,UGUI中经常使用。当CanUseSpriteAtlas为False时,将不会对打进图集里的贴图进行绘制。默认为True。

1.7 PreviewType标签
设置编辑器Material材质球展示材质预览效果时的形状,默认球形,通过这个标签,可以设置为Plane面板形状或者Skybox天空盒形状。

以上标签可以在SubShader子着色器下定义也可以在Pass下定义

1.2.2 SubShader的渲染状态设置

ShaderLab提供了一系列渲染状态的设置,这些指令可以设置显卡的各种状态,例如是否开启混合/深度测试等。

Cull    Cull Back | Front | Off

设置剔除模式,剔除背面 | 正面 | 关闭剔除 默认CullBack

ZTest    ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always
设置深度测试,默认ZTest LEqual即小于或等于目标深度缓冲中国的深度值深度才能被渲染

ZWrite    ZWrite On | Off

设置深度写入,默认ZWrite On

Blend    Blend SrcFactor DstFactor | Off

Blend Off: 关闭混合

Blend SrcFactor DstFactor: 开启混合,并设置混合因子,源颜色(片元产生的颜色)乘以SrcFactor,目标颜色(颜色缓冲中的颜色)乘以DstFactor,两者相加后在存入颜色缓冲中

例如:
前向渲染路径Additional Pass中的Blend One One

前向渲染路径Base Pass使用透明度混合渲染透明物体时的Blend SrcAlpha OneMinusSrcAlpha

Offset   Factor Units
设置深度缓存的偏移量,Factor偏移缩放大小和Units偏移量。Factor相当于缓存数据偏移放大的倍数,Units相当于直接偏移的量。

SubShader的渲染状态也可以在Pass中定义,例如关闭深度写入ZWrite Off可以也可以写在SubShader中的单个Pass中,写在SubShader中意味着该SubShader中的所有Pass都会关闭深度写入

1.2.3 Pass语义块

Pass通道是最为重要的部分,一个SubShader可以添加多个pass,每个pass都是一个DrawCall,在Pass语义块中也可以定义[Tags]、[RenderSetup],其中的CG代码块可以定义固定功能着色器(FixedFunction Shader)、顶点着色器&片元着色器(Vertex Shader & Fragment Shader)、表面着色器(SurfaceShader)。

1.2.31 Pass通道的标签设置

“LightMode”标签

实际开发中的游戏场景往往不止一个光源,而渲染路径Render Path就是决定了光照是如何被应用到Shader中的,所以在与多个光源打交道时需要为每个Pass指定它使用的渲染路径,只有为Pass设定了正确的渲染路径,Shader的光照计算才会被正确执行,如果没有在Pass中设置正确的渲染路径,则会被当做默认的顶点照明渲染路径,造成一些光照变量不能被正确赋值且计算出错误的光照效果。

Unity支持多种渲染路径,主要包括,前向渲染路径Forward Rendering Path延迟渲染路径Deferred Rendering Path顶点照明渲染路径Vertex Lit Rendering Path

前向渲染路径
  • 原理:一个Base Pass(也可以有多个Base Pass,例如双面渲染效果)一个Additional Pass,一个Base Pass仅执行一次(有多个Base Pass则执行多个Base Pass)渲染对象图元,通过深度缓冲Depth Buffer / Z-Buffer判断对象图元的每个片元是否可见,如果可见就进行光照计算并更新颜色缓冲区的颜色值;一个Additional Pass会根据影响该物体的其他逐像素光源的数目进行多次调用,即每有一个逐像素光源就执行一此Additional Pass渲染,并且该Pass开启混合模式Blend One One使每个Additional Pass结算结果可以与上一次光照结果进行叠加。

  • 优缺点:每个对象对于影响它的每个光源都以通道Pass形式呈现,所以每个对象可能会执行多个Pass,执行次数次数取决于该对象所在范围内有多少个光源,这种方法的优点是非常快,硬件要求低于延迟渲染,并且提供了可定制的阴影模型,可以快速处理透明度。缺点是需要为每个光源付出渲染成本,即影响对象的光源越多,渲染性能越慢。如果可以在游戏中管理灯光的数量,则向前渲染是一个非常快速的解决方案。

  • Unity中的前向渲染路径:unity的前向渲染路径中有三种处理光源的方式:逐像素处理、逐顶点处理、球谐函数SH处理,具体规则:场景中影响物体的最亮的几个光源使用逐像素光照模式。其次,最多有4个点光源会以逐顶点渲染的方式被计算。剩余其他光源将以球谐函数SH方式进行计算,球谐函数SH计算很快但只能得到近似值。可以在光源Light组件下的Render Mode选项进行更改设置为Important或者Not important。

延迟渲染路径

延迟渲染是一种更古老的渲染方法,因为前向渲染的性能瓶颈问题,所以又被重视,除了前向渲染使用的深度和颜色缓冲区之外,还使用了额外的缓冲区G-Buffer缓冲,G缓冲区储存了我们所关心表面的其他信息

  • 原理:两个Pass,第一个Pass不进行光照计算,仅通过深度缓冲区计算哪些片元是可见,将可见片元的相关信息储存在G缓冲区G-Buffer;然后第二个Pass,利用G缓冲区储存的各个片元信息(表面法线、视角方向、漫反射系数、材质属性等)进行真正的光照计算。

  • 优缺点:延迟渲染路径使用的Pass通常只有两个,其渲染的效率不依赖于场景的复杂度,适合在场景光源数目较多,在使用前向渲染时会引发渲染性能瓶颈时使用,并且延迟渲染路径中的每个光源都可以按照逐像素的方式处理,缺点是,不能够支持真正的抗锯齿anti-aliasing功能、不能够处理透明物体并且对显卡有一定要求

  • Unity中的延迟渲染路径:在Unity中使用延迟渲染路径,要求我们提供两个Pass,第一个用于把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光、深度缓存等信息渲染到屏幕空间的G缓冲区,对于每个物体这个Pass仅会执行一次;第二个Pass,用于计算真正的光照模型,这个Pass会使用上一个Pass中渲染的数据来计算最终的颜色并存储到帧缓冲区中

顶点照明渲染路径
  • 原理:实际上只使用了逐顶点的方式计算了光照,所以并不支持那些需要进行逐像素处理才能得到的效果,例如阴影、法线映射、高精度高光反射等

  • 优缺点:对硬件配置的需求最少、运算性能最高,但是得到的渲染效果较差

  • Unity中的顶点照明路径:unity的顶点照明渲染路径通常在一个Pass中就可以完成对物体的渲染,在这个Pass中会通过逐顶点处理计算出所有光源对该物体的照明,所以这个渲染路径计算快速兵器支持广泛

大多数情况下,一个项目只是用一种渲染路径,可以在Unity Player Setting中设置Rendering Path,也可以在Camera的Camera组件下设置该Camera特有的Rendering Path来覆盖掉Project Setting中的渲染路径设置

在Project中设置完毕后,我们就可以在pass中设置标签LightMode来指定pass使用的渲染路径,设置后Unity就会把不同的光照变量传递给Shader
例如指定Forward Base pass:

    pass{
        Tags{"LightMode" = "ForwardBase"}
        CGPROGRAM
        #pragma multi_compile_fwdbase

        ENDCG
    }

指定Forward Additional pass:

    pass{
        Tags{"LightMode" = "ForwardAdd"}
        Blend One One
        CGPROGRAM
        #pragma multi_compile_fwdadd

        ENDCG
    }

LightMode标签支持的渲染路径设置选项:

  • Always,永远都渲染,但不处理光照。
  • ForwardBase,用于前向渲染,指定后该pass会计算环境光、最重要的平行光、逐顶点、SH光源以及访问Lightingmaps,可以实现的光照效果:环境光、自发光、光照纹理、阴影
  • ForwardAdd,用于前向渲染,指定后该pass会计算逐像素光源,每个pass对应一个光源,可实现的光照效果:默认情况下不支持阴影但可以通过编译指令来实现
  • Deferred,用于延迟渲染,指定后该pass会渲染G-buffer缓冲
  • ShadowCaster,用于渲染产生阴影的物体。指定后把物体的深度信息渲染到阴影映射纹理Shadowmap或一张深度纹理中
  • ShadowCollector,用于收集物体阴影到屏幕坐标Buff里。

1.2.32 Pass通道的渲染状态设置

在Pass语义块中除了可以设置独立的标签还可以设置独立的渲染状态,例如在前向渲染路径中的Additional Pass中设置的混合系数Blend One One

1.2.33 CG代码块

Shader "Custom/YourShaderName"{
    Properties{
        _Color("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
    }

    SubShader{
        Pass{
            CGPROGRAM
            //CG code elide

            ENDCG
        }
    }
}

CG代码块中的Vertex Shader & Fragment Shader

可编程Shader分为vertex shader和fragment shader,其中vertex shader分别对应渲染管线的顶点变换和光照,fragment shader对应光栅化之后的像素处理。

//指定顶点、片元函数
#pragma vertex vert
#pragma fragment frag

定义结构体a2v、v2f

struct a2v{
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;

}

struct v2f{
    float4 pos : SV_POSITION;
    fixed3 color : COLOR0;
}
  • POSITION语义告诉unity将模型空间中顶点的位置信息填充至特定参数
  • NORMAL语义告诉unity将模型空间中顶点的法线方向填充至特定变量
  • TEXCOORD0语义告诉unity将模型的第一套纹理坐标填充至特定变量
  • COLOR0语义告诉unity,该变量储存了一个颜色信息
  • SV_POSITION语义告诉unity,该变量中包含了顶点在剪裁空间的位置信息
  • SV_Target语义告诉渲染器将用户的输出颜色存储到一个渲染目标(Render Target)中,默认存储带帧缓存中

PS:在unity中POSITION、NORMAL、TANGENT这些数据都是由Unity的MeshRender组件提供的,在每帧调用Draw Call时,MeshRender组件会把它所负责渲染的模型数据发送给shader

1.3 Fallback语义块

    Fallback "Specular"

Fallback语义快用于对Unity Shader进行回调,例如当我们对目标物体的Mesh Render组件设置了能够投射阴影Caster Shadow,但却并没有在该物体所用的Shader中编写用于前向渲染的ShadowCaster的Pass,但是却可以在渲染出该物体的阴影,因为我们在Shader的最后加入了Fallback "Specular"回调,虽然在内置的Specular中也没有这样一个pass,但是会继续进行回调直到内置的VertexLit,其中就有LightMode设置为ShadowCaster的pass

REF

书籍:

OpenGL超级宝典、UnityShader入门

文档:

https://docs.unity3d.com/Manual/SL-Shader.html

博客:

https://blog.csdn.net/hcud024/article/details/51360099

posted @ 2019-03-24 18:32  SylvanYan  阅读(1259)  评论(0编辑  收藏  举报
TOP