Unity 5着色器系统代码介绍(上)
来源:互联网 发布:淘宝男装夏季 编辑:程序博客网 时间:2024/09/21 06:33
http://forum.china.unity3d.com/thread-25724-1-10.html
Unity 5着色器系统代码介绍(上)
Unity在着色器开发方面提供了很大的灵活性。有些工具需要你编写一个“合适”的自定义着色器(合适,即无法在节点编辑器里完成,必须要写代码),其麻烦程度可算是相当轻量。不过,完全基于延迟渲染器,意味着我们无法像在前向着色器中那样可以放开手脚,而只能受限于g-buffer中包含的那些无法进行更改的信息。
所以说,如果你对Unity 5的标准着色器基本满意,想在其中添加点东西,比如一个额外的着色器属性,或者修改某些功能点。或者你想重新制作你自己的着色器系统,这个系统需要涉及阴影、全局光照、光照贴图、直接光照等等。
最主要的问题是,无法对标准着色器进行编辑。你无法直接修改标准着色器的代码。你必须先下载你所安装的Unity版本的对应着色器代码。
获取标准着色器代码
具体做法是,检查你所安装的Unity 5版本,然后访问Unity Download Archive网页,在下拉列表中选择适合你平台(Win/Mac)的“内置着色器”,最后将下载的zip文件解压。
概述
Zip文件中包含了标准着色器的完整源代码,包括特殊的检视视图UI以及它包含的所有内容。四个文件夹:
- CGIncludes
- DefaultResources
- DefaultResourcesExtra
- Editor
Editor仅包含实现标准着色器检视视图UI的.cs文件。
CGIncludes中的文件包含了所有其他着色器所需要的函数。我们会仔细研究它们,因为我们将会用到那些函数。
DefaultResources和DefaultResourcesExtra 包含了许多适用于不同情况的着色器。
下面来学习如何解读标准着色器,然后依次查看各个子系统,直接光照、阴影、全局光照等等。本文以Unity 5.4版本为例,建议大家采用Unity 5.3或以上版本,因为Unity将光照模型(BRDF)从Phong改为了GGX。Phong简单快速,各向同性,但是表达能力有限;GGX更为复杂,支持各向异性,并且有接近现实世界的高光效果,表达能力较强。这是一个很大的改进,使我们可以制作更多有趣的示例。
追踪pragma
回到着色器代码。从一个简单的标准着色器开始:DefaultResourcesExtra\Standard.shader。
打开这个文件后会发现,它是一个Surface着色器,包含一个Properties部分,以及不同的着色器pass。它针对延迟与前向渲染器还有不同的pass。
让我们分析前向渲染器(前向渲染器有两个Pass,Base Pass针对第一个光源,另一个Add Pass针对所有其他光源)的Base Pass:
[C#] 纯文本查看 复制代码
// ------------------------------------------------------------------
// Base forward pass (directional light, emission, lightmaps, ...)
Pass
{
Name
"FORWARD"
Tags {
"LightMode"
=
"ForwardBase"
}
Blend [_SrcBlend] [_DstBlend]
ZWrite [_ZWrite]
CGPROGRAM
#pragma target 3.0
// -------------------------------------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
#pragma shader_feature _PARALLAXMAP
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"
ENDCG
}
如你所见,它基本上由一堆pragma和定义组成。在ubershader样式中,那些pragma激活了在不同包含文件中的代码段。因此要了解这个pass中实际发生的事情,必须打开CGIncludes\UnityStandardCoreForward.cginc,并逐一查看每个代码段中的每个pragma。这个过程太长,需要太多笔墨,所以现在还是让我们专注于寻找基本函数,即主要的光照计算过程发生的地方。
CGIncludes\UnityStandardCoreForward.cginc的作用仅仅是将在其他cginclude中包含的东西连在一起。在这里,它负责根据定义UNITY_STANDARD_SIMPLE,设置好要使用的顶点与片段函数。
下面看看更简单的那个CGIncludes\UnityStandardCoreForwardSimple.cginc。它很“简单”,因为它不支持PARALLAXMAP、DIRLIGHTMAPCOMBINED、DIRLIGHTMAP_SEPARATE,因此解读起来也相对简单。
基础函数与结构体
最后,这个文件里还有一些函数与结构体,基础的有以下这些:
- struct VertexOutputBaseSimple,这个数据结构用于保存从顶点着色器向片段着色器传送的数据。
- vertForwardBaseSimple 是在每个顶点上都会执行的函数,填充 VertexOutputBaseSimple结构体。
- fragForwardBaseSimpleInternal,正向渲染器中,接受顶点输出结构体,并计算第一个光源的函数。
片段函数
它返回一个向量,其中包含四个half精度浮点数(一个颜色和透明度),它接受一个VertexOutputBaseSimple结构体:
[C#] 纯文本查看 复制代码
half4 fragForwardBaseSimpleInternal (VertexOutputBaseSimple i)
{
FragmentCommonData s = FragmentSetupSimple(i);
UnityLight mainLight = MainLightSimple(i, s);
half atten = SHADOW_ATTENUATION(i);
half occlusion = Occlusion(i.tex.xy);
half rl = dot(REFLECTVEC_FOR_SPECULAR(i, s), LightDirForSpecular(i, mainLight));
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
half3 attenuatedLightColor = gi.light.color * mainLight.ndotl;
half3 c = BRDF3_Indirect(s.diffColor, s.specColor, gi.indirect, PerVertexGrazingTerm(i, s), PerVertexFresnelTerm(i));
c += BRDF3DirectSimple(s.diffColor, s.specColor, s.oneMinusRoughness, rl) * attenuatedLightColor;
c += UNITY_BRDF_GI (s.diffColor, s.specColor, s.oneMinusReflectivity, s.oneMinusRoughness, s.normalWorld, -s.eyeVec, occlusion, gi);
c += Emission(i.tex.xy);
UNITY_APPLY_FOG(i.fogCoord, c);
return
OutputForward (half4(c, 1), s.alpha);
}
从代码中可以看到,它收集了所需的信息,并对直接与间接光的贡献、雾、衰减、自发光和遮蔽进行了计算。
这些过程已被高度封装,因此我们需要依次查看这些函数,才能了解它们的实际用途,以及代码的具体作用。
追踪更多的函数
对光源进行实际计算的函数并不在此文件中,部分在CGIncludes/UnityStandardCore.cginc中:
- MainLight (实际上用于 UnityStandardCoreForward.cginc中的MainLightSimple)
[C#] 纯文本查看 复制代码
UnityLight MainLightSimple(VertexOutputBaseSimple i, FragmentCommonData s)
{
UnityLight mainLight = MainLight(s.normalWorld);
#if defined(LIGHTMAP_OFF) && defined(_NORMALMAP)
mainLight.ndotl = LambertTerm(s.tangentSpaceNormal, i.tangentSpaceLightDir);
#endif
return
mainLight;
}
我们能看到那里对LambertTerm进行了计算,但仅在光照贴图关闭且法线贴图打开时会这样。
CGIncludes/UnityStandardBRDF.cginc:
- BRDF3DirectSimple (使用BRDF3Direct)
[C#] 纯文本查看 复制代码
half3 BRDF3_Direct(half3 diffColor, half3 specColor, half rlPow4, half oneMinusRoughness)
{
half LUT_RANGE = 16.0;
// must match range in NHxRoughness() function in GeneratedTextures.cpp
// Lookup texture to save instructions
half specular = tex2D(unity_NHxRoughness, half2(rlPow4, 1-oneMinusRoughness)).UNITY_ATTEN_CHANNEL * LUT_RANGE;
#if defined(_SPECULARHIGHLIGHTS_OFF)
specular = 0.0;
#endif
它看起来在使用查表法计算镜面反射的贡献。
- LambertTerm, 导向到DotClamped
[C#] 纯文本查看 复制代码
inline half DotClamped (half3 a, half3 b)
{
#if (SHADER_TARGET < 30 || defined(SHADER_API_PS3))
return
saturate(dot(a, b));
#else
return
max(0.0h, dot(a, b));
#endif
}
从MainLightSimple我们得知,传入的参数是N和L。所以片段函数首先设置好片段,计算主光源ndotl、衰减、遮蔽、全局光照以及灯光颜色。然后计算最终光线的所有贡献,并将直接、间接、全局光照加总后再应用雾。
正如你所见,着色器在光照计算方面相当轻量,仅使用了一个Lambert算法,并查了一下表。它不像基于物理的着色器使用的那么多,这很可能是最廉价的标准着色器版本了。
在下一篇中,我们会以同样的方式审视标准版本的着色器,很可能会看到一些更加高级的BRDF。
阅读全文
0 0
- Unity 5着色器系统代码介绍(上)
- Unity 5着色器系统代码介绍(下)
- Unity Shader(ShaderLab)着色器简介
- unity着色器基础(一)
- unity着色器基础(二)
- Unity创建着色器
- Unity 着色器
- Unity着色器学习
- Unity着色器基础
- 【Unity手册】着色器参考
- Unity Shaders表面着色器
- Unity 简单渐变着色器
- unity水面波纹着色器
- Shader(着色器)相关介绍
- OpenGL着色器介绍
- unity shader 可编程管线(一) 表面着色器SurfaceShader
- Unity标准着色器(Standard Shader)参数讲解(一)
- Unity标准着色器(Standard Shader)参数讲解(二)
- 如何自己做lenet样本,使用LMDB格式
- CSS3的calc()做响应模式布局
- Maven标签解释
- C# 只开启一个程序,如果第二次打开则自动将第一个程序显示到桌面
- Wifi 控制
- Unity 5着色器系统代码介绍(上)
- 资讯精选 | 如何保证人工智能与HPC的成功融合?
- sql 20170906 left join
- C++回顾(三)智能指针
- NZT提示200;201;202处理方式
- 数据库设计
- Longest Palindromic Substring
- iOS 一一 storyBoard中segue实现页面跳转
- STM32调试支持