预处理宏定义
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
才会显示在 属性检查器 面板上:
CCEffect %{
# ...
properties:
# ...
}%
// ...
vec4 frag () {
#if USE_TEXTURE
// 处理 mainTexture 逻辑
#endif
}%
虽然引擎会尝试自动识别所有出现在预处理分支逻辑中 (#if
) 的宏定义,但有时实际使用方式要比简单的布尔开关更复杂,比如下面的条件判断:
针对这类有固定取值范围或固定选项的宏定义,需要选择一个合适的 tag 显式声明:
比如声明一个 LAYERS
宏,当 tag 为 range
时,当前宏在运行时可能的取值范围为 [4, 5]
。
#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 的内置头文件中,有不少工具函数都是函数式宏定义,比如:
#pragma define CCDecode(position) \
position = vec4(a_position, 1.0)
#pragma define CCVertInput(position) \
CCDecode(position); \
#if CC_USE_SKINNING \
CCSkin(position); \
#endif \
与 C/C++ 的宏定义系统相同,这套机制不会对宏定义的 卫生情况 做任何处理,由不卫生的宏展开而带来的问题需要开发者自行处理。
因此,请谨慎定义含有局部变量的预处理宏:
注意:在 v3.5 之前,glsl 标准的
#define
语句被函数式宏定义占用,因此不能使用相关语句如#ifdef
/#ifndef
等。但是从 v3.5 开始,#define
和#ifdef
/ 等都可以正常使用了。函数式宏定义从 v3.5 开始被替换为#pragma define
,而 effect 资源升级过程中会自动被替换,在书写新的 effect 资源或使用外部不带 meta 的 effect 资源时请注意语法的正确性。