源自Mobile-Lightmap-Unlit
这个shader算是挺长的了,一共3个pass。
先来看一下第一个pass。
OMG,是fixed pipeline。。。忽略吧,我们只看一下它的tags
Tags
{
"LightMode"
=
"Vertex"
}
这个tags什么意思呢。lightmode为vertex,意思就是说,如果相机的Rendering Path选择了LegacyVertexLit,就会启用这一个pass,而忽略后面那两个pass。启用这个pass还有一个附件条件就是说,应用这个shader的物体没有lightmap时才会启用,并将计算所有的vertexlight。
再来看一下第二个pass
// Lightmapped, encoded as dLDR
Tags
{
"LightMode"
=
"VertexLM"
}
如果相机的rendering path是LegacyVertexLit的话,并且有lightmap是启用这个pass,忽略其余两个pass。注意一下他的注释----encoded as dLDR 就是说在移动端是lightmap以double ldr去解码(这个注释告诉我们说,移动端的lightmap会与pc端的不同,如果要修改的话,就自己写vf,然后修改一下sample lightmap的函数)
留意一下,sample lightmap的uv是模型的第二套uv。
最后看一下最后一个pass
#define
USING_FOG
(
defined
(FOG_LINEAR) ||
defined
(FOG_EXP) ||
defined
(FOG_EXP2))
定义了一个宏USING_FOG 值为后面一坨。FOG_LINEAR FOG_EXP FOG_EXP2的定义在unitycg.cginc里
这个宏的使用在v2f和vert以及frag里。这样就想当于这个pass会有4个变体,没有启用雾效,linear , exp exp2一共4个变体
有个using_fog这个宏,也在struct和vf里用于标记fog的运算,这样如果没有开启雾效就可以不做fog部分的运算。
先来看一下vert部分的
这样有个不解的地方就是
half4
color =
half4
(
0
,
0
,
0
,
1.1
);
o.color =
saturate
(color);
官方的这波操作不太理解。
继续。
float3
eyePos =
UnityObjectToViewPos
(
float4
(IN.pos,
1
));
#if
USING_FOG
float
fogCoord =
length
(eyePos.xyz);
// radial fog distance
UNITY_CALC_FOG_FACTOR_RAW
(fogCoord);
o.fog =
saturate
(unityFogFactor);
#endif
查看一下UnityObjectToViewPos这个函数的定义
// Tranforms position from object to camera space
inline
float3
UnityObjectToViewPos
(
in
float3
pos )
{
return
mul
(
UNITY_MATRIX_V
,
mul
(
unity_ObjectToWorld
,
float4
(pos,
1.0
))).xyz;
}
inline
float3
UnityObjectToViewPos
(
float4
pos)
// overload for float4; avoids "implicit truncation" warning for existing shaders
{
return
UnityObjectToViewPos
(pos.xyz);
}
他的简略写法就是
float3 eyePos = mul(UNITY_MATRIX_MV,float4(IN.pos,1.0)).xyz;
然后unity给我抛出一个warning
Shader warning in 'ShaderStore/UnitShader2017/Mobile/Unlit (Supports Lightmap)': Use of UNITY_MATRIX_MV is detected. To transform a vertex into view space, consider using UnityObjectToViewPos for better performance.
大意就是:我们检测到shader中使用了UNITY_MATRIX_MV,您就是想把vertex从objectspace转到viewspace里,建议您使用UnityObjectToViewPos函数,以便带来跟好的性能。
unity以为他提了一下性能这两个字,我就会信了。。
在UnityShaderVariables.cginc里查到
#define
UNITY_MATRIX_V
unity_MatrixV
#define
UNITY_MATRIX_MV
mul
(unity_MatrixV,
unity_ObjectToWorld
)
我用UNITYI_MATRIX_MV这个矩阵,unity会先做MV矩阵的乘法,这样,这一步一共有(4个乘法和3个加法)*16=64个乘法和48个加法运算。然后在与float4(IN.pos,1.0)相乘,这一步一共有(4个乘法和3个加法)*4=16个乘法和12个加法运算,这样下来,
我这里简单的一行float3 eyePos = mul(UNITY_MATRIX_MV,float4(IN.pos,1.0)).xyz;带来的运算次数是80次乘法和60次加法。
然后官方的函数中,mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, float4(pos, 1.0))),的
运算次数是4个乘法和3个加法)*4 *2=32个乘法和24个加法
运算减少了一半以上,(unity:快叫爸爸! “爸爸”)
再来看一下UNITY_CALC_FOG_FACTOR_RAW这个函数
// x = density / sqrt(ln(2)), useful for Exp2 mode
// y = density / ln(2), useful for Exp mode
// z = -1/(end-start), useful for Linear mode
// w = end/(end-start), useful for Linear mode
float4
unity_FogParams
;
#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
根据unity_fogparams去看FOG_LINEAR就很容易理解了。
补充一点
float fogCoord= length(_WorldSpaceCameraPos - mul(unity_ObjectToWorld,float4(IN.pos,1.0)).xyz);
岂不是更好?!
雾就这么草草结束了
最后看一下frag里的DecodeLightmap这个函数
// Decodes HDR textures
// handles dLDR, RGBM formats
// Called by DecodeLightmap when UNITY_NO_RGBM is not defined.
inline
half3
DecodeLightmapRGBM
(
half4
data,
half4
decodeInstructions)
{
// If Linear mode is not supported we can skip exponent part
#if
defined
(UNITY_COLORSPACE_GAMMA)
#
if
defined
(UNITY_FORCE_LINEAR_READ_FOR_RGBM)
return
(decodeInstructions.x * data.a) *
sqrt
(data.rgb);
#
else
return
(decodeInstructions.x * data.a) * data.rgb;
#
endif
#else
return
(decodeInstructions.x *
pow
(data.a, decodeInstructions.y)) * data.rgb;
#endif
}
// Decodes doubleLDR encoded lightmaps.
inline
half3
DecodeLightmapDoubleLDR
(
fixed4
color )
{
return
2.0
* color.rgb;
}
inline
half3
DecodeLightmap
(
fixed4
color,
half4
decodeInstructions)
{
#if
defined
(
UNITY_NO_RGBM
)
return
DecodeLightmapDoubleLDR
( color );
#else
return
DecodeLightmapRGBM
( color, decodeInstructions );
#endif
}
half4
unity_Lightmap_HDR;
inline
half3
DecodeLightmap
(
fixed4
color )
{
return
DecodeLightmap
( color, unity_Lightmap_HDR );
}
我把他拆成两个部分,根据UNITY_NO_RGBM
第一个部分
// Decodes doubleLDR encoded lightmaps.
inline
half3
DecodeLightmapDoubleLDR
(
fixed4
color )
{
return
2.0
* color.rgb;
}
inline
half3
DecodeLightmap
(
fixed4
color,
half4
decodeInstructions)
{
return
DecodeLightmapDoubleLDR
( color );
}
half4
unity_Lightmap_HDR;
inline
half3
DecodeLightmap
(
fixed4
color )
{
return
DecodeLightmap
( color, unity_Lightmap_HDR );
}
关于unity_Lightmap_HDR里存的什么,unity并没有告诉我们。。
这就尴尬了。没法独秀了。。。
我又转念一想。。。下载个最新的shader builtin看看有没有新发现。。。(builtin_shaders-2018.1.6f1)
// Decodes HDR textures
// handles dLDR, RGBM formats
// Called by DecodeLightmap when UNITY_NO_RGBM is not defined.
inline
half3
DecodeLightmapRGBM
(
half4
data,
half4
decodeInstructions)
{
// If Linear mode is not supported we can skip exponent part
#if
defined
(UNITY_COLORSPACE_GAMMA)
#
if
defined
(UNITY_FORCE_LINEAR_READ_FOR_RGBM)
return
(decodeInstructions.x * data.a) *
sqrt
(data.rgb);
#
else
return
(decodeInstructions.x * data.a) * data.rgb;
#
endif
#else
return
(decodeInstructions.x *
pow
(data.a, decodeInstructions.y)) * data.rgb;
#endif
}
// Decodes doubleLDR encoded lightmaps.
inline
half3
DecodeLightmapDoubleLDR
(
fixed4
color,
half4
decodeInstructions)
{
// decodeInstructions.x contains 2.0 when gamma color space is used or pow(2.0, 2.2) = 4.59 when linear color space is used on mobile platforms
return
decodeInstructions.x * color.rgb;
}
inline
half3
DecodeLightmap
(
fixed4
color,
half4
decodeInstructions)
{
#if
defined
(UNITY_LIGHTMAP_DLDR_ENCODING)
return
DecodeLightmapDoubleLDR
(color, decodeInstructions);
#elif
defined
(UNITY_LIGHTMAP_RGBM_ENCODING)
return
DecodeLightmapRGBM
(color, decodeInstructions);
#else
//defined(UNITY_LIGHTMAP_FULL_HDR)
return
color.rgb;
#endif
}
half4
unity_Lightmap_HDR;
inline
half3
DecodeLightmap
(
fixed4
color )
{
return
DecodeLightmap
( color, unity_Lightmap_HDR );
}
我们直接看DecodeLightMap这个函数,
如果没有定义UNITY_LIGHTMAP_DLDR_ENCODING和UNITY_LIGHTMAP_RGBM_ENCODING,就是说相当于定义了UNITY_LIGHTMAP_FULL_HDR,那么就直接返回color就好了。
UNITY_LIGHTMAP_DLDR_ENCODING:
先看 DecodeLightmapDoubleLDR()这个函数下的注释:
// decodeInstructions.x contains 2.0 when gamma color space is used or
pow(2.0, 2.2) = 4.59 when linear color space is used on mobile platforms
decodeInstructions是形参,unity_Lightmap_HDR是实参。
在移动端上,如果是gamma颜色空间的话,unity_Lightmap_HDR.x=2;
如果是linear颜色空间的话,unity_Lightmap_HDR.x=4.59(pow(2.0,2.2));
也就是说unity_Lightmap_HDR这个float4值,在移动端只有x分量有关系(这里只是从lightmap 的decode来看,这个变量是否在其他方面使用并未查看)
看一下官网的这个说明
就是说除了移动端,其他平台都是RGBM或者HDR编码的。
那就继续源码里的
DecodeLightmapRGBM
inline half3 DecodeLightmapRGBM (half4 data, half4 decodeInstructions)
{
// If Linear mode is not supported we can skip exponent part
#if defined(UNITY_COLORSPACE_GAMMA)
# if defined(UNITY_FORCE_LINEAR_READ_FOR_RGBM)
return (decodeInstructions.x * data.a) * sqrt(data.rgb);
# else
return (decodeInstructions.x * data.a) * data.rgb;
# endif
#else
return (decodeInstructions.x * pow(data.a, decodeInstructions.y)) * data.rgb;
#endif
}
上面那张图里可以看到
RAGM encoding那一块说了,在linear空间下rgbm的范围是(0-34.49)------pow(5,2.2)
在gamma空间下的范围是(0-5)
这篇博文说的是,unity_Lightmap_HDR的x分量存的是最大范围,y分量存储的是gamma矫正的值
也就是说
linear空间unity_Lightmap_HDR=float4(34.49,2.2,0,0)
gamma空间unity_Lightmap_HDR=float4(5.,1.,0,0)
这个就要去研究一下RGBM的编码规则了。
于是开始了google和bing和baidu之路。
我们先看一下RGM的Encode的过程。
1.定义一个最大范围Max,那么整个颜色的范围就是(0-Max)
2.颜色rgb分量的最大值为rgbMax。
3.rgbMax与Max的商就是M
4.取大于M*255的最小整数与255相除的商赋值给M
5.最后把颜色的各个分量与M和Max的乘积做除法,得到新的rgb分量,然后M做a通道的分量值
用码表示就是
float Max= 8;
float4 EncodeRGBM(float3 rgb)
{
float rgbMax= max(rgb.x,max(rgb.g,rgb.b));
float M = rgbMax/ Max;
M = ceil(M * 255.0) / 255.0;
return float4(rgb / (M * Max), M);
}
Decode就很简单了
假设rgba = Encode(rgb);
那么Decode的返回值就是rgb*a*Max就好了
然后回看unity的shader源码
return (decodeInstructions.x * data.a) * data.rgb;
这个就是decode了,但是
return (decodeInstructions.x * data.a) * sqrt(data.rgb);
和
return (decodeInstructions.x * pow(data.a, decodeInstructions.y)) * data.rgb;
的decode方式没有看懂。。。。
希望有人能解释一下这个玄学背后的原理。多谢。。