U3D Shader

版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/blues1021。 https://blog.csdn.net/Blues1021/article/details/78165096
Shader实现各种材质和光照效果,镜头景深,动态模糊,卡通渲染等。
U3D是简单的游戏引擎,提供了尽量多的内建功能,丰富全面的Shader就是其中之一。要善于使用U3D内建的Shader和自己编写的Shader实现效果。

一.内建Shader有简单光效,高光,法线,反射等,分类有:

1)普通,用于不透明的物体。
2)透明,用于透明物体。
3)透明镂空效果,用于包含完全透明部分的半透明对象。
4)自发光。
5)反射,用于能反射环境立方体贴图的不透明对象。
每个类型都包含了 Vertex lit, simple, bumped(凹凸法线), parallax bumped(视差凹凸高光) 从简单到复杂的光照效果,每种类型越复杂性能开销越大。

二.U3D ShaderLab基础语法结构:

Shader "Custom/NewShader" { // 名称
Properties { // 定义属性
_MainTex ("Base (RGB)", 2D) = "white" {}
}
// 子着色器1,U3D会从上往下找到一个子着色器则运行之,后面的不再理会。
SubShader { // 实现代码
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert

sampler2D _MainTex;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
// 子着色器2
SubShader {}
// 如果所有着色器都不能正确运行,使用备用着色器
FallBack "Diffuse"
}

属性:
Properties
{
    _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) = "" {}
}

SubShader:
子着色器,会从上到下选择一个进行,为了兼容不同硬件要注意支持下。
Pass:
有多个少个pass则会渲染多少遍(draw call),因此要减少pass的使用。Pass里面可以包含名称和标签用于重用,和渲染设置图形硬件的各种状态和功能。
SubShader { Pass { Lighting Off SetTexture [_MainTex] {} }}
UsePass "Specular/BASE" 可以重用Pass需要指明着色器名,和pass名称,方便Pass重用,可以跨着色器共享代码片断。
GrabPass 可以将本次渲染的后台缓存抓取到纹理中,后期可以用_GrabTexture来访问。
FallBack:
FallBack "Diffuse" 使用默认的Diffuse着色器。
或FallBack Off 关闭,没有子着色器选中也不会报错。
Category分类器:
分类可以让多个子着色器继承分类的设置,例如:
Shader "example"{
Category{
Fog {Mode off}
Blend one one
// 后面的子着色器可以继承Category中的设置,避免每个都写一份,改类型不能跨着色器使用
SubShader{
}
SubShader{
}
}
}
Shader Lab语法详解
Shader "name" { //只有一个Shader名字
[Properties] // 键值对,暴露给Unity编辑器,让 美术进行调整
Category
{
// 逻辑上封装的渲染状态,避免写很多的重复状态,后面多个subShader不用声明就能公用。只有在shader括号内起效,且不会加快运行速度。
例如:fragment后的雾颜色,测试后的alpha融合设置,可以被后面多个subshader公用。
Fog { Mode Off }Blend One One

Subshader { // unity会根据当前硬件,从上往下选择一个Shader进行渲染
[Tags]
标签键值对,用于渲染层级等
the following tags recognized by Unity  must  be inside SubShader section and not inside Pass!
Rendering Order - Queue tag
例如:
Tags { "Queue" = "Transparent" }
RenderType tag
DisableBatching tag
ForceNoShadowCasting tag
IgnoreProjector tag
CanUseSpriteAtlas tag
PreviewType tag

[CommonState] // This will make all passes use this “shared” state.
Pass {
// 多个pass 将会进行多次渲染 ,在GPU中多次draw call(非CPU的),会增加开销,尽量少pass.
// Pass的重用语法:UsePass "Shader/Name"
例如在Pass内部:
UsePass "VertexLit/SHADOWCASTER"
Name "MyPassName"
// 获取当前绘制产生在后台缓冲中的纹理,用GrabPass
例如:
GrabPass { "_BackgroundTexture" //后面pass可以使用该纹理 }
[Name and Tags]
// name是Pass的名字,用于标识和重用;Tags键值对可以指定渲染类型,延后或向前渲染
// 例如:向前渲染的 ForwardAdd对物体为每盏灯都执行Pass(应该只是灯光颜色部分)。
Note that the following tags recognized by Unity  must  be inside Pass section and not inside SubShader!
LightMode tag
ForwardBase等
PassFlags tag
OnlyDirectional
RequireOptions tag
SoftVegetation
[RenderSetup]
/*渲染状态的设置,类似:
Cull Back | Front | Off
ZTest (Less | Greater | LEqual | GEqual | Equal | NotEqual | Always)
ZWrite On | Off
Offset OffsetFactor, OffsetUnits
Blend sourceBlendMode destBlendModeBlendOp colorOp
ColorMask RGB | A | 0 | any combination of R, G, B, A
仅在遗留的固定管线Shader中起作用的:
Lighting On | OffMaterial { Material Block }SeparateSpecular On | OffColor Color-valueColorMaterial AmbientAndDiffuse | Emission
Fog { Fog Block }
AlphaTest (Less | Greater | LEqual | GEqual | Equal | NotEqual | Always) CutoffValue
SetTexture textureProperty { combine options }
*/
}
}
}
[Fallback] //没有Subshader满足,则用Fallback的Shader
[CustomEditor]
A CustomEditor can be defined for your shader,When you do this Unity will look for a class that extends ShaderGUI with this name. If one is found any material that uses this shader will use this ShaderGUI.
}

三.U3D Shader分为三种类型:

1.Surface shader

是经过U3D封装的,可以方便的实现灯光,阴影和投影器效果,可以同时正常的工作在向前和延后的光照渲染路径中。用ShaderLab包装CG/HLSL语言编写,且封装了很多内置的函数。
需要放置在 CGPROGREAM ... ENDCG代码块之间,会自己编译到各个pass中。
使用 #pragma surface 表面函数 光照模型 [可选参数] 命令来指明它是一个表面着色器。
表面函数的输入是自定义的Input结构体
结构体可以包含的数据是:
float2 uv_Tex1Name(纹理1的uv坐标名称);
float2 uv2_Tex2Name(纹理2的uv坐标名称);
float4 color;
float3 viewDir;
float4 screenPos
float3 worldPos
float3 worldRefl
float3 worldNormal
INTERNAL_DATA
输出是SurfaceOutput结构体:
struct SurfaceOutput{
half3 Albedo;// 反射光
half3 Normal; // 法线
half3 Enission; // 自发光
half Specular; // 高光
half Gloss; // 光泽度
half Alpha; // 透明度
}
Surface shader 会在封装的内部使用光照模型对这里输出的SurfaceOutput进行光照计算得到最终颜色。
示例:
Shader "Example/Diffuse Bump" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
        float2 uv_MainTex;
        float2 uv_BumpMap;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }
