Thinking in Unity3D:材质系统概览(转)

关于《Thinking in unity3D》笔者在研究和使用unity3d的过程中,获得了一些Unity3D方面的信息,同时也感叹Unity3D设计之精妙。不得不说,笔者最近几年的引擎研发工作中,早已习惯性的从Unity3D中寻找解决方案。
Unity3D虽比不上UE那么老练沉稳,气势磅礴。也比不上CE那样炫丽多姿,盛气凌人。但它的发展势如破竹,早已遍地生花!故而在此记录一些自己的心得体会,供大家参详交流。若有欠妥之处,还望各位及时指正。

Thinking in Unity3D由一系列文章组成,文章列表地址:http://www.cnblogs.com/geniusalex/p/5321545.html

什么是材质?


材质是一个相对广泛的概念,不同的专业领域有不同的定义。 在此也不一一举例说明了,我们只说在3D游戏引擎中,材质的定义。
材质的本质定义,是指能够描述一个物体的显示外观的一系列数据。它包括几个方面
1、渲染状态
     渲染状态是指早期的setRenderState那一套东西。比如,前后面裁剪,是否开启混合,混合因子等等。
2、着色方式
     着色方式,在固定管线年代,是通过一系列的API进设置。 在可编程管线年代。就对应的是我们的着色器代码。
3、参数
     不管是固定管线还是非固定管线,我们都可以设置一些参数用于着色计算。比如颜色,光源信息等等
4、纹理贴图
     纹理贴图是表现一个物体表现的颜色细节的必不可少的东西。就是一张张图片。
有了上面的这些数据后,我们就可以根据一定的运算规则,将一个几何体的质感显示为我们想要的样子。


材质系统的常见需求


一个工具或者系统的设计不可能是凭空而出的。 一定是根据需求和经验的积累,才形成了我们今天这种“材质系统”的概念。 说到这里,那么我们常见的材质系统需要做到什么样子呢。
一、模板 + 实例
     材质是一个模板,通过对某一个材质进行实例化,指定不同的数据和贴图,就可以让物体表现出不同的显示效果。 和Class + Object的关系很像。
二、多Pass
     有时候,我们为了实现一个绘制效果,靠单次绘制是无法实现的。比如描边效果。 这就要求我们单个物体能够在进行绘制的时候,多次提交材质并绘制。
三、多Technique
     多Technique是指一个材质中,应该包含不只一个实现方案。 这样当我们进行材质更替,或者进行高中低端机适配的时候。 就不会那么麻烦。 同时在数据管理上,也显得更为规范。
四、高中低端机适配
     高中低端机适配是一个很重要的特性,因为玩家的机型不可能是一样的。 在需要保证效率的情况下,我们很多时候需要降低物体渲染的复杂度。在《3D游戏中的画质与效率适配》一文中。笔者也描述过,有两种方案。 一种是通过宏定义,一种是动态切换Technique。
总结下来,就是说一个材质模板文件应该像这样的一个结构

[C] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
MaterialTemplate
{
     Technique
     {
          Pass{}
          Pass{}
     }
     Technique
     {
          Pass{}
          Pass{}
     }
}



Unity3D中的材质系统


在说到Unity3D中的材质系统的时候,我们先来看一下我们创建一个材质需要做的事情。
一、创建一个Shader并编写出自己想要的效果
二、创建一个Material并将这个Material的Shader指定为自己的材质
三、为这个Material设置参数,赋上贴图等
四、将创建好的Material拖到对象上
我们再来看一个典型的Shader应该具备的内容

[C] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Shader "MyShader" {
    Properties {
        _MyTexture ("My Texture", 2D) = "white" { }
        // other properties like colors or vectors go here as well
    }
    SubShader {
 
          LOD 100
        // here goes the 'meat' of your
        // - surface shader or
        // - vertex and program shader or
        // - fixed function shader
 
          Pass{}
 
          Pass{}
 
    }
    SubShader {
 
          LOD 1000
        // here goes a simpler version of the SubShader
        // above than can run on older graphics cards
 
          Pass{}
 
          Pass{}
    }
}



