1.从Unlit-Color开始入手

Shader 源码-Unlit-Color

需要有一定的shader基础,深刻理解unity 内置shader的实现原里。

先贴代码,逐步分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

// Unlit shader. Simplest possible colored shader.
// - no lighting
// - no lightmap support
// - no texture

Shader "Unlit/Color"
{
Properties
{
_Color ("Main Color", Color) = (1, 1, 1, 1)
}

SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100

Pass
{
CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog

#include "UnityCG.cginc"

struct appdata_t
{
float4 vertex: POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct v2f
{
float4 vertex: SV_POSITION;
UNITY_FOG_COORDS(0)
UNITY_VERTEX_OUTPUT_STEREO
};

fixed4 _Color;

v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
UNITY_TRANSFER_FOG(o, o.vertex);
return o;
}

fixed4 frag(v2f i): SV_Target
{
fixed4 col = _Color;
UNITY_APPLY_FOG(i.fogCoord, col);
UNITY_OPAQUE_ALPHA(col.a);
return col;
}
ENDCG

}
}
}

#pragma multi_compile_fog

实现很简单,重点点记录

  • #pragma multi_compile_fog
  • UNITY_VERTEX_INPUT_INSTANCE_ID
  • UNITY_FOG_COORDS(0)
  • UNITY_VERTEX_OUTPUT_STEREO
  • UNITY_SETUP_INSTANCE_ID(v)
  • UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o)
  • UNITY_TRANSFER_FOG(o, o.vertex)
  • UNITY_APPLY_FOG(i.fogCoord, col)
  • UNITY_OPAQUE_ALPHA(col.a)

这些在一个基础的顶点片元着色器的基本原里。

基础概念宏定义

1
2
3
4
5
6
7
8
9
10
#define 标识  //定义标识,如果 (标识) 已经被标识过会报错。
#undef 标识 //取消标识的定义
#ifdef 标识 //判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 标示 // 判断"标示"是否定义,如果被定义则返回false,如果没有被定义则返回true

#if //编译预处理中的条件命令,相当于C语法中的if语句
#elif // 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else //与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif //#if, #ifdef, #ifndef这些条件命令的结束标志.
defined(宏) 与#if, #elif配合使用,判断某个宏是否被定义

#include “UnityCG.cginc”

这里需要注意下,include并不是像c++ 这样在调用shader时将文件include进去,而是在编译shader时决定生成的代码。可以测试在去掉#pragma multi_compile_fog前后点击shader面板 Compile and show code 查看编译后的代码。

追到UnityCG.cginc文件中查看UNITY_FOG_COORDS宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
#define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)

#if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
// mobile or SM2.0: calculate fog factor per-vertex
#define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.tSpace1.y = tangentSign; o.tSpace2.y = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.worldPos.w = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.eyeVec.w = unityFogFactor
#else
// SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
#define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) o.tSpace2.y = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) o.worldPos.w = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) o.eyeVec.w = (outpos).z
#endif
#else
#define UNITY_FOG_COORDS(idx)
#define UNITY_TRANSFER_FOG(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos)
#endif

当开启Fog时

1
#define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)

UNITY_FOG_COORDS_PACKED

1
#define UNITY_FOG_COORDS_PACKED(idx, vectype) vectype fogCoord : TEXCOORD##idx;

可知实际 UNITY_FOG_COORDS(0) 如果Fog开启,相当一在片元数据中定义了

1
float1 fogCoord : TEXCOORD0

UNITY_TRANSFER_FOG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
#define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)

#if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)
// mobile or SM2.0: calculate fog factor per-vertex
#define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.tSpace1.y = tangentSign; o.tSpace2.y = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.worldPos.w = unityFogFactor
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.eyeVec.w = unityFogFactor
#else
// SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
#define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) o.tSpace2.y = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) o.worldPos.w = (outpos).z
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) o.eyeVec.w = (outpos).z
#endif
#else
#define UNITY_FOG_COORDS(idx)
#define UNITY_TRANSFER_FOG(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos)
#define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos)
#endif

因为 #pragma target 2.0 时 SHADER_TARGET <30 所以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#if defined(FOG_LINEAR)
// factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w
#elif defined(FOG_EXP)
// factor = exp(-density*z)
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor)
#elif defined(FOG_EXP2)
// factor = exp(-(density*z)^2)
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor)
#else
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0
#endif


#define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(UNITY_Z_0_FAR_FROM_CLIPSPACE(coord))

#define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor

可以追溯宏定义的运算 得到 fogCoord值,并在 UNITY_APPLY_FOG(i.fogCoord, col);得到新得col值。

备注

INSTANCE 和FOG 会在后面补充,这里只记录shader分析的一个过程。