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

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

这里写图片描述

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

由上图我们可以看到。


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


SO:

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

属性名称参数作用
CullBack剔除内侧,只显示外侧。默认值
CullFront剔除外侧,只显示内侧
Culloff关闭剔除。内外都展示

代码:

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中所定义的变量的话,必须使用和之前变量相同的名字进行声明。。

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"
}

这里写图片描述



这里写图片描述

我是李本心明


首先谢谢大家的支持,其次如果你碰到什么其他问题的话,欢迎来 我自己的一个 讨论群559666429来(扫扫下面二维码或者点击群链接 Unity3D[ 交流] ),大家一起找答案,共同进步

由于工作生活太忙了,对于大家的帮助时间已经没有之前那么充裕了。如果有志同道合的朋友,可以接受无偿的帮助别人,可以加我QQ单独联系我,一块经营一下。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值