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。

Unity 5着色器系统代码介绍(上)的更多相关文章

  1. Unity 5着色器系统代码介绍(下)

    http://forum.china.unity3d.com/thread-25738-1-10.html 上一篇对着色器系统的工作原理做了介绍,现在我们将继续深入,将目光聚焦在标准着色器的光照函数. ...

  2. Unity Shader着色器优化

    https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247493518&idx=1&sn=c51b92e9300bcf ...

  3. Unity 几何着色器

    Unity 几何着色器 shaderGeometry Shader几何着色器 Unity 几何着色器 如果学习不能带来价值,那将毫无意义 简介     在顶点和片段着色器之间有一个可选的着色器,叫做几 ...

  4. [Unity] Shader(着色器)输入输出和语义

    在Unity5.x后, 已经支持了基于物理的光照模型,也就是常说的次时代引擎所必须具备的功能. 如果在Properties使用2D,CG里要用sampler2D,代表使用的是2维纹理 如果在Prope ...

  5. [Unity] Shader(着色器)之纹理贴图

    在Shader中,我们除了可以设定各种光线处理外,还可以增加纹理贴图. 使用 settexture 命令可以为着色器指定纹理. 示例代码: Shader "Sbin/ff2" { ...

  6. Unity 光照着色器

    光照着色器需要考虑光照的分类,一般分为漫反射和镜面反射. 漫反射计算基本光照: float brightness=dot(normal,lightDir)    将法线和光的入射方向进行点积运算,求出 ...

  7. [Unity] Shader(着色器)之固定管线

    在Unity中,固定管线Shader的性能是最好的. 什么是固定管线呢? 固定渲染管线 —— 这是标准的几何&光照(T&L)管线,功能是固定的,它控制着世界.视.投影变换及固定光照控制 ...

  8. Unity 渲染教程(二):着色器基础

    转载:https://www.jianshu.com/p/7db167704056 这是关于渲染基础的系列教程的第二部分.这个渲染基础的系列教程的第一部分是有关矩阵的内容.在这篇文章中我们将编写我们的 ...

  9. Unity 着色器基础知识

    一.着色器基础知识 着色器通过代码模拟物体表面发生的事情,其实就是GPU中运行的一段代码. 着色器的类型: 顶点着色器.片元着色器.无光照着色器.表面着色器.图像特效着色器.计算着色器. 坐标空间: ...

随机推荐

  1. 基于ajax和Form实现的注册

    注册 urls.py from django.contrib import admin from django.urls import path, re_path from app01 import ...

  2. matlab之plot()函数

    是个画图函数: 语法: figure(1000);hold on;plot(x,y);axis equal; 其中,x和y是某两个长度相同的列向量.比如:x=[1;2;3;4];y=[1;2;3;4] ...

  3. BZOJ 1601 [Usaco2008 Oct]灌水:最小生成树

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1601 题意: Farmer John已经决定把水灌到他的n(1<=n<=300 ...

  4. 勤于思考:IE10不支持检测IE6的代码

    这句话 var isIE6 = isIE && ([/MSIE (\d)\.0/i.exec(navigator.userAgent)][0][1] == 6); 在IE6~9都没问题 ...

  5. UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

    py文件直接在cmd窗口用python命令执行时正常:代码逐句在ipython中也正常:但是, 在wingIDE中运行报错“UnicodeEncodeError: 'ascii' codec can' ...

  6. 第十五章-Web开发

    随着web的发展, 最开始使用的CS架构已经不适合web了, 现在web使用的架构是BS架构 如今大部分重量级的软件都以web形式提供了 web开发的四个阶段 1) 静态web页面 2) CGI: 静 ...

  7. linux命令学习笔记(46):vmstat命令

    vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动 进行监控.他是对系统的整体情况进行统计,不足之处是无法对某个进程进行 ...

  8. PHP读取xml方法介绍

    一,什么是xml,xml有什么用途 XML(Extensible Markup Language)即可扩展标记语言,它与HTML一样,都是SGML(Standard Generalized Marku ...

  9. python 多模块文件共享变量

    Python import 包的机制是,import进来的和默认的系统的module了,都放在sys.module这个字典里面. 多个py文件再次import的时候,会先去sys.module里面检查 ...

  10. ACM学习历程—HDU2222 Keywords Search(字典树)

    Keywords Search Description In the modern time, Search engine came into the life of everybody like G ...