自定义光照模型:
 ...ShaderLab code...
    CGPROGRAM
    #pragma surface surf WrapLambert

    half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
        half NdotL = dot (s.Normal, lightDir);
        half diff = NdotL * 0.5 + 0.5;
        half4 c;
        c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
        c.a = s.Alpha;
        return c;
    }

    struct Input {
        float2 uv_MainTex;
    };
    
    sampler2D _MainTex;
        void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    }
    ENDCG
    ...ShaderLab code...
拓展更多详细信息:
(1)编译配置
会自动生成一些代码,将不同光照类型,不同阴影选择,不同渲染路径(向前或延后渲染)封装起来,根据一些开关,就可以编译生成完全的Vertex/Fragment Shader。
标志是:
  CGPROGRAM..ENDCG在SubShader内部,不在Pass内。
#pragma surface surfaceFunction lightModel [optionalparams]
surfaceFunction一般是surf函数,lightModel 是内建的光照模型,
optionalparams可以是:
Transparency and alpha testing类型:
alpha:blend
decal:add //贴花,在其它shader前面
decal:blend // 贴花使用alpha blend
Custom modifier functions类型:
vertex:VertexFunction  // 顶点shader自定义函数,会插入默认的vertexshader前面,处理每个顶点的信息。
finalcolor:ColorFunction  //片段着色器中,最终计算颜色的函数
finalgbuffer:ColorFunction  - Custom deferred path for altering gbuffer content.
finalprepass:ColorFunction  - Custom prepass base path.
Shadows and Tessellation类型:
addshadow
fullforwardshadows
tessellate:TessFunction
Code generation options类型:
exclude_path:deferred exclude_path:forward exclude_path:prepass
  • noshadow - Disables all shadow receiving support in this shader.
  • noambient - Do not apply any ambient lighting or light probes.
  • novertexlights - Do not apply any light probes or per-vertex lights in Forward rendering.
  • nofog - Disables all built-in Fog support.