我们可以看到。 每一个Shader有多个SubShader,每一个SubShader有多个Pass。 这样看起来和我们前面提到的MaterialTemplate结构几乎一致。
而在Unity3D中。Shader就是材质模版。 Material就是一个材质实例。 每一个Material你可以认为是一个材质实例的序列化存储。

Unity3D中的材质LOD


那对于前面提到的 高中低端机适配。 有两种方法解决。一是通过宏定义,这个并不是Unity3D的推荐方式,且需要Shader编写的相关知识,在这里不作详细讨论。 如果有兴趣的朋友,可以访问括号中的链接(http://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html),以后的文章在涉及Unity3D的Shader编写方面的内容时,再进行讨论。 在此主要讨论Unity3D中通过材质LOD进行画质控制。
在Unity3D的Shader中,可以为每一个SubShader指定一个LOD值,这个LOD值可以通过设置Shader.globalMaximumLOD和Shader.maximumLOD来实现SubShader的切换。所有的SubShader按顺序进行判定,当一个SubShader满足下面两个条件时,才表明可用。
1、SubShaderLOD值小于设定的值。(你可以把LOD看作是开销,开销越大的SubShader指定越高的LOD值)
2、SubShader所有Pass使用到的显卡特性被当前设备支持。
注:Shader API文档地址:http://docs.unity3d.com/ScriptReference/Shader.html
注:Shader LOD文档地址:http://docs.unity3d.com/Manual/SL-ShaderLOD.html
注:Unity3D为每一档的内置Shader都设置了一个默认值,在LOD文档中有
注:如果不指定LOD值,那LOD=infinite 。 你可以当它是一个小于0的值。
注:这个值除了可以自己给以外,还可以在Edit->Project Settings->Quality面板中指定,如下图


Unity3D中的材质替换


官方文档地址:http://docs.unity3d.com/Manual/SL-ShaderReplacement.html

有时候,我们希望能够进行一些特殊操作。将场景物体的按另一种渲染方式渲染到RT中。 比如,仅渲染物体的深度,把物体渲染成纯白,把物体渲染成红绿热成像模式等等。但是,我们的材质在一开始就指定好了。如果要替换的话,就需要遍历所有对像,为它们重新指定材质。渲染完毕后,再切换回来。Unity3D为我们提供了更直接的方式。Camera.RenderWithShader和Camera.SetReplacementShader。前者是仅作用一次,后者是一直生效,如果想取消设置。 使用Camera.ResetReplacementShader即可。

那么问题来了,我们有时候只想渲染一些特殊的物体。比如,我们想处理除主角以外的内容。 这就需要对主角进行剔除。 那上面两个函数的第二个参数replacementTag就起作用了。如果设置了它,在进行材质替换时,它会和SubShader中的 RenderType的值 进行比对。 比如,我们即将替换的材质如下。

[C] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Shader "EffectShader" {
     SubShader {
         Tags { "RenderType"="Opaque" }
         Pass {
             ...
         }
     }
     SubShader {
         Tags { "RenderType"="SomethingElse" }
         Pass {
             ...
         }
     }
...
 
}



如果我们调用了camera.SetReplacementShader (EffectShader, "RenderType");那么,一个物体在被渲染时,会产生以下几种结果。
1、如果物体当前激活的SubShader的RenderType是Opaque,那么将会采用EffectShader的第一个SubShader。
2、如果物体当前激活的SubShader的RenderType是SomethingElse,那么将会采用EffectShader的第二个SubShader。
3、如果物体当前激活的SubShader的RenderType在EffectShader中没有与之对应的项,此物体将会在渲染中被忽略。
4、如果物体当前激活的SubShader没有RenderType这个Tag,此物体将会在渲染中被忽略。

结束语


Unity3D拥有着一个灵活的渲染管线和自由的材质系统。与其它图形引擎不一样的是,Unity3D针对游戏需求所留出来的接口非常好用。
而本文仅仅是对Unity3D的材质系统进行初步的描述。要想完全把Unity3D的材质系统总结出来,不是一两篇文章可以搞定的。Unity3D文档中的 Shader Reference可以作为一个十分不错的开始http://docs.unity3d.com/Manual/SL-Reference.html

posted on 2016-04-10 21:43  &大飞  阅读(186)  评论(0编辑  收藏  举报

导航