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. CodeIgniter底层数据库类继承关系

    1.CI_DB_mysql_driver 继承 CI_DB,  CI_DB这个类是不存在的,每次调用文件中~/CodeIgniter_2.1.3/system/database/DB.php函数&am ...

  2. ansible3

    一.setup模块 ansible的setup模块主要用来收集信息,查看参数: [root@localhost ~]# ansible-doc -s setup # 查看参数,部分参数如下: filt ...

  3. php基于swoole扩展的WebSocket

    php的swoole的扩展可以实现WebSocket通信,方法如下 1.php添加swoole扩展: 一:两种安装方式:1>编译安装:1>wget http://pecl.php.net/ ...

  4. docker 常用命令整理

    1.查看镜像 docker images 2.查看所有状态的容器 docker ps -a 3.运行容器 docker exec -it  container /bin/bash docker att ...

  5. CodeForces - 540B School Marks —— 贪心

    题目链接:https://vjudge.net/contest/226823#problem/B Little Vova studies programming in an elite school. ...

  6. C语言实现wc基本功能

    GitHub地址:https://github.com/hhx007/wc 项目要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数. 这个项目要求写一个命令行程序,模仿已有w ...

  7. tf.stack和tf.unstack

    import tensorflow as tf a = tf.constant([1,2,3]) b = tf.constant([4,5,6]) c1 = tf.stack([a,b],axis=0 ...

  8. Django:locals()小技巧

    locals()返回一个包含当前作用域里面的所有变量和它们的值的字典. 所以可以把views改写为 def current_datetime(request):     current_date = ...

  9. Sysctl命令及linux内核参数调整

        一.Sysctl命令用来配置与显示在/proc/sys目录中的内核参数.如果想使参数长期保存,可以通过编辑/etc/sysctl.conf文件来实现.    命令格式:  sysctl [-n ...

  10. FFmpeg命令:几种常见场景下的FFmpeg命令(摄像头采集推流,桌面屏幕录制推流、转流,拉流等等)

    前提: 首先你得有FFmpeg(ffmpeg官网快捷通道:http://ffmpeg.org/) 再者,推流你得有个流媒体服务,个人测试用小水管:rtmp://eguid.cc:1935/rtmp/t ...