【Unity3D Shader编程】之十一 深入理解Unity5中的Standard Shader 三 屏幕像素化特效

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                 

 本系列文章由 @浅墨_毛星云 出品,转载请注明出处。   
  文章链接:  http://blog.csdn.net/poem_qianmo/article/details/50095705
 作者:毛星云(浅墨)    微博: http://weibo.com/u/1723155442  
  本文工程使用的Unity3D版本:   5.2.1 


 
概要:续接上文,本文进一步讲解与分析了上文未讲完的Unity5中Standard Shader正向基础渲染通道源码的片段着色实现部分,以及对屏幕像素化后期特效进行了实现。
 
同样需要声明的是。本文中对Stardard Shader源码的一些分析,全是浅墨自己通过对Unity Shader内建源码的理解,以及Google之后理解与分析而来。如有解释不妥当之处,还请各位及时指出。
 

依然是附上一组本文配套工程的运行截图之后,便开始我们的正文。


傍晚的野外(with 屏幕像素化特效):
 


傍晚的野外(原始场景):



图依然是贴这两张。文章末尾有更多的运行截图,并提供了源工程的下载。先放出可运行的exe下载,如下:

【可运行的本文配套exe游戏场景请点击这里下载】
 
提示:在此游戏场景中按F键可以开关屏幕特效。


 

 
一、关于BRDF(双向反射分布函数)
 

本次源码剖析有涉及BRDF的相关内容,这边简单提一下。
双向反射分布函数(Bidirectional ReflectanceDistribution Function,BRDF)用来定义给定入射方向上的辐射照度(irradiance)如何影响给定出射方向上的辐射率(radiance)。更笼统地说,它描述了入射光线经过某个表面反射后如何在各个出射方向上分布——这可以是从理想镜面反射到漫反射、各向同性(isotropic)或者各向异性(anisotropic)的各种反射。
 
BRDF作为图形学中比较常见的一个知识点,这边暂时不多讲,因为随便拿一本图形学相关的书都可以看到他的身影。这边给出一些参考的链接,大家有需要可以深入了解:
 
1. 如何正确理解 BRDF (双向反射分布函数)? - 计算机 - 知乎
2.图形学理论知识:BRDF 双向反射分布函数
3. An Introduction to BRDF-based Lighting –Nvidia

 



 

二、续Standard Shader中正向基础渲染通道源码分析


 


此部分接上文《【浅墨Unity3D Shader编程】之十 深入理解Unity5中的Standard Shader(二)&屏幕油画特效的实现》的第二部分“Standard Shader中正向基础渲染通道源码分析“。

上文中分析了Standard Shader中正向基础渲染通道的源码,刚好分析完了顶点着色函数vertForwardBase,本文中将对片段着色函数fragForwardBase 进行说明。分析完之后,也就结束了这一系列长得稍微有些离谱的Standard Shader正向基础渲染通道的源码分析。
 

OK,开始吧,先上注释好的片段着色函数fragForwardBase的代码,位于UnityStandardCore.cginc中:

//----------------------------------------【fragForwardBase函数】-------------------------------------------//  用途:正向渲染基础通道的片段着色函数//  输入:VertexOutputForwardBase结构体//  输出:一个half4类型的颜色值//------------------------------------------------------------------------------------------------------------------half4 fragForwardBase (VertexOutputForwardBase i) : SV_Target//定义并初始化类型为FragmentCommonData的变量s FRAGMENT_SETUP(s) //若定义了UNITY_OPTIMIZE_TEXCUBELOD,则由输入的顶点参数来设置反射光方向向量#if UNITY_OPTIMIZE_TEXCUBELOD s.reflUVW  = i.reflUVW;#endif //设置主光照 UnityLight mainLight = MainLight (s.normalWorld); //设置阴影的衰减系数 half atten = SHADOW_ATTENUATION(i); //计算全局光照 half occlusion = Occlusion(i.tex.xy); UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight); //加上BRDF-基于物理的光照 half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.oneMinusRoughness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect); //加上BRDF-全局光照 c.rgb += UNITY_BRDF_GI (s.diffColor, s.specColor, s.oneMinusReflectivity, s.oneMinusRoughness, s.normalWorld, -s.eyeVec, occlusion, gi); //加上自发光 c.rgb += Emission(i.tex.xy); //设置雾效 UNITY_APPLY_FOG(i.fogCoord, c.rgb); //返回最终的颜色 return OutputForward (c, s.alpha);}
依然是老规矩,把上面代码中新接触到的相关内容进行下分条讲解。





1. FRAGMENT_SETUP(x)宏

FRAGMENT_SETUP(x)宏定义于UnityStandardCore.cginc头文件中,其作用其实就是用FragmentSetup函数初始化括号中的x变量。
#define FRAGMENT_SETUP(x) FragmentCommonData x = \ FragmentSetup(i.tex, i.eyeVec, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndParallax, IN_WORLDPOS(i));

