Unity3D Shader创建一个双层的透明Shader(一)

上节我们介绍了Unity Shader 的 基础知识。害怕初学者还是看的云里雾里的。所以我们这章不如找个例子来分析并且做出来。

这里写图片描述

如上图。我们这里就一步步分析怎么从零到有创建一个这样的Shader。


由上图我们可以看到。

1. 材质是里外都展示
2. 为透明材质
3. 内外颜色基础贴图和法线贴图不一样(这个你们在上图中看不出来,我告诉你们就好)
4. 加了 Mask(蒙版)是部分缕空
5. 内外的颜色不一样


SO:

1. 我们需要在SubShader语块中设置他的Culloff,来使材质里外都显示。

属性名称 参数 作用
Cull Back 剔除内侧,只显示外侧。默认值
Cull Front 剔除外侧,只显示内侧
Cull off 关闭剔除。内外都展示

代码:

SubShader
    {
      ....
      Cull Off
      ....
    }

2. 使材质为透明缕空材质,则需要在Tags 标签里面添加对应的标签。

  • RenderType
参数 作用
Opaque 不透明:大部分着色器(普通物体,自发光,反射,地形着色器)
Transparent 透明:大部分半透明对象(透明物体,粒子,字体,地形附加物(草,花,树))。
TransparentCutout 遮罩透明类型。用于缕空效果的几何
Background 类似Skybox用的类型
Overlay 覆盖效果的对象。GUITexture,Halo,Flare
TreeOpaque 类似地形上树皮使用的类型
TreeTransparentCutout 类似树叶使用的类型
TreeBillboard 类似地形上永远面向摄像机视角的 树木
Grass
GrassBillboard 类似地形上永远面向摄像机视角的 草。
  • Queue : 渲染队列。Shader决定对象属于哪个渲染队列。队列的不同,则计算机渲染的顺序不同。
参数 作用
Background 一开始就渲染的队列,比如天空材质
Geometry 不透明 几何使用这个队列。
AlphaTest 介于 Geometry 和 Transparent 之间。
Transparent 不写入深度缓冲区的着色器(玻璃,粒子)使用的队列
Overlay 覆盖效果的对象使用这个队列,比如 镜头耀斑

代码:

SubShader
    {
      ....
      Tags{ "RenderType" = "TransparentCutout"  "Queue" = "AlphaTest+0" }
      ....
    }

3. 内外颜色基础贴图和法线贴图不一样的话,就得声明一些“Properties”

代码:

    Properties
    {
        // 蒙版修剪大小
        _MaskClipValue( "Mask Clip Value", Float ) = 0.5
        //外面的基础颜色
        _FrontFacesColor("Front Faces Color", Color) = (1,0,0,0)
        //外面的基础贴图
        _FrontFacesAlbedo("Front Faces Albedo", 2D) = "white" {}
        //外面的法线贴图
        _FrontFacesNormal("Front Faces Normal", 2D) = "bump" {}

        //内面的基础颜色
        _BackFacesColor("Back Faces Color", Color) = (0,0.04827571,1,0)
        //内面的基础贴图
        _BackFacesAlbedo("Back Faces Albedo", 2D) = "white" {}
        //内面的发现贴图
        _BackFacesNormal("Back Faces Normal", 2D) = "bump" {}

        //蒙版贴图  
        _OpacityMask("Opacity Mask", 2D) = "white" {}
    }

同时也要在“SubShader” 语块中声明。

SubShader{
        ...
        CGPROGRAM
        ...
        uniform sampler2D _FrontFacesNormal;
        uniform float4 _FrontFacesNormal_ST;
        uniform sampler2D _BackFacesNormal;
        uniform float4 _BackFacesNormal_ST;
        uniform float4 _FrontFacesColor;
        uniform sampler2D _FrontFacesAlbedo;
        uniform float4 _FrontFacesAlbedo_ST;
        uniform float4 _BackFacesColor;
        uniform sampler2D _BackFacesAlbedo;
        uniform float4 _BackFacesAlbedo_ST;
        uniform sampler2D _OpacityMask;
        uniform float4 _OpacityMask_ST;
        uniform float _MaskClipValue = 0.5;
        ...
        ENDCG
        ...
        }

