从一个shader剖析unity混合光照和pbr
2018-01-31 09:44
555 查看
从unity5.6自带的代码来看。
下面是standard.shader的面板
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/413153e59d13ab5c26d906438301f942)
shader结构图:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/ebee2bfcc04851eaafd77e08bd4634b1)
subShader的区别,先看两个pass的区别:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/c5d90c40828964c375fc872472243cc7)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/a685a90cc16da4f5b57dd1f736e52e48)
然后我们先看第一个forward pass,“UnityStandardCoreForward.cginc”里查看,vertBase和fragBase又根据是否定义simple而执行不同的分支:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/6a6673bc90f79a0fd3f234d57e202395)
定义了simple的分支,数据结构和函数,我们去“UnityStandardCoreForwardSimple.cginc”文件里查看,vert函数的输入结构定义:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/d5809513802770be0e8e08337e42ec77)
vert函数输出结构定义
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/d66156b92c7cbd4bca35a4344248510e)
vert函数定义
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/5964adf872b69eb9051788b0c5dcdff1)
里面涉及的函数,获取切线空间光照和view方向的函数
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/a5f39d7b4c31ddccad19ced3649bdc4b)
获取lightmap或环境光的uv的函数:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/696031172cd7f100fd01093caad51455)
grazing参数的获取:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/42d45640b8d19e30f1dae5f1de590b17)
再看fragment函数fragForwardBaseSimpleInternal,在UnityStandardCoreForwardSimple.cginc文件中:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/1742db6a72e3143d31890f089dfda03a)
里面第一句又从输入的结构得到FragmentCommonData,函数如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/0a2c00de417a098c7ba53bf256e51706)
fragmentCommonData结构如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/6c2980acfcb2d08426616916ed1afc79)
与前面的结构比较:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/54f4e9d4b1beb40613076d0e650ca0b2)
转换函数FragmentSetupSimple也在SimpleForward这个文件里
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/be815e176645ff968d3618c5738a7bfe)
其中的UNITY_SETUP_BRDF_INPUT函数(UnityStandardCore.cginc中)定义如下,这个函数里获取FragmentCommonData的difColor,SpecColor,oneMinusReflectivity和smoothness。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/86be390ac3e0212ea97e3a88340202d6)
其中a通过Alpha函数得到,如果定义了_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A则通过_color.a得到,否则:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/e0ff244798632315ff2063f204c361ab)
。该宏的定义通过editor得到,在builtin_shaders-5.6\Editor\StandardShaderGUI.cs文件中:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/83c11d522432c36dfca72a22189d41b2)
其中smoothness通过SpecularGloss函数得到(在UnityStandardInput.cginc中定义),如果有speculargloss贴图,则对贴图采样,如果用主贴图的a通道存储gloss值,则sg的a通道通过采样主贴图得到,如果a通道在specularGLoss贴图,则通过采样specGLoss贴图得到。如果没有定义高光贴图,则高光颜色通过_SpecColor得到,如果gloss在主贴图的a通道,则采样主贴图获取gloss,否则,gloss通过_Glossiness得到:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/ac99f84913a2448e66a62bd8ec31793d)
diffColor,SpecColor,oneMinusReflectivity又通过EnergyConservationBetweenDiffuseAndSpecular得到:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/d47f6575b51a622f0d6c13696d8621ef)
diffColor用preMultiplyAlpha函数,里面将blend模式加入:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/c8667a74473ea8616f6ad92afb4d75d5)
看完FragmentCommonData后,我们再继续看fragForwardBaseSimpleInternal函数,mainLight包含颜色和方向,nDotL,然后是shadowMask衰减,实时阴影衰减并将两个衰减混合。从遮挡贴图采样得到遮挡因子。rl好像是反射光线和主光源方向的点乘,然后是FragmentGI求出GI。FragmentGI函数如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/8f98f56fababa0e941f59727d38b551c)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/3e19d17bd63a96de1920adf35a4383d2)
fragmentGI函数里首先构建了一个UnityGIInput数据结构,然后调用UnityGlobalIllumination求出gi,有Unity_GlossyEnvironmentData的话,GI里会加入该参数。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/48c90d066a6441afa1bae04738a94ca6)
主要包含UnityGIbase和UnityGI_IndirectSpecular,前者计算了阴影的衰减(烘焙阴影和实时阴影混合),SH光照,烘焙的lightmap,平行光与非平行光lightmap,动态lightmap。最终返回UnityGI结构,该结构包含light,color,indirect.diffuse参数。
UnityGI_IndirectSpecular(UnityGLobalIllumination.cginc)计算间接高光,用probe相关的属性计算。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/93e4144423d1c7c193eb4e514633a0da)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/c153cac6d5f9631c14a9fa187b6ec823)
用求得的GI,计算衰减的光颜色。
然后就是BRDF计算间接光照和直接光照了
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/90d130f47180bd1f7c9be1c63bbd3c71)
simple版好像brdf不多,下面看看复杂版
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/a68e6613a4bd8030f2dfc2c19029d1df)
输入结构:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/f838838ebdc617246d64e4b5161e006f)
复杂的比simple版多了些东西,但原理是一样的。
后面我发现BRDF有3个版本。上面的是brdf3.
根据冯乐乐的数,第18章,在disney使用的brdf2中,它的漫反射项为:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/5c43953e5665c71a3d1adf7dd6d65c1a)
pbs高光项:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/414bf018a91420495ed0255a1dfbf1e4)
函数实现:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/3bad5c1e091270df9929a8d0c45a9132)
代码实例:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202009/07/d88f845de188a5135f9428b846602775)
整个fragment shader代码(包含了GI部分,调用unity内置函数实现):
下面是standard.shader的面板
shader结构图:
subShader的区别,先看两个pass的区别:
然后我们先看第一个forward pass,“UnityStandardCoreForward.cginc”里查看,vertBase和fragBase又根据是否定义simple而执行不同的分支:
定义了simple的分支,数据结构和函数,我们去“UnityStandardCoreForwardSimple.cginc”文件里查看,vert函数的输入结构定义:
vert函数输出结构定义
vert函数定义
里面涉及的函数,获取切线空间光照和view方向的函数
获取lightmap或环境光的uv的函数:
grazing参数的获取:
再看fragment函数fragForwardBaseSimpleInternal,在UnityStandardCoreForwardSimple.cginc文件中:
里面第一句又从输入的结构得到FragmentCommonData,函数如下:
fragmentCommonData结构如下:
与前面的结构比较:
转换函数FragmentSetupSimple也在SimpleForward这个文件里
其中的UNITY_SETUP_BRDF_INPUT函数(UnityStandardCore.cginc中)定义如下,这个函数里获取FragmentCommonData的difColor,SpecColor,oneMinusReflectivity和smoothness。
其中a通过Alpha函数得到,如果定义了_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A则通过_color.a得到,否则:
。该宏的定义通过editor得到,在builtin_shaders-5.6\Editor\StandardShaderGUI.cs文件中:
其中smoothness通过SpecularGloss函数得到(在UnityStandardInput.cginc中定义),如果有speculargloss贴图,则对贴图采样,如果用主贴图的a通道存储gloss值,则sg的a通道通过采样主贴图得到,如果a通道在specularGLoss贴图,则通过采样specGLoss贴图得到。如果没有定义高光贴图,则高光颜色通过_SpecColor得到,如果gloss在主贴图的a通道,则采样主贴图获取gloss,否则,gloss通过_Glossiness得到:
diffColor,SpecColor,oneMinusReflectivity又通过EnergyConservationBetweenDiffuseAndSpecular得到:
diffColor用preMultiplyAlpha函数,里面将blend模式加入:
看完FragmentCommonData后,我们再继续看fragForwardBaseSimpleInternal函数,mainLight包含颜色和方向,nDotL,然后是shadowMask衰减,实时阴影衰减并将两个衰减混合。从遮挡贴图采样得到遮挡因子。rl好像是反射光线和主光源方向的点乘,然后是FragmentGI求出GI。FragmentGI函数如下:
fragmentGI函数里首先构建了一个UnityGIInput数据结构,然后调用UnityGlobalIllumination求出gi,有Unity_GlossyEnvironmentData的话,GI里会加入该参数。
主要包含UnityGIbase和UnityGI_IndirectSpecular,前者计算了阴影的衰减(烘焙阴影和实时阴影混合),SH光照,烘焙的lightmap,平行光与非平行光lightmap,动态lightmap。最终返回UnityGI结构,该结构包含light,color,indirect.diffuse参数。
UnityGI_IndirectSpecular(UnityGLobalIllumination.cginc)计算间接高光,用probe相关的属性计算。
用求得的GI,计算衰减的光颜色。
然后就是BRDF计算间接光照和直接光照了
simple版好像brdf不多,下面看看复杂版
输入结构:
复杂的比simple版多了些东西,但原理是一样的。
后面我发现BRDF有3个版本。上面的是brdf3.
根据冯乐乐的数,第18章,在disney使用的brdf2中,它的漫反射项为:
float attenuation = 1; float3 attenColor = attenuation * _LightColor0.xyz; float Pi = 3.141592654; float InvPi = 0.31830988618; float gloss = _Gloss; half fd90 = 0.5 + 2 * LdotH * LdotH * (1-gloss); float3 directDiffuse = ((1 +(fd90 - 1)*pow((1.00001-NdotL), 5)) * (1 + (fd90 - 1)*pow((1.00001-NdotV), 5)) * NdotL) * attenColor;
pbs高光项:
函数实现:
代码实例:
整个fragment shader代码(包含了GI部分,调用unity内置函数实现):
float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); // UNITY_SAMPLE_DEPTH对深度贴图进行采样 // _ProjectionParams的x为1,如果投影翻转则x为-1,y是摄像机近裁剪平面,z是摄像机远裁剪平面,w是1/z. float sceneZ = max(0,LinearEyeDepth (UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)))) - _ProjectionParams.g); //减去近裁剪平面 float partZ = max(0,i.projPos.z - _ProjectionParams.g); //物体之深度 float3x3 tangentTransform = float3x3( i.tangentDir, i.bitangentDir, i.normalDir); // 转移矩阵 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz); float3 _WetNormal_var = UnpackNormal(tex2D(_WetNormal,TRANSFORM_TEX(i.uv0, _WetNormal))); float3 _Ripple_var = UnpackNormal(tex2D(_Ripple,TRANSFORM_TEX(i.uv0, _Ripple))); //float3 node_4532_nrm_base = _WetNormal_var.rgb + float3(0,0,1); //float3 node_4532_nrm_detail = _Ripple_var.rgb * float3(-1,-1,1); //wet和下雨混合 //float3 node_4532_nrm_combined = node_4532_nrm_base*dot(node_4532_nrm_base, node_4532_nrm_detail)/node_4532_nrm_base.z - node_4532_nrm_detail; float3 node_4532_nrm_combined = _WetNormal_var.rgb + _Ripple_var.rgb; float3 node_4532 = node_4532_nrm_combined; float3 normalLocal = node_4532; float3 normalDirection = normalize(mul( normalLocal, tangentTransform )); float3 viewReflectDirection = reflect( -viewDirection, normalDirection ); float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); float3 lightColor = _LightColor0.rgb; float3 halfDirection = normalize(viewDirection+lightDirection); float attenuation = 1; float3 attenColor = attenuation * _LightColor0.xyz; float Pi = 3.141592654; float InvPi = 0.31830988618; float gloss = _Gloss; float specPow = exp2( gloss * 10.0+1.0); UnityLight light; #ifdef LIGHTMAP_OFF light.color = lightColor; light.dir = lightDirection; light.ndotl = LambertTerm (normalDirection, light.dir); #else light.color = half3(0.f, 0.f, 0.f); light.ndotl = 0.0f; light.dir = half3(0.f, 0.f, 0.f); #endif UnityGIInput d; d.light = light; d.worldPos = i.posWorld.xyz; d.worldViewDir = viewDirection; d.atten = attenuation; #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON) d.ambient = 0; d.lightmapUV = i.ambientOrLightmapUV; #else d.ambient = i.ambientOrLightmapUV; #endif d.boxMax[0] = unity_SpecCube0_BoxMax; d.boxMin[0] = unity_SpecCube0_BoxMin; d.probePosition[0] = unity_SpecCube0_ProbePosition; d.probeHDR[0] = unity_SpecCube0_HDR; d.boxMax[1] = unity_SpecCube1_BoxMax; d.boxMin[1] = unity_SpecCube1_BoxMin; d.probePosition[1] = unity_SpecCube1_ProbePosition; d.probeHDR[1] = unity_SpecCube1_HDR; Unity_GlossyEnvironmentData ugls_en_data; ugls_en_data.roughness = 1.0 - gloss; ugls_en_data.reflUVW = viewReflectDirection; UnityGI gi = UnityGlobalIllumination(d, 1, normalDirection, ugls_en_data ); lightDirection = gi.light.dir; lightColor = gi.light.color; float NdotL = max(0, dot( normalDirection, lightDirection )); float LdotH = max(0.0,dot(lightDirection, halfDirection)); float3 specularColor = _Specular.rgb; float specularMonochrome = max( max(specularColor.r, specularColor.g), specularColor.b); float NdotV = max(0.0,dot( normalDirection, viewDirection )); float NdotH = max(0.0,dot( normalDirection, halfDirection )); float VdotH = max(0.0,dot( viewDirection, halfDirection )); float visTerm = SmithBeckmannVisibilityTerm( NdotL, NdotV, 1.0-gloss ); float normTerm = max(0.0, NDFBlinnPhongNormalizedTerm(NdotH, RoughnessToSpecPower(1.0-gloss))); float specularPBL = max(0, (NdotL*visTerm*normTerm) * UNITY_PI / 4.0 ); float3 directSpecular = 1 * pow(max(0,dot(halfDirection,normalDirection)),specPow)*specularPBL*lightColor*FresnelTerm(specularColor, LdotH); half grazingTerm = saturate( gloss + specularMonochrome ); float3 indirectSpecular = (gi.indirect.specular); indirectSpecular *= FresnelLerp (specularColor, grazingTerm, NdotV); float3 specular = (directSpecular + indirectSpecular); NdotL = max(0.0,dot( normalDirection, lightDirection )); half fd90 = 0.5 + 2 * LdotH * LdotH * (1-gloss); float3 directDiffuse = ((1 +(fd90 - 1)*pow((1.00001-NdotL), 5)) * (1 + (fd90 - 1)*pow((1.00001-NdotV), 5)) * NdotL) * attenColor; float3 indirectDiffuse = float3(0,0,0); indirectDiffuse += gi.indirect.diffuse; float3 diffuseColor = _LightColor0.rgb; diffuseColor *= 1-specularMonochrome; float3 diffuse = (directDiffuse + indirectDiffuse) * diffuseColor; float3 finalColor = diffuse + specular; fixed4 finalRGBA = fixed4(finalColor,(saturate((sceneZ-partZ)/_Depth)*_Alpha)); UNITY_APPLY_FOG(i.fogCoord, finalRGBA); return finalRGBA; }
相关文章推荐
- unity shader 固定管线实例(三) 光照 自发光混合 纹理混合
- unity后期特效shader之photoshop混合模式公式的应用
- Unity shader教程-第五课:自定义光照模型之Half Lambert模型
- 聊聊Unity2018的LWRP和混合光照
- vert fragment shader在unity中如果实现光照阴影
- UnityShader官方案例之表面着色器光照示例
- Unity Shader 学习笔记(十一) 混合纹理Shader实例
- Unity 不受光照影响shader 仿Unlit/Texture
- UnityShader初级篇——实现逐顶点高光反射光照模型
- 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
- [Unity Shader]光照模型对物体的假设
- 关于unity里pbr技术和材质 unity5默认shader和传统的对比
- UnityShader初级篇——透明度混合
- 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
- Unity Shader Example 2 (流光与模拟光照)
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
- Unity Shader Example 8 (光照贴图)
- Unity Shader自定义光照模型
- unity中使用自定义shader进行光照贴图烘培无法出现透明度的坑爹问题
- Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质