• 材质系统总览
    • EffectAsset
    • Material
    • Builtins

    材质系统总览

    材质系统控制着每个模型最终的着色流程与顺序, 在引擎内相关类间结构如下:Assets

    EffectAsset

    EffectAsset 是由用户书写的着色流程描述文件, 详细结构及书写指南可以参考这里.这里主要介绍引擎读取 EffectAsset 资源的流程:

    在编辑器导入 EffectAsset 时, 会对用户书写的内容做一次预处理, 替换 GL 字符串为管线内常量, 提取 shader 信息, 转换 shader 版本等.还以 skybox.effect 为例, 预处理输出的 EffectAsset 结构大致是这样的:

    1. {
    2. "name": "pipeline/skybox",
    3. "techniques": [
    4. {"passes":[{"rasterizerState":{"cullMode":0}, "program":"pipeline/skybox|sky-vs:vert|sky-fs:frag", "priority":245, "depthStencilState":{"depthTest":true, "depthWrite":false}}]}
    5. ],
    6. "shaders": [
    7. {
    8. "name": "pipeline/skybox|sky-vs:vert|sky-fs:frag",
    9. "hash": 1154395944,
    10. "glsl3": {
    11. "vert": "// glsl 300 es vert source, omitted here for brevity",
    12. "frag": "// glsl 300 es frag source, omitted here for brevity",
    13. },
    14. "glsl1": {
    15. "vert": "// glsl 100 vert source, omitted here for brevity",
    16. "frag": "// glsl 100 frag source, omitted here for brevity",
    17. },
    18. "builtins": {"globals":{"blocks":[{"name":"CCGlobal", "defines":[]}], "samplers":[{"name":"cc_environment", "defines":[]}]}, "locals":{"blocks":[], "samplers":[]}},
    19. "defines": [
    20. {"name":"CC_USE_HDR", "type":"boolean", "defines":[]},
    21. {"name":"USE_RGBE_CUBEMAP", "type":"boolean", "defines":[]}
    22. ],
    23. "blocks": [],
    24. "samplers": [],
    25. "dependencies": {}
    26. }
    27. ]
    28. }

    接着这个生成的 EffectAsset 正常参与标准(反)序列化流程.另外在反序列化时, 其中包含的 shaders 会被直接注册到 ProgramLib, 供运行时使用.

    Material

    Material 资源可以看成是 EffectAsset 在场景中的资源实例, 它本身的可配置参数有:

    • effectAsset 或 effectName: effect 资源引用, 使用哪个 EffectAsset 所描述的流程进行渲染? (必备)
    • technique: 使用 EffectAsset 中的第几个 technique? (默认为 0 号)
    • defines: 宏定义列表, 需要开启哪些宏定义? (默认全部关闭)
    • states: 管线状态重载列表, 对渲染管线状态 (深度模板透明混合等) 有哪些重载? (默认与 effect 声明一致)
    1. const mat = new cc.Material();
    2. mat.initialize({
    3. effectName: 'pipeline/skybox',
    4. defines: { USE_RGBE_CUBEMAP: true }
    5. });

    有了这些信息后, Material 就可以被正确初始化(标志是生成渲染使用的 Pass 对象数组), 用于具体模型的渲染了.

    根据所使用 EffectAsset 的信息, 可以进一步设置每个 Pass 的 uniform 参数等.

    1. mat.setProperty('cubeMap', someCubeMap);
    2. console.log(mat.getProperty('cubeMap') === someCubeMap); // true

    这些属性都是在材质资源对象本身内部生效, 还并不涉及场景.

    Material 通过挂载到 RenderableComponent 上与场景连接, 所有需要设定材质的 Component (ModelComponent, SkinningModelComponent等) 都继承自它.

    1. const comp = someNode.getComponent(cc.ModelComponent);
    2. comp.material = mat;
    3. comp.setMaterial(mat, 0); // 与上一行作用相同

    根据子模型的数量, RenderableComponent 也可以引用多个 Material 资源:

    1. comp.setMaterial(mat, 1); // 赋给第二个 submodel

    同一个 Material 也可挂载到任意多个 RenderableComponent 上, 一般在编辑器中通过拖拽的方式即可自动赋值. 而当场景中的某个模型的 Material 需要特化的设置, 会在从 RenderableComponent 获取 Material 时自动做拷贝实例化, 从而实现独立的定制.

    1. const comp2 = someNode2.getComponent(cc.ModelComponent);
    2. const mat2 = comp2.material; // 拷贝实例化, 接下来对 `mat2` 的修改只会影响 `comp2` 的模型

    对于一个已初始化的材质, 如果希望修改最初的基本信息, 可以直接再次调用 initialize 函数, 重新创建渲染资源.

    1. mat2.initialize({
    2. effectName: 'builtin-standard',
    3. technique: 1
    4. });

    特别地, 如果只是希望修改 defines 或 states, 我们提供更高效的直接设置接口, 只需提供相对当前值的重载即可:

    1. mat.recompileShaders({ USE_RGBE_CUBEMAP: false });
    2. mat.overridePipelineStates({ rasterizerState: { cullMode: cc.GFXCullMode.NONE } });

    每帧动态更新 uniform 值是非常常见的需求, 在类似这种需要更高效接口的情景下, 可以手动调用对应 pass 的接口:

    1. // 初始化时保存以下变量
    2. const pass = mat2.passes[0];
    3. const hColor = pass.getHandle('albedo');
    4. const color = cc.color('#dadada');
    5. // 每帧更新时:
    6. color.a = Math.sin(cc.director.getTotalFrames() * 0.01) * 127 + 127;
    7. pass.setUniform(hColor, color);

    Builtins

    编辑器内置了几种常见类型的材质,无光照的 unlit、基于物理光照的 standard、skybox、粒子、sprite 等;这里列一下最常用的 standard 各项参数的意义和用法:

    Standard

    PropertyInfo
    tilingOffset模型 UV 的平铺和偏移量,xy 对应平铺,zw 对应偏移
    albedo漫反射颜色,指定模型的主要基色
    albedoMap漫反射贴图,如果有指定,这项会和漫反射颜色相乘
    albedoScalexyz 对应模型的漫反射权重,用于控制漫反射颜色对于最终颜色的影响权重;w 通道为 alpha test 的测试阈值
    normalMap法线贴图,用于增加表面细节
    pbrParamsPBR 材质参数常量:粗糙度、金属度和 AO;每种属性具体对应的通道由 XX_CHANNEL 宏定义决定
    pbrMapPBR 材质参数贴图:粗糙度、金属度和 AO;如果有指定,这项会替代材质参数常量;每种属性具体对应的通道由 XX_CHANNEL 宏定义决定
    pbrScalePBR 材质参数的权重:粗糙度、金属度和 AO;每个分量具体对应的通道由 XX_CHANNEL 宏定义决定;但 w 通道固定为 normal map 的强度
    emissive自发光颜色,不参与光照计算模型直接发散出的颜色
    emissiveMap自发光贴图,如果有指定,这项会和自发光颜色相乘,因此需要把自发光颜色(默认是黑色)调高才会有效果
    emissiveScale自发光权重,用于控制自发光颜色的强度

    相对应的,还有控制这些参数的宏定义:

    MacroInfo
    ROUGHNESS_CHANNEL指定粗糙度的数据来源通道,默认为 r 通道
    METALLIC_CHANNEL指定金属度的数据来源通道,默认为 g 通道
    AO_CHANNEL指定 AO 的数据来源通道,默认为 b 通道
    USE_ALPHA_TEST是否开启透明测试(镂空效果)?将通过比较漫反射颜色与漫反射权重的 a 通道,决定模型的哪部分将不会被绘制
    USE_ALBEDO_MAP是否使用漫反射贴图?
    USE_NORMAL_MAP是否使用法线贴图?
    USE_PBR_MAP是否使用 PBR 材质参数贴图?
    USE_EMISSIVE_MAP是否使用自发光贴图?