有人会疑惑。为什么要声明两次呢?
我们用来实例的这个shader其实是由两个相对独立的块组成的,外层的Properties属性声明,Fallback回滚等等是Unity可以直接使用和编译的ShaderLab;而现在我们是在CGPROGRAM…ENDCG这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在Properties中所定义的变量的话,必须使用和之前变量相同的名字进行声明。。

扫描二维码关注公众号,回复: 443371 查看本文章

4. 渲染内外面。
首先我们先忽略Mask蒙版效果。先把外层渲染出来。这就很简单了。学过U4E的同学应该知道。使用颜色和贴图一混合就OK。
这里写图片描述


此处得先介绍一些表面着色器的知识:
上篇文章说过,我们写Shader就是计算他的颜色,反射,粗糙度等等属性。那么要计算这些,首先就得获取一些纹理贴图,顶点,光照的方向等等的数据。而这些数据就包含在 Shader 的 结构体中。为我们提供方便的获取,以及处理,然后返回。

参数 解析
Input i 输入结构体 $1600
inout SurfaceOutputStandard o 听名字“inout”就知道。这个是输出值。

输入的结构体中所以的变量:

struct Input
        {
            float2 uv_MainTex;   //主贴图UV
            float2 uv_BumpMap;   //法线贴图UV
            float2 uv_Detail;    //细节贴图UV
            float3 viewDir;  //视角方向。既是摄像机的方向
            float4 with COLOR ;  //包含每个顶点颜色
            float4 screenPos;    //屏幕空间的位置。用于反射或屏幕空间的效果的屏幕空间位置
            float3 worldPos ;    //世界坐标的数据
            float3 worldRefl ;   //如果不写入 o.Normal ,则包含世界反射向量
            float3 worldNormal ;  //如果不写入 o.Normal,则包含世界法向量
        }

输出的结构体中所以的变量:

//普通的surface shaders结构体
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
};

//在Unity 5中,表面着色器也可以使用基于物理的照明模型。

//标准的surface shaders结构体
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
};

//标准的镜面surface shaders结构体
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
};

更多官方介绍:Writing Surface Shaders

正式代码:

//surf就是那个处理函数。他就想一个锅。i 就是你要的各种佐料,o 就是食材,
//然后通过一大段的计算,“砰”,最后得到的一团黑黑的东西就是我们劳动成果了
//(原谅我不会做饭,哈哈哈~~~)

void surf( Input i , inout SurfaceOutputStandard o )
{
            //使用 WorldNormalVector 根据每个像素的法线贴图获取法线矢量
            float3 ase_worldNormal = WorldNormalVector( i, float3( 0, 0, 1 ) );

            //UnityWorldSpaceViewDir 来自 “UnityCG.cginc” 。
            //normalize( UnityWorldSpaceViewDir( i.worldPos ) )用与计算世界空间视图方向
            float3 worldViewDir = normalize( UnityWorldSpaceViewDir( i.worldPos ) );

            //得到两个向量的点积。来判断他们的夹角关系。
            //如果点积为负则证明为反方向,既内部。为正则为正方向,外部
            float dotResult20 = dot( ase_worldNormal , worldViewDir );

            float FaceSign48 = (1.0 + (sign( dotResult20 ) - -1.0) * (0.0 - 1.0) / (1.0 - -1.0));


            float2 uv_FrontFacesAlbedo = i.uv_texcoord * _FrontFacesAlbedo_ST.xy + _FrontFacesAlbedo_ST.zw;
            //使用tex2D采样,获取uv对应的图片的颜色数据 再 Multiply 它基础颜色
            float4 FrontFacesAlbedo44 = ( _FrontFacesColor * tex2D( _FrontFacesAlbedo, uv_FrontFacesAlbedo ) );


            float2 uv_BackFacesAlbedo = i.uv_texcoord * _BackFacesAlbedo_ST.xy + _BackFacesAlbedo_ST.zw;
            //使用tex2D采样,获取uv对应的图片的颜色数据 再 Multiply 它基础颜色
            float4 BackFacesAlbedo47 = ( _BackFacesColor * tex2D( _BackFacesAlbedo, uv_BackFacesAlbedo ) );

            //通过插值来分别渲染内部和外部
            float4 lerpResult24 = lerp( FrontFacesAlbedo44 , BackFacesAlbedo47 , FaceSign48);

            //把新得到的颜色赋值给输出值
            o.Albedo = lerpResult24.rgb;
            //透明度为 1
            o.Alpha = 1;
}

