第1.3章:Cocos Shader语法详解

Cocos Shader采用了独特的YAML + GLSL混合语法,本章将深入介绍Cocos Shader的语法结构、编写规则和最佳实践,为后续的着色器开发打下坚实基础。

🎯 学习目标

通过本章学习,你将掌握:

  • Cocos Shader的整体语法结构
  • CCEffect配置的详细语法
  • CCProgram着色器程序的编写规则
  • 属性定义和传递机制
  • 内置函数和宏的使用方法

🏗️ Cocos Shader整体结构

基本文件结构

Cocos Shader文件(.effect)由三个主要部分组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. CCEffect配置段
CCEffect:
techniques:
- name: opaque
passes:
- vert: vs-main
frag: fs-main
properties:
# 属性定义

# 2. 顶点着色器程序
CCProgram vs-main %{
// GLSL顶点着色器代码
}%

# 3. 片元着色器程序
CCProgram fs-main %{
// GLSL片元着色器代码
}%

语法特点

  1. YAML配置语法:用于定义渲染状态和属性
  2. CCProgram语法:用于编写GLSL着色器代码
  3. 预处理器支持:支持条件编译和宏定义
  4. 内置函数库:提供丰富的工具函数

📋 CCEffect配置详解

Techniques技术配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CCEffect:
techniques:
- name: opaque # 技术名称
passes: # 渲染通道列表
- vert: vs-main # 顶点着色器名称
frag: fs-main # 片元着色器名称
phase: forward # 渲染阶段
priority: 128 # 渲染优先级
stage: default # 渲染阶段标识

- name: transparent # 透明技术
passes:
- vert: vs-main
frag: fs-main
phase: forward
priority: 256
blendState: # 混合状态
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha

Pass渲染通道配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
passes:
- vert: vertex-shader-name
frag: fragment-shader-name

# 渲染状态配置
rasterizerState:
cullMode: back # 背面剔除

depthStencilState:
depthTest: true # 深度测试
depthWrite: true # 深度写入

blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha

# 动态状态
dynamics:
- name: CC_USE_HDR
type: number
value: 0

Properties属性定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
properties:
# 纹理属性
mainTexture:
value: white # 默认值
editor:
tooltip: "主纹理" # 编辑器提示

# 颜色属性
tintColor:
value: [1, 1, 1, 1] # RGBA默认值
editor:
type: color # 编辑器类型
tooltip: "着色颜色"

# 数值属性
intensity:
value: 1.0 # 默认值
range: [0, 5.0] # 取值范围
editor:
slide: true # 滑动条
tooltip: "强度"

# 向量属性
offset:
value: [0, 0] # vec2默认值
editor:
tooltip: "偏移量"

🔧 CCProgram着色器程序

顶点着色器基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CCProgram vs-main %{
precision highp float;

// 包含内置头文件
#include <builtin/uniforms/cc-global>
#include <builtin/uniforms/cc-local>

// 输入属性
in vec3 a_position; // 顶点位置
in vec3 a_normal; // 顶点法线
in vec2 a_texCoord; // 纹理坐标
in vec4 a_color; // 顶点颜色

// 输出变量
out vec3 v_worldPos; // 世界空间位置
out vec3 v_worldNormal; // 世界空间法线
out vec2 v_uv; // UV坐标
out vec4 v_color; // 顶点颜色

// 主函数
vec4 vert() {
// 计算世界空间位置
vec4 worldPos = cc_matWorld * vec4(a_position, 1.0);
v_worldPos = worldPos.xyz;

// 计算世界空间法线
v_worldNormal = normalize((cc_matWorldIT * vec4(a_normal, 0.0)).xyz);

// 传递UV和颜色
v_uv = a_texCoord;
v_color = a_color;

// 计算裁剪空间位置
return cc_matViewProj * worldPos;
}
}%