调用此宏,也就是表示写了如下的代码,定义了一个x变量:
FragmentCommonData x =FragmentSetup(i.tex, i.eyeVec, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndParallax, IN_WORLDPOS(i));


其中FragmentSetup函数也定义于UnityStandardCore.cginc头文件中,用于填充一个FragmentCommonData结构体并于返回值中返回,也就是进行片段函数相关参数的初始化,相关代码如下:

//函数FragmentSetup:填充一个FragmentCommonData结构体并于返回值中返回,进行片段函数相关参数的初始化inline FragmentCommonData FragmentSetup (float4 i_tex, half3 i_eyeVec, half3 i_viewDirForParallax, half4 tangentToWorld[3], half3 i_posWorld){ i_tex = Parallax(i_tex, i_viewDirForParallax); half alpha = Alpha(i_tex.xy); #if defined(_ALPHATEST_ON)  clip (alpha - _Cutoff); #endif FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex); o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld); o.eyeVec = NormalizePerPixelNormal(i_eyeVec); o.posWorld = i_posWorld; // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha) o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha); return o;}
其中的FragmentCommonData结构体也是定义于UnityStandardCore.cginc头文件中:
//FragmentCommonData结构体:存放片段着色常用变量struct FragmentCommonData{
     half3 diffColor, specColor;//漫反射颜色;镜面反射颜色 // Note: oneMinusRoughness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level. // Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots. half oneMinusReflectivity, oneMinusRoughness;//1减去反射率;1减去粗糙度 half3 normalWorld, eyeVec, posWorld;//世界空间中的法线向量坐标;视角向量坐标;在世界坐标中的位置坐标 half alpha;//透明度#if UNITY_OPTIMIZE_TEXCUBELOD || UNITY_STANDARD_SIMPLE half3 reflUVW;//反射率的UVW#endif#if UNITY_STANDARD_SIMPLE half3 tangentSpaceNormal;//切线空间中的法线向量#endif};



 

2. MainLight函数



MainLight函数定义于UnityStandardCore.cginc头文件中,用途是实例化一个UnityLight结构体对象,并进行相应的填充,其返回值作为主光源。相关代码如下:

//  用途:该函数为主光照函数//  说明:实例化一个UnityLight结构体对象,并进行相应的填充/*//注:UnityLight结构体定义于UnityLightingCommon.cginc文件中,原型如下:struct UnityLight{half3 color;half3 dir;half  ndotl;};*///------------------------------------【函数3】MainLight函数-----------------------------------------//  用途:该函数为主光照函数//  说明:实例化一个UnityLight结构体对象,并进行相应的填充//---------------------------------------------------------------------------------------------------------UnityLight MainLight (half3 normalWorld)//【1】实例化一个UnityLight的对象 UnityLight l; //【2】填充UnityLight的各个参数 //若光照贴图选项为关,使用Unity内置变量赋值 #ifdef LIGHTMAP_OFF  //获取光源的颜色  l.color = _LightColor0.rgb;   //获取光源的方向  l.dir = _WorldSpaceLightPos0.xyz;  //获取法线与光源方向的点乘的积  l.ndotl = LambertTerm (normalWorld, l.dir); //光照贴图选项为开,将各项值设为0 #else  l.color = half3(0.f, 0.f, 0.f);  l.ndotl  = 0.f;  l.dir = half3(0.f, 0.f, 0.f); #endif //返回赋值完成的UnityLight结构体对象 return l;}


 

3. SHADOW_ATTENUATION宏



SHADOW_ATTENUATION宏相关的代码位于AutoLight.cginc头文件中,用于实现阴影渲染相关的辅助工作,代码如下:

// ----------------//  阴影相关工具代码 || Shadow helpers// ----------------// ---- 屏幕空间阴影 || Screen space shadows#if defined (SHADOWS_SCREEN)……#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)#endif// ----聚光灯光源阴影 || Spot light shadows#if defined (SHADOWS_DEPTH) && defined (SPOT) #define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1; #define TRANSFER_SHADOW(a) a._ShadowCoord = mul (unity_World2Shadow[0], mul(_Object2World,v.vertex)); #define SHADOW_ATTENUATION(a) UnitySampleShadowmap(a._ShadowCoord)#endif// ----点光源阴影 ||  Point light shadows#if defined (SHADOWS_CUBE) #define SHADOW_COORDS(idx1) unityShadowCoord3 _ShadowCoord : TEXCOORD##idx1; #define TRANSFER_SHADOW(a) a._ShadowCoord = mul(_Object2World, v.vertex).xyz - _LightPositionRange.xyz; #define SHADOW_ATTENUATION(a) UnitySampleShadowmap(a._ShadowCoord)#endif// ---- 关闭阴影 || Shadows off#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE) #define SHADOW_COORDS(idx1) #define TRANSFER_SHADOW(a) #define SHADOW_ATTENUATION(a) 1.0#
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值