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. Redis——慢查询分析

    核心知识点: 1.什么是慢查询? 2.客户端执行一条命令的步骤? 3.阈值和慢查询日志的设置? 4.慢查询日志的操作命令:slowlog get.slowlog len.slowlog reset. ...

  2. Java for LeetCode 096 Unique Binary Search Trees

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  3. 利用iOS原生系统进行人脸识别+自定义滤镜(GPUImage)

    人脸识别+滤镜效果(基于GPUImage实现的自定义滤镜) 最近碰到一个好玩的需求.说要客户端这边判定一下是否有人脸.在有的基础上.对相片做进一步的美化滤镜处理. 首先是人脸的识别判定; //将图片对 ...

  4. delphi XE7 HttpEncode 编码问题

    近期在做网址编码相关的工作,发现在用 XE5 编译的时候,一切正常,拿 到 XE7下 就 结果错误了.百度了下,谷歌 了下,有人提出,但是,我没有找到答案,也许都没有碰到这个问题,也许都己经自己默默的 ...

  5. Linux线程的几种结束方式

    Linux创建线程使用 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) ...

  6. 《avascript 高级程序设计(第三版)》 ---第三章 基本概念

    本章主要介绍Javasript语言的一些语法: 1.严格模式:开启:"use strict"; 2.变量:全部用var来定义,在函数中使用的称为局部变量,不能全局使用. 3.数据类 ...

  7. 打开Vs2010时,卡在加载工具箱内容 不动了

    我是直接打开Visual Studio 2010,而不是以打开解决方案的方式打开.然后就在左下角显示“正在从包‘Microsoft.VisualStudio.IDE.ToolboxControlsIn ...

  8. 图形绘制处理逻辑VC

    // 逻辑1:先从资源中读取背景资源,然后将绘图对象与DC绑定,通过绘图对象绘出背景 // 逻辑2:先从资源中读取背景资源,新建一个MEMDC,将绘图对象与MEMDC绑定,并且 // 通过绘图对象在内 ...

  9. Too many open files解决方案及原理

    以下是我解决Too many open files异常时学习的知识的理解和总结,如有不正确指出,敬请指出! 此问题中文搜索雷同,你可以尝试以下关键字:"file descriptor lea ...

  10. BZOJ 1609 [Usaco2008 Feb]Eating Together麻烦的聚餐:LIS & LDS (nlogn)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1609 题意: 给你一个只由数字"1,2,3"组成的序列a[i],共n个 ...