片元着色器基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CCProgram fs-main %{
precision highp float;

// 包含内置头文件
#include <builtin/uniforms/cc-global>
#include <builtin/functions/common>

// 输入变量
in vec3 v_worldPos;
in vec3 v_worldNormal;
in vec2 v_uv;
in vec4 v_color;

// Uniform块定义
uniform sampler2D mainTexture;
uniform Properties {
vec4 tintColor;
float intensity;
vec2 offset;
};

// 主函数
vec4 frag() {
// 采样主纹理
vec2 uv = v_uv + offset;
vec4 texColor = texture(mainTexture, uv);

// 应用顶点颜色和着色颜色
vec4 finalColor = texColor * v_color * tintColor;
finalColor.rgb *= intensity;

return finalColor;
}
}%

🎨 属性系统详解

属性类型支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
properties:
# 基础数值类型
floatValue:
value: 1.0
range: [0, 10]

intValue:
value: 5
range: [0, 100]

# 向量类型
vec2Value:
value: [0.5, 0.5]

vec3Value:
value: [1, 1, 1]

vec4Value:
value: [1, 1, 1, 1]

# 颜色类型
colorValue:
value: [1, 0, 0, 1]
editor:
type: color

# 纹理类型
textureValue:
value: white
editor:
tooltip: "纹理属性"

# 布尔类型
boolValue:
value: true
editor:
inspector: boolean

编辑器集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
properties:
# 滑动条控件
sliderValue:
value: 0.5
range: [0, 1]
editor:
slide: true
tooltip: "滑动条控制"

# 下拉选择
enumValue:
value: 0
editor:
inspector: enum
options:
- "选项1"
- "选项2"
- "选项3"

# 分组显示
groupedValue:
value: 1.0
editor:
group: "材质参数"
tooltip: "分组属性"

🔍 内置函数和宏

常用内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CCProgram common-functions %{
// 数学函数
float smoothstep(float edge0, float edge1, float x);
float clamp(float x, float minVal, float maxVal);
float mix(float x, float y, float a);

// 向量函数
float dot(vec3 x, vec3 y);
vec3 cross(vec3 x, vec3 y);
float length(vec3 x);
vec3 normalize(vec3 x);

// 纹理函数
vec4 texture(sampler2D sampler, vec2 coord);
vec4 textureLod(sampler2D sampler, vec2 coord, float lod);

// 矩阵变换
vec4 matrixTransform(mat4 matrix, vec4 position);
}%

内置宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 平台相关宏
#ifdef GL_ES
// WebGL/移动端代码
precision mediump float;
#else
// 桌面端代码
precision highp float;
#endif

// 功能相关宏
#ifdef CC_USE_HDR
// HDR相关代码
#endif

#ifdef CC_USE_IBL
// IBL相关代码
#endif

// 自定义宏
#define PI 3.14159265359
#define TWO_PI 6.28318530718
#define HALF_PI 1.57079632679

// 便利宏函数
#define TRANSFORM_TEX(tex, name) (tex.xy * name##_ST.xy + name##_ST.zw)

💡 高级语法特性

条件编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CCProgram conditional-compile %{
precision highp float;

#define QUALITY_LOW 0
#define QUALITY_MEDIUM 1
#define QUALITY_HIGH 2

#ifndef QUALITY_LEVEL
#define QUALITY_LEVEL QUALITY_MEDIUM
#endif

vec4 frag() {
#if QUALITY_LEVEL == QUALITY_LOW
// 低质量代码
return vec4(1.0, 0.0, 0.0, 1.0);
#elif QUALITY_LEVEL == QUALITY_MEDIUM
// 中等质量代码
return vec4(0.0, 1.0, 0.0, 1.0);
#else
// 高质量代码
return vec4(0.0, 0.0, 1.0, 1.0);
#endif
}
}%

Include机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// common/math-utils.chunk
float remap(float value, float oldMin, float oldMax, float newMin, float newMax) {
return newMin + (value - oldMin) * (newMax - newMin) / (oldMax - oldMin);
}

vec3 rgb2hsv(vec3 c) {
// RGB到HSV转换代码
}