等。
(2).Input surface着色器输入类型
输入是位置,uv,法向量,颜色等作为输入,在编辑器中设置输入 就行。
The input structure  Input  generally has any texture coordinates needed by the shader. Texture coordinates must be named “ uv ” followed by texture name (or start it with “ uv2 ” to use second texture coordinate set).
也 可以是其它类型:
Additional values that can be put into Input structure:
  • float3 viewDir - contains view direction, for computing Parallax effects, rim lighting etc.
  • float4 with COLOR semantic - contains interpolated per-vertex color.
  • float4 screenPos - contains screen space position for reflection or screenspace effects. Note that this is not suitable for GrabPass; you need to compute custom UV yourself using ComputeGrabScreenPos function.
  • float3 worldPos - contains world space position.
  • float3 worldRefl - contains world reflection vector if surface shader does not write to o.Normal. See Reflect-Diffuse shader for example.
  • float3 worldNormal - contains world normal vector if surface shader does not write to o.Normal.
  • float3 worldRefl; INTERNAL_DATA - contains world reflection vector if surface shader writes to o.Normal. To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN, o.Normal). See Reflect-Bumped shader for example.
  • float3 worldNormal; INTERNAL_DATA - contains world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal).
自定义Surface shader的Input结构体,只能使用前面预定义的类型。
实例代码:
Shader "Example/Rim" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
      _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 viewDir;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      float4 _RimColor;
      float _RimPower;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
          half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
          o.Emission = _RimColor.rgb * pow (rim, _RimPower);
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }
(3)surface着色器输出类型
输出是:
struct SurfaceOutput
{
    fixed3 Albedo;  // diffuse color
    fixed3 Normal;  // tangent space normal, if written
    fixed3 Emission;
    half Specular;  // specular power in 0..1 range
    fixed Gloss;    // specular intensity
    fixed Alpha;    // alpha for transparencies
};
unity5是:
struct SurfaceOutputStandard
{
    fixed3 Albedo;      // base (diffuse or specular) color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Metallic;      // 0=non-metal, 1=metal
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};
struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      // diffuse color
    fixed3 Specular;    // specular color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};
实例:
Shader "Example/Diffuse Bump" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
// 在 SubShader后,Tags后面指定CG语言
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
        float2 uv_MainTex;
        float2 uv_BumpMap;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
// 结束CG,结束SubShader
      ENDCG
    } 
    Fallback "Diffuse"
  }
(4)Surface生成代码查看
Surface类型可以在编辑器inspector shader组件下->Select Shader下的:
Show Generate code可以看到surface shader生成的代码,Complie and show code可以查看当前平台编译后生成的代码。
5)surface shader的限制
Currently some parts of surface shader compilation pipeline do not understand  DirectX 11 -specific HLSL syntax, so if you’re using HLSL features like StructuredBuffers, RWTextures and other non-DX9 syntax, you have to wrap it into a DX11-only preprocessor macro.

2.Vertex and fragment shader

可以非常灵活的实现效果但是要编写更多代码。用ShaderLab包装CG/HLSL语言编写。
需要放置在 ShaderLab的Pass命令
需要用 CGPROGREAM .. ENDCG包围起来。
需要用#pragma vertex vertfunctionname
#pragma fragment fragfunctionname 指定顶点和片断着色器函数,还有更多的编译命令,例如使用geometry shader,domain shader, glsl_no_auto_normalization 编译到移动平台时 关闭在顶点着色器中对法线向量和切线向量自动进行规范化,#pragma target2.0 指定编译目标对应不同版本的着色器模型。 可以使用#include "UnityCG.cginc" 引入U3D预定义内建的头文件,使用内建的函数和变量
例如:
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
实例代码:
Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        // we have removed support for texture tiling/offset,
        // so make them not be displayed in material inspector
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // use "vert" function as the vertex shader
            #pragma vertex vert
            // use "frag" function as the pixel (fragment) shader
            #pragma fragment frag

            // vertex shader inputs
            struct appdata
            {
                float4 vertex : POSITION; // vertex position
                float2 uv : TEXCOORD0; // texture coordinate
            };

            // vertex shader outputs ("vertex to fragment")
            struct v2f
            {
                float2 uv : TEXCOORD0; // texture coordinate
                float4 vertex : SV_POSITION; // clip space position
            };

            // vertex shader
            v2f vert (appdata v)
            {
                v2f o;
                // transform position to clip space
                // (multiply with model*view*projection matrix)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // just pass the texture coordinate
                o.uv = v.uv;
                return o;
            }
            
            // texture we will sample
            sampler2D _MainTex;

            // pixel shader; returns low precision ("fixed4" type)
            // color ("SV_Target" semantic)
            fixed4 frag (v2f i) : SV_Target
            {
                // sample texture and return it
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}


