上节我们介绍了Unity Shader 的 基础知识。害怕初学者还是看的云里雾里的。所以我们这章不如找个例子来分析并且做出来。
如上图。我们这里就一步步分析怎么从零到有创建一个这样的Shader。
由上图我们可以看到。
1. 材质是里外都展示
2. 为透明材质
3. 内外颜色基础贴图和法线贴图不一样(这个你们在上图中看不出来,我告诉你们就好)
4. 加了 Mask(蒙版)是部分缕空
5. 内外的颜色不一样
SO:
1. 我们需要在SubShader语块中设置他的Cull
为off
,来使材质里外都显示。
属性名称 | 参数 | 作用 |
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中所定义的变量的话,必须使用和之前变量相同的名字进行声明。。
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;
}
加上 法线贴图和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"
}
我是李本心明