网站默认网站名,泰安人事考试网,wordpress登陆post,广州手机网站开发报价最近有相关需求制作#xff0c;所以这里编写一个文档#xff0c;方便后续的流程查看。
下载源码
由于unity内置的shader是无法查看源码的#xff0c;你需要去官网下载对应版本内置源码查看 在引擎下载那里#xff0c;会有一个Built in Shaders#xff0c;下载 打开以后…最近有相关需求制作所以这里编写一个文档方便后续的流程查看。
下载源码
由于unity内置的shader是无法查看源码的你需要去官网下载对应版本内置源码查看 在引擎下载那里会有一个Built in Shaders下载 打开以后就是对应的shader 内置的shandard在DefaultResourcesExtra目录内打开便是。
shader解析
Standard里面分了两套一套正常的一套精简版的 这两套渲染的切换是通过设置shader的lod进行切换的。 每个shader下面由5个pass组成简化版的不支持延迟渲染
前向渲染主光源前向渲染副光源阴影渲染延迟渲染烘焙
简化版本的渲染也不支持视差偏移它们是通过宏去控制的更多不同在渲染代码内部。
ForwardBase 和ForwardAdd引用的一套渲染逻辑然后通过定义的宏和调用不同的顶点/片元着色器函数来区分到底是base还是add。
base的是这样
add是这样 它们都引用的UnityStandardCoreForward渲染逻辑 在这个文件里面是一些主要函数的定义区分是否为简化的shader如果简化的shader则引入简化的库文件非简化则引入了UnityStandardCore.cginc在这里定义了pass里面调用的顶点着色器和片元着色器函数函数内直接调用了对应的UnityStandardCore库里的函数这里也是standard的核心代码。 上面还引入了UnityStandardConfig.cginc这个文件则是一些配置主要定义的宏抽几个比较重要的 下面定义了立体图贴图的曝光度以及lod层级数 定义brdf GGX
UnityStandardCore.cginc
里面代码的一些库的引用核心库也是基于这些库实现的最终渲染 ForwardBase渲染主要就是调用了
VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }VertexInput 就是在UnityStandardInput.cginc内实现的需要传入到顶点着色器的数据 VertexOutputForwardBase 则是从顶点传入到片元的数据 vertForwardBase 函数里对位置UV法向等做了一些处理更复杂的还有lightmap的UV还有视差偏移 重点函数片元着色器fragForwardBaseInternal有点代码越少越狠的节奏后面我将一个个的函数解析
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy)
这个是为了实现淡入淡出的效果 unity_DitherMask 为unity内置生成的抖动noise贴图 unity_LODFade 为需要设置的变量在UnityShaderVariables.cginc里面定义
float4 unity_LODFade; // x is the fade value ranging within [0,1]. y is x quantized into 16 levelsFRAGMENT_SETUP(s)
这个函数主要是生成后续使用的数据FragmentCommonData定义为FragmentSetup函数
#define FRAGMENT_SETUP(x) FragmentCommonData x FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i));i.tex 顶点着色器计算的uv i.eyeVec.xyz 摄像机朝向 IN_VIEWDIR4PARALLAX(i) 摄像机朝向基于视差偏移的法向值 i.tangentToWorldAndPackedData 切线坐标系转世界坐标系矩阵 [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos] IN_WORLDPOS(i) 渲染目标世界坐标位置
FragmentCommonData 则是返回从顶点着色器拿到的数据处理后的数据后续获取通过s变量获取。oneMinusReflectivity 为1-反射率 然后就是函数FragmentSetup设置数据截图里面我也加了注释 这里主要讲的是UNITY_SETUP_BRDF_INPUT函数它可以根据工作流去设置数据有三种 SpecularSetup RoughnessSetup MetallicSetup分别对应 高光工作流 粗糙度工作里 金属度工作流standard.shader里面定义了金属度工作流 如果没有定义的话会切换高光工作流 由于我这里使用的是金属度工作流这里讲解一些金属度工作流的相关内容MetallicSetup函数函数内有两个函数第一个函数去获取贴图的值第二个函数为计算漫反射颜色镜面反射颜色和反射率 MetallicGloss内返回二维向量x为金属度y为光滑度光滑度还可以选择是使用的_MetallicGlossMap的a通道还是_MainTex的a通道 DiffuseAndSpecularFromMetallic unity_ColorSpaceDielectricSpec的值在线性空间中默认是 half4(0.04, 0.04, 0.04, 1.0 - 0.04)这也是物理渲染中默认反射率 根据金属度求出反射率
MainLight UnityLight mainLight MainLight(); //主光源UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld); //合并阴影UnityLight 结构里面有三个值 color 光的颜色 dir 光的朝向 ndotl 法向和光的点乘值已弃用MainLight函数里面就是获取第一盏灯的颜色和朝向 UNITY_LIGHT_ATTENUATION 计算阴影遮挡。会根据光的类型调用不同的函数一般主光源都是平衡光这里看一下平衡光的实现代码在AutoLight.cginc里面 在AutoLight.cginc中对多种情况的处理比如屏幕空间阴影包含烘焙阴影 这里我们看最简单的使用SHADOW_ATTENUATION生成的unitySampleShadow函数这个函数会去获取shadowmap的值来做处理
#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) UNITY_SAMPLE_TEX2DARRAY(tex, float3((uv).x / (uv).w, (uv).y / (uv).w, (float)unity_StereoEyeIndex)).r这一块解析起来确实麻烦如果你需要阴影的话记得直接使用UNITY_LIGHT_ATTENUATION函数。第一个值就是阴影的值。
FragmentGI
全局光照包含了lightmapsh球谐光照ibl等对物体影响的内容
UnityGI gi FragmentGI(s, occlusion, i.ambientOrLightmapUV, atten, mainLight); //全局光照UnityGI包含全局光照有光的数据以及间接光的漫反射和镜面反射颜色 FragmentGI 函数主要是设置一些所需要的值然后调用UnityGlobalIllumination生成最终所需的UnityGI数据 UnityGlossyEnvironmentSetup 主要是求出了两个值 SmoothnessToPerceptualRoughness是通过光滑度求出粗糙度也就是1-光滑度reflUVW根据眼睛和法向求出反射方向 准备好需要的全局光照计算数据以后就要开始调用UnityGlobalIllumination计算了分别去计算间接光漫反射以及间接光镜面反射 在间接光漫反射里面考虑光照贴图和动态光照贴图这个我在之前我的文章里面说过这里就不再多解释。 解析一下上图比较重要的几行代码
o_gi.light.color * data.atten;
o_gi.indirect.diffuse ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
o_gi.indirect.diffuse * occlusion;ShadeSHPerPixel 计算间接光漫反射相对于lightmap里面获取的它具有动态性。球谐光照是由七个四维向量组成
由引擎设置参数。unity还兼容的3d纹理方式的SHEvalLinearL0L1_SampleProbeVolume 计算完成间接光漫反射以后就是计算间接光镜面反射在unity里面是通过实现原理就是通过立方体贴图去拾取颜色作为镜面反射的颜色 里面主要的方法就是Unity_GlossyEnvironment这个去拾取引擎设置的立方体贴图并获取颜色 perceptualRoughnessToMipmapLevel就是粗糙度乘以LOD级数UNITY_SPECCUBE_LOD_STEPS粗糙度越低表示越光滑那么lod层级就越低图片拾取的也最清晰。 最后将全局光的灯光颜色间接光漫反射间接光镜面反射计算完成交给物理渲染BRDF函数实现最后的颜色。
UNITY_BRDF_PBS
half4 c UNITY_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect); //基于物理的渲染看代码standard里面内置了三套方式
BRDF1_Unity_PBS 是基于物理的BRDFBidirectional Reflectance Distribution Function双向反射分布函数BRDF2_Unity_PBS 是基于极简的微表面理论的BRDF http://www.thetenthplanet.de/archives/255BRDF3_Unity_PBS 是不是微表面的基于修正归一化的 Blinn-Phong BRDF 这里我只介绍质量最好的第一种BRDF1_Unity_PBS看注释也能了解到它的模型是如何计算的 直接光漫反射 kD / pi 直接光镜面反射 kS * (D * V * F) / 4 最后乘以NdotL BRDF里面还有两种一种是GGX的高光另一种是老旧的BlinnPhong的 首先函数获取到需要用的数据粗糙度半角向量halfDirNdotVNdotLNdotHLdotVLdotH 然后基于数据求直接光漫反射 // Diffuse termhalf diffuseTerm DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;求漫反射还给注释迪士尼的漫反射必须除以PI在函数外实现貌似unity都亮了PI所以不用除以PI了 然后解释了一下为什么不除以PI 接下来先求出BRDF的D项和V项 然后在最后颜色合并的时候求出菲涅尔项 F怪不得之前听朋友说unity的BRDF写的很难看确实难看 最终计算出来了颜色加上自发光合并雾效返回片元颜色