拓展更多详细信息:
顶点和片段着色器中的顶点着色器输入结构体定义在:UnityCG.cginc,可以借助CG的预定义,也可以不使用,不过一般顶点输入都使用。
例如顶点着色器的输入为:
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
实例:
Shader "Tutorial/Textured Colored" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _MainTex ("Texture", 2D) = "white" { }
    }
    SubShader {
        Pass {
	// 需要再pass后面
        CGPROGRAM
   // 编译使用的顶点着色器和像素着色器
        #pragma vertex vert
        #pragma fragment frag
// 引入预定义的CG结构体,和函数方便使用
        #include "UnityCG.cginc"

        fixed4 _Color;
        sampler2D _MainTex;

        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        float4 _MainTex_ST;
//appdata_base是 UnityCG.cginc中定义的输入结构体,也 可以自己指定
        v2f vert (appdata_base v)
        {
            v2f o;
// 利用UnityCG.cginc中定义的UnityObjectToClipPos(计算*MVP
            o.pos = UnityObjectToClipPos(v.vertex);
// 利用UnityCG.cginc确保uv是正确的 
            o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
// 像素着色器计算
            fixed4 texcol = tex2D (_MainTex, i.uv);
            return texcol * _Color;
        }
// 结束CG语言,结束Pass
        ENDCG

        }
    }
}

3.fixed function shaders

一般在老旧的硬件上使用,可以作为上述着色器的备用选择,用ShaderLab语言实现。主要用于开启图形管线状态和功能,设置材质和设置纹理。
固定管线的实例:
Shader "Example/Test" {
    Properties {
    }
SubShader {
    Pass {
        Lighting Off
        SetTexture [_MainTex] {}
    }
}
   Fallback "Diffuse"
  }

拓展更多详细信息:
fixed function shader只能用shaderlab来描述。
shaderlab语法,包含了固定管线中预定义的标准光照模型和设置渲染状态,一个很简短的语句包含了内部丰富的含义,所以要多查询,多应用才能熟练。
Shader "VertexLit" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }
// unity会从下往下选用硬件能够支持的SubShader 
    SubShader {
// 一个pass就是一次GPU渲染,尽量少用
        Pass {
// 定义材质,从inpector设置属性到材质之间的映射关系,材质属性是固定的,因为光照模型是固定的,这里的材质是指狭隘的物体的材质,其实都是狭隘的,只是unity材质球却是不一样的。
            Material {
                Diffuse [_Color]
                Ambient [_Color]
                Shininess [_Shininess]
                Specular [_SpecColor]
                Emission [_Emission]
            }
// 启用固定管线标准的顶点光照
            Lighting On
// 对于镜面高光,采用分开的颜色,也就是辅助颜色到片段着色后再应用
            SeparateSpecular On
// 定义应用的纹理,以及如何和光照混合应用
            SetTexture [_MainTex] {
 // 后面的constant就是constantColor 
                constantColor [_Color]
// Combine命令格式是:Combine ColorPart, AlphaPart
// 这里的意思是颜色部分:tex(texture,uv)*2倍的顶点光照主颜色,为了增强光照效果
// alpha部分用纹理的alpha乘以_Color颜色。
// 因为开启了镜面高光辅助颜色,最后颜色部分还会加上镜面反射的辅助颜色,为最终结果
                Combine texture * primary DOUBLE, texture * constant
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Blues1021/article/details/78165096