预处理宏定义

    Cocos Creator 会在加载资源时收集所有在 Cocos Effect 中出现的宏定义,然后将其显示在 属性检查器 中,方便用户进行可视化调整,如下图所示:

    以使用预处理宏 为例,代码示例如下:

    宏定义的声明规则如下:

    • 所有的宏定义默认值都是 false,因此当定义一个简单宏定义(例如用于布尔开关的宏)时,无法指定其默认值,但可通过 属性检查器 或代码修改。如果设计上某些宏之间存在互斥关系(不能同时为 true),可以通过使用 tag 声明的宏来处理,详情请参考下文 Macro Tags 部分的内容。
    • 运行时会显式定义所有 Shader 中出现的自定义宏(默认值为 0),所以 除了 GLSL 语言内置宏外(GL_ 开头的 extension 等),请不要使用 #ifdef#if defined 这样的形式做判断,否则执行结果会始终为 true;
    • 运行时会对宏定义组合进行 hash 计算,目前的计算机制最多可支持 32 个布尔开关,请不要超出此限制。
    • 宏定义不仅可以应用在 CCProgram 里,控制宏定义内的代码逻辑,还可以应用在 CCEffect 中,将可编辑属性的显示状态与宏定义关联。

    如下所示,仅当 USE_TEXTURE 预处理宏开启时,mainTexture 才会显示在 属性检查器 面板上:

    1. CCEffect %{
    2. # ...
    3. properties:
    4. # ...
    5. }%
    6. // ...
    7. vec4 frag () {
    8. #if USE_TEXTURE
    9. // 处理 mainTexture 逻辑
    10. #endif
    11. }%

    虽然引擎会尝试自动识别所有出现在预处理分支逻辑中 (#if) 的宏定义,但有时实际使用方式要比简单的布尔开关更复杂,比如下面的条件判断:

    针对这类有固定取值范围或固定选项的宏定义,需要选择一个合适的 tag 显式声明:

    比如声明一个 LAYERS 宏,当 tag 为 range 时,当前宏在运行时可能的取值范围为 [4, 5]

    1. #pragma define-meta LAYERS range([4, 5])

    比如声明一个 METALLIC_SOURCE 宏,当 tag 为 options 时,当前宏在运行时可能的取值范围为 ‘r’、’g’、’b’、’a’ 四种。

    由于 WebGL 1.0 不支持函数式宏定义,因此 Creator 在 Cocos Effect 编译时支持了函数式宏定义,在输出的 Shader 中就已经将此类宏定义展开。

    这个操作对于一些需要 inline 的工具函数,或需要大量重复定义的相似代码非常有帮助。在 Cocos Effect 的内置头文件中,有不少工具函数都是函数式宏定义,比如:

    1. #pragma define CCDecode(position) \
    2. position = vec4(a_position, 1.0)
    3. #pragma define CCVertInput(position) \
    4. CCDecode(position); \
    5. #if CC_USE_SKINNING \
    6. CCSkin(position); \
    7. #endif \

    与 C/C++ 的宏定义系统相同,这套机制不会对宏定义的 卫生情况 做任何处理,由不卫生的宏展开而带来的问题需要开发者自行处理。

    因此,请谨慎定义含有局部变量的预处理宏:

    注意:在 v3.5 之前,glsl 标准的 #define 语句被函数式宏定义占用,因此不能使用相关语句如 #ifdef / #ifndef 等。但是从 v3.5 开始,#define#ifdef / 等都可以正常使用了。函数式宏定义从 v3.5 开始被替换为 #pragma define,而 effect 资源升级过程中会自动被替换,在书写新的 effect 资源或使用外部不带 meta 的 effect 资源时请注意语法的正确性。