UnityShader源码2017---学习笔记与自我拓展027

源自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_ENCODINGUNITY_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方式没有看懂。。。。

希望有人能解释一下这个玄学背后的原理。多谢。。




猜你喜欢

转载自blog.csdn.net/u012871784/article/details/80885599
今日推荐