了解更多Unity Shader 的内部函数

加上 法线贴图和Mask蒙版遮罩后的完整代码:

void surf( Input i , inout SurfaceOutputStandard o )
        {
            float2 uv_FrontFacesNormal = i.uv_texcoord * _FrontFacesNormal_ST.xy + _FrontFacesNormal_ST.zw;
            float3 FrontFacesNormal51 = UnpackNormal( tex2D( _FrontFacesNormal, uv_FrontFacesNormal ) );
            float2 uv_BackFacesNormal = i.uv_texcoord * _BackFacesNormal_ST.xy + _BackFacesNormal_ST.zw;
            float3 BackFacesNormal54 = UnpackNormal( tex2D( _BackFacesNormal, uv_BackFacesNormal ) );
            float3 ase_worldNormal = WorldNormalVector( i, float3( 0, 0, 1 ) );
            float3 worldViewDir = normalize( UnityWorldSpaceViewDir( i.worldPos ) );
            float dotResult20 = dot( ase_worldNormal , worldViewDir );
            float FaceSign48 = (1.0 + (sign( dotResult20 ) - -1.0) * (0.0 - 1.0) / (1.0 - -1.0));
            float3 lerpResult64 = lerp( FrontFacesNormal51 , BackFacesNormal54 , FaceSign48);
            o.Normal = lerpResult64;
            float2 uv_FrontFacesAlbedo = i.uv_texcoord * _FrontFacesAlbedo_ST.xy + _FrontFacesAlbedo_ST.zw;
            float4 FrontFacesAlbedo44 = ( _FrontFacesColor * tex2D( _FrontFacesAlbedo, uv_FrontFacesAlbedo ) );
            float2 uv_BackFacesAlbedo = i.uv_texcoord * _BackFacesAlbedo_ST.xy + _BackFacesAlbedo_ST.zw;
            float4 BackFacesAlbedo47 = ( _BackFacesColor * tex2D( _BackFacesAlbedo, uv_BackFacesAlbedo ) );
            float4 lerpResult24 = lerp( FrontFacesAlbedo44 , BackFacesAlbedo47 , FaceSign48);
            o.Albedo = lerpResult24.rgb;
            o.Alpha = 1;

            //Mask 蒙版遮罩
            float2 uv_OpacityMask = i.uv_texcoord * _OpacityMask_ST.xy + _OpacityMask_ST.zw;
            float OpacityMask56 = tex2D( _OpacityMask, uv_OpacityMask ).a;
            clip( OpacityMask56 - _MaskClipValue );
        }

