深入URP之Shader篇7: SimpleLit Shader分析(3)
创始人
2024-03-24 01:47:15
0

Simple Lit Forward Pass

Fragment shader 函数

上篇中分析了Simple Lit Forward Pass的Vertex Shader都计算和输出了什么数据,本篇就看看在Fragment shader中是怎么使用这些数据计算最终的光照颜色的。
由于代码较短,就都贴出来方便查看:

half4 LitPassFragmentSimple(Varyings input) : SV_Target
{UNITY_SETUP_INSTANCE_ID(input);UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);float2 uv = input.uv;half4 diffuseAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));half3 diffuse = diffuseAlpha.rgb * _BaseColor.rgb;half alpha = diffuseAlpha.a * _BaseColor.a;AlphaDiscard(alpha, _Cutoff);#ifdef _ALPHAPREMULTIPLY_ONdiffuse *= alpha;#endifhalf3 normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap));half3 emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));half4 specular = SampleSpecularSmoothness(uv, alpha, _SpecColor, TEXTURE2D_ARGS(_SpecGlossMap, sampler_SpecGlossMap));half smoothness = specular.a;InputData inputData;InitializeInputData(input, normalTS, inputData);half4 color = UniversalFragmentBlinnPhong(inputData, diffuse, specular, smoothness, emission, alpha);color.rgb = MixFog(color.rgb, inputData.fogCoord);color.a = OutputAlpha(color.a, _Surface);return color;
}

首先进行的是Alpha Test

之前在分析Depth Only Pass的时候就看过alpha test的处理,和这儿有些类似,但是由于Depth only pass更加通用,所以会在使用albedo贴图alpha的时候做一些判断,在某些情况下这个贴图的alpha是不用来做alpha test的(贴图的alpha通道存储的是smoothness, glossiness这样的值),因此就不使用它作为alpha test的参数。而Simple Lit这儿就简单了,因为这个shader里面不使用那些关键字对应的功能,因此也无需判断那些关键字。
注意这儿采样贴图使用的函数名是SampleAlbedoAlpha,这个函数之前已经看过,就是使用SAMPLE_TEXTURE2D采样一张2D贴图,采样出来的结果是一个half4。你可以理解为这个half4包含了rgb反射率(albedo)和alpha。即不是简单的将贴图中的texel看成是一个颜色。diffuseAlpha.rgb_BaseColor.rgb相乘作为最终的diffuse系数(其实仍然是反射率),而diffuseAlpha.a_BaseColor.a相乘得到最终的alpha,使用AlphaDiscard函数进行Alpha Test。

void AlphaDiscard(real alpha, real cutoff, real offset = 0.0h)
{#ifdef _ALPHATEST_ONclip(alpha - cutoff + offset);#endif
}

最终调用的是hlsl的clip函数,clip函数当接受的参数值小于0时丢弃这个片段。这儿的offset一般都不会设置,就是0,可以忽略,而alpha和cutoff比较,如果alpha < cutoff则片段没通过alpha test被丢弃。所以cutoff设置的是可通过alpha test的最小的alpha值,大于或等于cutoff的alpha值是通过alpha test的。

预乘alpha

还记得一开始我们看unlit shader的属性时说的BaseShaderGUI吗?预乘alpha开启的条件其实就是blend mode为Premultiply:

                    case BlendMode.Premultiply:material.SetInt("_SrcBlend", (int) UnityEngine.Rendering.BlendMode.One);material.SetInt("_DstBlend", (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);material.EnableKeyword("_ALPHAPREMULTIPLY_ON");

此时,srcBlend为one,而dstBlend为1-srcAlpha,即blend的公式为:

finalColor = srcColor + (1-srcAlpha)*dstColor

和标准的alpha blend模式相比,srcColor在混合时不需要再乘以alpha,因为alpha已经预先乘上去了:

#ifdef _ALPHAPREMULTIPLY_ONdiffuse *= alpha;
#endif

这两种做法有何区别?很显然,这儿只有diffuse使用了alpha,而如果在blend时使用alpha是对最终颜色乘以alpha,所以这种做法得到的颜色应该更亮一些。因为可以认为乘alpha是减淡颜色。再多说一句预乘alpha这个技术,早期经常用在2d游戏渲染半透明sprite,在载入sprite时即预乘alpha(或是在导入资源时处理sprite图片),这样在渲染时就可以少一个乘法,性能提高了而效果是一样的,当然了这种做法时alpha值是不能变的,不能做fade in/fade out。

采样法线贴图

half3 normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap));

SampleNormal函数如下:

half3 SampleNormal(float2 uv, TEXTURE2D_PARAM(bumpMap, sampler_bumpMap), half scale = 1.0h)
{
#ifdef _NORMALMAPhalf4 n = SAMPLE_TEXTURE2D(bumpMap, sampler_bumpMap, uv);#if BUMP_SCALE_NOT_SUPPORTEDreturn UnpackNormal(n);#elsereturn UnpackNormalScale(n, scale);#endif
#elsereturn half3(0.0h, 0.0h, 1.0h);
#endif
}

如果定义了相应的关键字,则采样法线贴图,且根据是否使用了bump scale,使用不同的解码函数。这两个unpcak函数在SRP Core的Packing.hlsl中,涉及到法线贴图相关的编码方式,这儿不细说,也许会有一篇单独分析法线贴图会说。一般来说,法线贴图会保存切线空间的法线,因此这儿的变量名为normalTS

采样自发光贴图

half3 emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
half3 SampleEmission(float2 uv, half3 emissionColor, TEXTURE2D_PARAM(emissionMap, sampler_emissionMap))
{
#ifndef _EMISSIONreturn 0;
#elsereturn SAMPLE_TEXTURE2D(emissionMap, sampler_emissionMap, uv).rgb * emissionColor;
#endif
}

就是采样一张自发光贴图再乘以一个自发光颜色。

采样高光贴图以及获取smoothness

half4 specular = SampleSpecularSmoothness(uv, alpha, _SpecColor, TEXTURE2D_ARGS(_SpecGlossMap, sampler_SpecGlossMap));
half smoothness = specular.a;

SampleSpecularSmoothness函数在SimpleLitInput.hlsl中:

TEXTURE2D(_SpecGlossMap);       SAMPLER(sampler_SpecGlossMap);half4 SampleSpecularSmoothness(half2 uv, half alpha, half4 specColor, TEXTURE2D_PARAM(specMap, sampler_specMap))
{half4 specularSmoothness = half4(0.0h, 0.0h, 0.0h, 1.0h);
#ifdef _SPECGLOSSMAPspecularSmoothness = SAMPLE_TEXTURE2D(specMap, sampler_specMap, uv) * specColor;
#elif defined(_SPECULAR_COLOR)specularSmoothness = specColor;
#endif#ifdef _GLOSSINESS_FROM_BASE_ALPHAspecularSmoothness.a = exp2(10 * alpha + 1);
#elsespecularSmoothness.a = exp2(10 * specularSmoothness.a + 1);
#endifreturn specularSmoothness;
}

根据不同的关键字,从高光贴图中采样出高光颜色或者直接使用材质定义的高光色。注意这儿的_GLOSSINESS_FROM_BASE_ALPHA,前面alpha test那儿说过,这个关键字表示base贴图的alpha存储的是glossiness,否则就使用高光贴图的alpha作为glossiness使用。这儿使用exp2解码smoothness,说明贴图中存储的是log空间。

待续

后面还有两篇讲这个Fragment Shader,这个Shader虽然不复杂,光照计算也只是传统的BlinnPhong,但是涉及到非常多的Unity光照系统的东西以及URP的惯例用法,所以内容较多,咱们慢慢来。

相关内容

热门资讯

屋有蜜蜂来筑巢好不好 屋有蜜蜂来筑巢好不好当然好了察祥蔽呀,蜜蜂也是风水专家,说明你家位置很好。如果不影响你们正常生活的话...
哈尔滨空调股份有限公司2025... 证券代码:600202 证券简称:哈空调 编号:临2025-036哈尔滨空调股份有限公司2025年半...
促政策与市场双轨共振,激活产业... “十四五”规划的深化布局,为中国电子信息产业绘制了波澜壮阔的发展蓝图。作为国民经济的战略性支柱产业,...
我是做电话销售的,公司安排每人... 我是做电话销售的,公司安排每人每天轮流开一次早会,谁可以给些简单的小游戏。小故事、小话术提供一下!轰...
从世运手包到童趣闯关 世运火花... 转自:成都日报锦观从世运手包到童趣闯关 世运火花在社区点燃 本报讯 (成都日报锦观新闻记者 袁...
工地开讲“护薪”课,筑牢农民工... 为切实保障农民工合法权益,7月6日,中铁上海局七公司引汉济渭项目部联合监理单位及其他标段,共同组织开...
温江区万春镇:轨道为笔 绘就公... 转自:成都日报锦观温江区万春镇:轨道为笔 绘就公园城市乡村振兴新图景 在成都建设公园城市示范...
公开通报3起整治形式主义为基层... 转自:成都日报锦观中央层面整治形式主义为基层减负专项工作机制办公室 中央纪委办公厅公开通报3起整治形...
今起三日 成都持续晴热 转自:成都日报锦观今起三日 成都持续晴热 7月20日将进入今年的“三伏天”,但在入伏前,四川...
中国石化胜利石油工程公司:以创... 在保障国家能源安全的征程中,中国石化胜利石油工程公司以一组组亮眼的数据,交出了一份沉甸甸的“半年答卷...