// 在着色器中使用
CCProgram main-shader %{
#include <common/math-utils>

vec4 frag() {
float remappedValue = remap(input, 0.0, 1.0, -1.0, 1.0);
return vec4(remappedValue);
}
}%

🎮 实际应用示例

完整的Unlit着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
CCEffect %{
techniques:
- name: opaque
passes:
- vert: unlit-vs
frag: unlit-fs
properties: &props
mainTexture: { value: white }
tintColor: { value: [1, 1, 1, 1], editor: { type: color } }
alphaThreshold: { value: 0.5, range: [0, 1] }
}%

CCProgram unlit-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#include <builtin/uniforms/cc-local>

in vec3 a_position;
in vec2 a_texCoord;

out vec2 v_uv;

vec4 vert() {
vec4 position = vec4(a_position, 1);
position = cc_matWorld * position;
v_uv = a_texCoord;
return cc_matViewProj * position;
}
}%

CCProgram unlit-fs %{
precision highp float;
#include <builtin/uniforms/cc-global>

in vec2 v_uv;

uniform sampler2D mainTexture;
uniform Properties {
vec4 tintColor;
float alphaThreshold;
};

vec4 frag() {
vec4 col = texture(mainTexture, v_uv);
col *= tintColor;

// Alpha测试
if (col.a < alphaThreshold) {
discard;
}

return col;
}
}%

📝 语法规范和最佳实践

命名规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 顶点属性:a_前缀
in vec3 a_position;
in vec2 a_texCoord;

// Varying变量:v_前缀
out vec2 v_uv;
out vec3 v_worldPos;

// Uniform变量:u_前缀或无前缀
uniform float u_time;
uniform sampler2D mainTexture;

// 常量:大写+下划线
#define MAX_LIGHT_COUNT 8
#define PI 3.14159265359

性能优化建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ✅ 推荐:精度声明
precision mediump float; // 移动端使用中等精度

// ✅ 推荐:早期丢弃
vec4 frag() {
vec4 col = texture(mainTexture, v_uv);
if (col.a < 0.1) discard; // 早期丢弃透明像素
// 其他计算...
}

// ❌ 避免:在片元着色器中进行复杂计算
// 应该在顶点着色器或CPU中预计算

// ✅ 推荐:使用内置函数
float result = mix(a, b, t); // 使用内置mix函数
// float result = a + (b - a) * t; // 避免手写插值

🔧 调试技巧

可视化调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 调试模式宏
#define DEBUG_MODE 0

#if DEBUG_MODE == 1
// 显示UV坐标
return vec4(v_uv, 0.0, 1.0);
#elif DEBUG_MODE == 2
// 显示法线
return vec4(v_normal * 0.5 + 0.5, 1.0);
#elif DEBUG_MODE == 3
// 显示顶点颜色
return v_color;
#else
// 正常渲染
return finalColor;
#endif

分步验证

1
2
3
4
5
6
7
8
9
10
11
12
vec4 frag() {
// Step 1: 验证纹理采样
vec4 texColor = texture(mainTexture, v_uv);
// return texColor; // 取消注释查看纹理

// Step 2: 验证光照计算
float NdotL = max(0.0, dot(v_normal, lightDir));
// return vec4(NdotL, NdotL, NdotL, 1.0); // 查看光照

// Step 3: 最终结果
return texColor * NdotL;
}

📚 小结

本章详细介绍了Cocos Shader的语法结构:

  • CCEffect配置定义渲染技术和属性
  • CCProgram包含GLSL着色器代码
  • 丰富的属性系统支持各种数据类型
  • 内置函数和宏简化开发工作
  • 条件编译和Include机制提高代码复用性

掌握了基础语法后,接下来我们将学习如何创建和使用自定义着色器。

下一章: 第2.1章:YAML配置详解

💡 学习建议

  1. 循序渐进:先理解整体结构,再深入细节
  2. 动手实践:每个语法点都要通过代码验证
  3. 参考示例:多看内置着色器的实现
  4. 调试练习:学会使用调试技巧排查问题

🔗 参考资源

继续你的Cocos Shader学习之旅!