shader first

来源:互联网 发布:震撼人心的照片知乎 编辑:程序博客网 时间:2024/05/16 08:28
一个Shader有多个SubShader。一个SubShader可理解为一个Shader的一个渲染方案。即SubShader是为了针对不同的显卡而编写的。每个Shader至少1个SubShader、理论可以无限多个,但往往两三个就足够。
SubShader和显卡的兼容性判断,和SubShader的标签、Pass的标签和显卡支持的“Unity渲染路径”有关。这些都会在下面逐一提到。

SubShader的Tag

Shader "ShaderLab Tutorials/TestShader" {    SubShader    {        Tags { "Queue"="Geometry+10" "RenderType"="Opaque" }        //...    }}
SubShader内部可以有标签(Tags)的定义。Tag指定了这个SubShader的渲染顺序(时机),以及其他的一些设置。

"Queue"标签。定义渲染顺序。预制的值为
  • "Background"。值为1000。比如用于天空盒。
  • "Geometry"。值为2000。大部分物体在这个队列。不透明的物体也在这里。这个队列内部的物体的渲染顺序会有进一步的优化(应该是从近到远,early-z test可以剔除不需经过FS处理的片元)。其他队列的物体都是按空间位置的从远到近进行渲染。
  • "AlphaTest"。值为2450。已进行AlphaTest的物体在这个队列。
  • "Transparent"。值为3000。透明物体。
  • "Overlay"。值为4000。比如镜头光晕。
  • 用户可以定义任意值,比如"Queue"="Geometry+10"
  • "RenderType"标签。Unity可以运行时替换符合特定RenderType的所有Shader。Camera.RenderWithShader或者Camera.SetReplacementShader配合使用。Unity内置的RenderType包括:
    • "Opaque":绝大部分不透明的物体都使用这个;
    • "Transparent":绝大部分透明的物体、包括粒子特效都使用这个;
    • "Background":天空盒都使用这个;
    • "Overlay":GUI、镜头光晕都使用这个;
    • 还有其他可参考Rendering with Replaced Shaders;用户也可以定义任意自己的RenderType字符串。
  • "ForceNoShadowCasting",值为"true"时,表示不接受阴影。
  • "IgnoreProjector",值为"true"时,表示不接受Projector组件的投影。

    Pass

    Shader "ShaderLab Tutorials/TestShader" {    SubShader {        Pass        {            //...        }    }}

    一个SubShader(渲染方案)是由一个个Pass块来执行的。每个Pass都会消耗对应的一个DrawCall。在满足渲染效果的情况下尽可能地减少Pass的数量。

    一个SubShader(渲染方案)是由一个个Pass块来执行的。每个Pass都会消耗对应的一个DrawCall。在满足渲染效果的情况下尽可能地减少Pass的数量。

    Pass的Tag

    Shader "ShaderLab Tutorials/TestShader" {    SubShader {        Pass        {            Tags{ "LightMode"="ForwardBase" }            //...        }    }}

    和SubShader有自己专属的Tag类似,Pass也有Pass专属的Tag。
    其中最重要Tag是 "LightMode",指定Pass和Unity的哪一种渲染路径(“Rendering Path”)搭配使用。这里需要描述的Tag取值可包括:

    • Always,永远都渲染,但不处理光照
    • ShadowCaster,用于渲染产生阴影的物体
    • ShadowCollector,用于收集物体阴影到屏幕坐标Buff里。

    其他渲染路径相关的Tag详见下面章节“Unity渲染路径种类”。
    具体所有Tag取值,可参考ShaderLab syntax: Pass Tags。

    FallBack

    Shader "ShaderLab Tutorials/TestShader"{    SubShader { Pass {} }    FallBack "Diffuse" // "Diffuse"即Unity预制的固有Shader    // FallBack Off //将关闭FallBack}

    当本Shader的所有SubShader都不支持当前显卡,就会使用FallBack语句指定的另一个Shader。FallBack最好指定Unity自己预制的Shader实现,因其一般能够在当前所有显卡运行。

    Properties

    Shader "ShaderLab Tutorials/TestShader"{    Properties {    _Range ("My Range", Range (0.02,0.15)) = 0.07 // sliders    _Color ("My Color", Color) = (.34, .85, .92, 1) // color    _2D ("My Texture 2D", 2D) = "" {} // textures    _Rect("My Rectangle", Rect) = "name" { }    _Cube ("My Cubemap", Cube) = "name" { }    _Float ("My Float", Float) = 1    _Vector ("My Vector", Vector) = (1,2,3,4)    }    // Shader    SubShader{        Pass{          //...          uniform float4 _Color;          //...          float4 frag() : COLOR{ return fixed4(_Color); }          //...          }    }    //fixed pipeline    SubShader    {        Pass{            Color[_Color]        }    }}
  • Shader在Unity编辑器暴露给美术的参数,通过Properties来实现。
  • 所有可能的参数如上所示。主要也就Float、Vector和Texture这3类。
  • 除了通过编辑器编辑Properties,脚本也可以通过Material的接口(比如SetFloatSetTexture编辑)
  • 之后在Shader程序通过[name](固定管线)或直接name(可编程Shader)访问这些属性。

    Shader中的数据类型

    有3种基本数值类型:floathalffixed
    这3种基本数值类型可以再组成vector和matrix,比如half3是由3个half组成、float4x4是由16个float组成。

    • float:32位高精度浮点数。
    • half:16位中精度浮点数。范围是[-6万, +6万],能精确到十进制的小数点后3.3位。
    • fixed:11位低精度浮点数。范围是[-2, 2],精度是1/256。

    数据类型影响性能

    • 精度够用就好。
      • 颜色和单位向量,使用fixed
      • 其他情况,尽量使用half(即范围在[-6万, +6万]内、精确到小数点后3.3位);否则才使用float
    • 不要将低精度fixed类型转换为更高的精度,否则会产生性能问题。
    • 低精度fixed不要使用“swizzle”(即形如myFixed4.xyzwmyFixed2.xyxy,中文不知咋译,“搅和
      访问”?),否则会产生性能问题。

    Shader形态

    Shader形态之1:固定管线

    固定管线是为了兼容老式显卡。都是顶点光照。之后固定管线可能是被Unity抛弃的功能,所以最好不学它、当它不存在。特征是里面出现了形如下面Material块、没有CGPROGRAMENDCG块。

    Shader "ShaderLab Tutorials/TestShader"{    Properties {    _Color ("My Color", Color) = (.34, .85, .92, 1) // color    }    // Fixed Pipeline    SubShader    {        Pass        {            Material{            Diffuse [_Color]            Ambient [_Color]            }            Lighting On        }    }}

    Shader形态之2:可编程Shader

    Shader "ShaderLab Tutorials/TestShader"{    Properties {}    SubShader    {        Pass        {          // ... the usual pass state setup ...          CGPROGRAM          // compilation directives for this snippet, e.g.:          #pragma vertex vert          #pragma fragment frag          // the Cg/HLSL code itself          float4 vert(float4 v:POSITION) : SV_POSITION{              return mul(UNITY_MATRIX_MVP, v);          }          float4 frag() : COLOR{              return fixed4(1.0, 0.0, 0.0, 1.0);          }          ENDCG          // ... the rest of pass setup ...          }    }}
      • 功能最强大、最自由的形态。
      • 特征是在Pass里出现CGPROGRAMENDCG
      • 编译指令#pragma。详见官网Cg snippets。其中重要的包括:
      编译指令示例/含义#pragma vertex name
      #pragma fragment name替换name,来指定Vertex Shader函数、Fragment Shader函数。#pragma target name替换name(为2.03.0等)。设置编译目标shader model的版本。#pragma only_renderers name name ...
      #pragma exclude_renderers name name...#pragma only_renderers gles gles3
      #pragma exclude_renderers d3d9 d3d11 opengl
      只为指定渲染平台(render platform)编译
    • 引用库。通过形如#include "UnityCG.cginc"引入指定的库。常用的就是UnityCG.cginc了。其他库详见官网Built-in shader include files。

    • ShaderLab内置值。Unity给Shader程序提供了便捷的、常用的值,比如下面例子中的UNITY_MATRIX_MVP就代表了这个时刻的MVP矩阵。详见官网ShaderLab built-in values。

    CC_MVPMatrix的作用

    CC_MVPMatrix本质上是一个变换矩阵,用来把一个世界坐标系中的点转换到Clipping space。当然,如果学过OpenGL的人都知道,3D物体从建模到最终显示到屏幕上面要经历以下几个阶段:

    • 对象空间(Object Space)
    • 世界空间(World Space)
    • 照相机空间(Camera Space)
    • 裁剪空间(Clipping Space)
    • 视口空间(Viewport)
    从对象空间到世界空间的变换通常叫做Model-To-World变换,从世界空间到照相机空间的变换叫做World-To-View变换,而从照相机空间到裁剪空间的变换叫做View-To-Projection。合起来,就是我们常常提到的MVP变换。这里面每一个变换都是乘以一个矩阵,3个矩阵相乘最后还是一个矩阵,也就是cocos2d-x里面的CC_MVPMatrix啦。当然,实际开发过程中,我们往往会把MV变换放到一起,一般做法如下


  • Shader输入输出参数语义(Semantics)。在管线流程中每个阶段之间(比如Vertex Shader阶段和FragmentShader阶段之间)的输入输出参数,通过语义字符串,来指定参数的含义。常用的语义包括:COLORSV_PositionTEXCOORD[n]。完整的参数语义可见HLSL Semantic(由于是HLSL的连接,所以可能不完全在Unity里可以使用)。

  • 特别地,因为Vertex Shader的的输入往往是管线的最开始,Unity为此内置了常用的数据结构:点击打开链接 go on
  • 0 0