5. 完整代码展示。

  Shader "SampleShaders/2 Sided"
{
    Properties
    {
        [HideInInspector] __dirty( "", Int ) = 1
        _MaskClipValue( "Mask Clip Value", Float ) = 0.5
        _FrontFacesColor("Front Faces Color", Color) = (1,0,0,0)
        _FrontFacesAlbedo("Front Faces Albedo", 2D) = "white" {}
        _FrontFacesNormal("Front Faces Normal", 2D) = "bump" {}
        _BackFacesColor("Back Faces Color", Color) = (0,0.04827571,1,0)
        _BackFacesAlbedo("Back Faces Albedo", 2D) = "white" {}
        _BackFacesNormal("Back Faces Normal", 2D) = "bump" {}
        _OpacityMask("Opacity Mask", 2D) = "white" {}
        [HideInInspector] _texcoord( "", 2D ) = "white" {}
    }

    SubShader
    {
        Tags{ "RenderType" = "TransparentCutout"  "Queue" = "AlphaTest+0" }
        Cull Off
        Stencil
        {
            Ref 1
            Comp Always
            Pass Replace
        }
        CGINCLUDE
        #include "UnityPBSLighting.cginc"
        #include "Lighting.cginc"
        #pragma target 3.0
        #ifdef UNITY_PASS_SHADOWCASTER
            #undef INTERNAL_DATA
            #undef WorldReflectionVector
            #undef WorldNormalVector
            #define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
            #define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
            #define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))
        #endif
        struct Input
        {
            float2 uv_texcoord;
            float3 worldNormal;
            INTERNAL_DATA
            float3 worldPos;
        };

        uniform sampler2D _FrontFacesNormal;
        uniform float4 _FrontFacesNormal_ST;
        uniform sampler2D _BackFacesNormal;
        uniform float4 _BackFacesNormal_ST;
        uniform float4 _FrontFacesColor;
        uniform sampler2D _FrontFacesAlbedo;
        uniform float4 _FrontFacesAlbedo_ST;
        uniform float4 _BackFacesColor;
        uniform sampler2D _BackFacesAlbedo;
        uniform float4 _BackFacesAlbedo_ST;
        uniform sampler2D _OpacityMask;
        uniform float4 _OpacityMask_ST;
        uniform float _MaskClipValue = 0.5;

        void surf( Input i , inout SurfaceOutputStandard o )
        {
            float2 uv_FrontFacesNormal = i.uv_texcoord * _FrontFacesNormal_ST.xy + _FrontFacesNormal_ST.zw;
            float3 FrontFacesNormal51 = UnpackNormal( tex2D( _FrontFacesNormal, uv_FrontFacesNormal ) );
            float2 uv_BackFacesNormal = i.uv_texcoord * _BackFacesNormal_ST.xy + _BackFacesNormal_ST.zw;
            float3 BackFacesNormal54 = UnpackNormal( tex2D( _BackFacesNormal, uv_BackFacesNormal ) );
            float3 ase_worldNormal = WorldNormalVector( i, float3( 0, 0, 1 ) );
            float3 worldViewDir = normalize( UnityWorldSpaceViewDir( i.worldPos ) );
            float dotResult20 = dot( ase_worldNormal , worldViewDir );
            float FaceSign48 = (1.0 + (sign( dotResult20 ) - -1.0) * (0.0 - 1.0) / (1.0 - -1.0));
            float3 lerpResult64 = lerp( FrontFacesNormal51 , BackFacesNormal54 , FaceSign48);
            o.Normal = lerpResult64;
            float2 uv_FrontFacesAlbedo = i.uv_texcoord * _FrontFacesAlbedo_ST.xy + _FrontFacesAlbedo_ST.zw;
            float4 FrontFacesAlbedo44 = ( _FrontFacesColor * tex2D( _FrontFacesAlbedo, uv_FrontFacesAlbedo ) );
            float2 uv_BackFacesAlbedo = i.uv_texcoord * _BackFacesAlbedo_ST.xy + _BackFacesAlbedo_ST.zw;
            float4 BackFacesAlbedo47 = ( _BackFacesColor * tex2D( _BackFacesAlbedo, uv_BackFacesAlbedo ) );
            float4 lerpResult24 = lerp( FrontFacesAlbedo44 , BackFacesAlbedo47 , FaceSign48);
            o.Albedo = lerpResult24.rgb;
            o.Alpha = 1;


            float2 uv_OpacityMask = i.uv_texcoord * _OpacityMask_ST.xy + _OpacityMask_ST.zw;
            float OpacityMask56 = tex2D( _OpacityMask, uv_OpacityMask ).a;
            clip( OpacityMask56 - _MaskClipValue );
        }

        ENDCG
        CGPROGRAM
        #pragma surface surf Standard keepalpha fullforwardshadows 

        ENDCG

    }
    Fallback "Diffuse"
    CustomEditor "ASEMaterialInspector"
}

提示:如果有些不懂的。其实不需要都懂的。慢慢来。因为我也不是全都明白这里写图片描述

猜你喜欢

转载自blog.csdn.net/kitok/article/details/79553311