第3.1章:创建和使用着色器
本章将手把手教你如何在Cocos Creator中创建自定义着色器,从最基础的步骤开始,到完整的着色器应用。通过实际操作,你将掌握着色器开发的完整流程。
🎯 学习目标
通过本章学习,你将掌握:
- 在Cocos Creator中创建Effect文件的方法
- 编写基础的顶点和片元着色器
- 创建和配置材质
- 将着色器应用到游戏对象上
- 调试和测试着色器效果
- 着色器的版本管理和优化
🛠️ 环境准备
开发工具要求
- Cocos Creator: 3.8.x 版本
- VSCode: 建议安装Cocos Effect扩展
- 图片编辑工具: Photoshop/GIMP(制作测试纹理)
项目设置
确保项目已正确配置:
1 2 3 4
| - 渲染管线:Built-in Forward - 图形API:根据目标平台选择 - Shader版本:300 es(推荐)
|
📁 第一步:创建着色器文件
1.1 在资源管理器中创建
- 选择目录:在
assets
目录下创建shaders
文件夹 - 右键菜单:选择”创建 → Effect”
- 命名文件:输入
MyFirstShader.effect
1.2 文件结构预览
创建后的基础结构:
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
| CCEffect %{ techniques: - name: opaque passes: - vert: vs:vert frag: fs:frag properties: mainTexture: { value: white } mainColor: { value: [1, 1, 1, 1], editor: { type: color } } }%
CCProgram 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 pos = vec4(a_position, 1); pos = cc_matViewProj * cc_matWorld * pos; v_uv = a_texCoord; return pos; } }%
CCProgram fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; uniform sampler2D mainTexture; uniform MainColor { vec4 mainColor; }; vec4 frag () { vec4 color = texture(mainTexture, v_uv); return color * mainColor; } }%
|
🎨 第二步:编写你的第一个着色器
2.1 创建简单的颜色变化效果
让我们创建一个根据时间变化颜色的着色器:
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| CCEffect %{ techniques: - name: opaque passes: - vert: rainbow-vs:vert frag: rainbow-fs:frag properties: mainTexture: { value: white } colorSpeed: { value: 1.0, editor: { range: [0, 5], step: 0.1 } } brightness: { value: 1.0, editor: { range: [0, 2], step: 0.1 } } }%
CCProgram rainbow-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; out vec3 v_worldPos; vec4 vert () { vec4 pos = vec4(a_position, 1); v_worldPos = (cc_matWorld * pos).xyz; pos = cc_matViewProj * cc_matWorld * pos; v_uv = a_texCoord; return pos; } }%
CCProgram rainbow-fs %{ precision mediump float; #include <builtin/uniforms/cc-global> in vec2 v_uv; in vec3 v_worldPos; uniform sampler2D mainTexture; uniform RainbowParams { float colorSpeed; float brightness; }; vec3 hsv2rgb(vec3 hsv) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www); return hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y); } vec4 frag () { vec4 texColor = texture(mainTexture, v_uv); float hue = fract(cc_time.x * colorSpeed + v_worldPos.x * 0.1 + v_worldPos.z * 0.1); float saturation = 0.8; float value = brightness; vec3 rainbowColor = hsv2rgb(vec3(hue, saturation, value)); vec3 finalColor = texColor.rgb * rainbowColor; return vec4(finalColor, texColor.a); } }%
|
2.2 详细解析
CCEffect部分解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| techniques: - name: opaque passes: - vert: rainbow-vs:vert frag: rainbow-fs:frag properties: mainTexture: { value: white } colorSpeed: { value: 1.0, editor: { range: [0, 5], step: 0.1 } } brightness: { value: 1.0, editor: { range: [0, 2], step: 0.1 } }
|
顶点着色器解析
1 2 3 4 5 6 7 8 9 10 11 12 13
| in vec3 a_position; in vec2 a_texCoord;
out vec2 v_uv; out vec3 v_worldPos;
vec4 vert () { vec4 pos = vec4(a_position, 1); v_worldPos = (cc_matWorld * pos).xyz; pos = cc_matViewProj * cc_matWorld * pos; v_uv = a_texCoord; return pos; }
|
片元着色器解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| vec3 hsv2rgb(vec3 hsv) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www); return hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y); }
vec4 frag () { vec4 texColor = texture(mainTexture, v_uv); float hue = fract(cc_time.x * colorSpeed + v_worldPos.x * 0.1 + v_worldPos.z * 0.1); vec3 rainbowColor = hsv2rgb(vec3(hue, 0.8, brightness)); vec3 finalColor = texColor.rgb * rainbowColor; return vec4(finalColor, texColor.a); }
|
📦 第三步:创建材质
3.1 创建Material文件
- 右键资源管理器:选择”创建 → Material”
- 命名材质:
RainbowMaterial.mtl
- 选择Effect:在Inspector中选择你的Effect文件
3.2 配置材质属性
在Material的Inspector面板中:
1 2 3 4
| - mainTexture: 拖入测试纹理 - colorSpeed: 调整颜色变化速度 (0-5) - brightness: 调整亮度 (0-2)
|
3.3 材质预览
Cocos Creator会在Material Inspector中提供实时预览,你可以:
- 调整参数查看实时效果
- 切换预览几何体(球体/立方体/平面)
- 观察参数变化对效果的影响
🎮 第四步:应用到游戏对象
4.1 创建测试场景
1 2 3 4
| 1. 新建Scene 2. 添加3D对象:Cube或Sphere 3. 确保对象有MeshRenderer组件
|
4.2 应用材质
有两种方式应用材质:
方式一:拖拽应用
1 2 3 4
| 1. 选择3D对象 2. 找到MeshRenderer组件 3. 将RainbowMaterial.mtl拖入Materials数组
|
方式二:脚本应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @ccclass('ShaderApplicator') export class ShaderApplicator extends Component { @property(Material) rainbowMaterial: Material = null!; @property(MeshRenderer) meshRenderer: MeshRenderer = null!; start() { this.meshRenderer.setMaterial(this.rainbowMaterial, 0); } switchMaterial(newMaterial: Material) { this.meshRenderer.setMaterial(newMaterial, 0); } }
|
4.3 动态参数控制
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
| @ccclass('ShaderController') export class ShaderController extends Component { @property(MeshRenderer) meshRenderer: MeshRenderer = null!; @property animateSpeed: boolean = true; private currentSpeed: number = 1.0; start() { const material = this.meshRenderer.getMaterial(0); if (material) { material.setProperty('colorSpeed', this.currentSpeed); } } update(deltaTime: number) { if (this.animateSpeed) { this.currentSpeed = Math.sin(director.getTotalTime()) * 2.0 + 2.5; const material = this.meshRenderer.getMaterial(0); if (material) { material.setProperty('colorSpeed', this.currentSpeed); } } } onSpeedSliderChanged(slider: Slider) { const material = this.meshRenderer.getMaterial(0); if (material) { material.setProperty('colorSpeed', slider.progress * 5.0); } } }
|
🔍 第五步:调试和测试
5.1 常见问题排查
编译错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 1. 查看控制台编译信息 2. 检查变量名拼写 3. 确认include路径正确 4. 验证uniform块定义
uniform UnknownBlock { float value; };
uniform RainbowParams { float colorSpeed; };
|
渲染问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@ccclass('ShaderDebugger') export class ShaderDebugger extends Component { @property(MeshRenderer) meshRenderer: MeshRenderer = null!; start() { const material = this.meshRenderer.getMaterial(0); if (material) { console.log('Material Effect:', material.effectAsset?.name); console.log('Technique Count:', material.techniqueIndex); const speed = material.getProperty('colorSpeed'); console.log('Color Speed:', speed); } } }
|
5.2 性能测试
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
| @ccclass('PerformanceTester') export class PerformanceTester extends Component { @property objectCount: number = 100; @property(Material) testMaterial: Material = null!; @property(Mesh) testMesh: Mesh = null!; start() { for (let i = 0; i < this.objectCount; i++) { this.createTestObject(i); } } createTestObject(index: number) { const node = new Node(`TestObject_${index}`); const meshRenderer = node.addComponent(MeshRenderer); meshRenderer.mesh = this.testMesh; meshRenderer.setMaterial(this.testMaterial, 0); node.position = new Vec3( (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20 ); this.node.addChild(node); } }
|
📈 第六步:优化和迭代
6.1 性能优化
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
|
precision mediump float;
vec4 frag() { float time = cc_time.x; vec3 color = vec3(sin(time), cos(time), sin(time * 2.0)); return vec4(color, 1.0); }
vec4 frag() { float time = cc_time.x; float sinTime = sin(time); vec3 color = vec3(sinTime, cos(time), sin(time * 2.0)); return vec4(color, 1.0); }
#if USE_COMPLEX_CALCULATION vec3 complexColor = calculateComplex(v_uv); #else vec3 complexColor = v_uv.xxx; #endif
|
6.2 功能扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| CCEffect %{ techniques: - name: opaque passes: - vert: rainbow-vs:vert frag: rainbow-fs:frag properties: mainTexture: { value: white } colorSpeed: { value: 1.0, range: [0, 5], step: 0.1 } brightness: { value: 1.0, range: [0, 2], step: 0.1 } # 新增属性 saturation: { value: 0.8, range: [0, 1], step: 0.1 } waveAmplitude: { value: 0.1, range: [0, 1], step: 0.01 } useWorldPosition: { value: true } # 纹理动画 uvSpeed: { value: [0, 0], editor: { tooltip: "UV scrolling speed" } } }%
|
📚 小结
通过本章学习,你已经掌握了:
- 着色器创建:从Effect文件到Material的完整流程
- 基础编程:顶点和片元着色器的编写方法
- 参数控制:静态和动态参数的配置和修改
- 应用实践:将着色器应用到实际游戏对象中
- 调试技巧:常见问题的排查和解决方法
- 性能优化:着色器的优化策略和最佳实践
这是着色器开发的基础,掌握了这些技能后,你就可以开始创建更复杂和有趣的视觉效果了。
下一章: 第3.2章:着色器材质系统
💡 实践建议
- 从简单开始:先创建简单的颜色变化效果,再逐步增加复杂度
- 多做实验:尝试修改参数,观察效果变化
- 注重性能:始终考虑着色器的性能影响
- 建立资源库:保存有用的着色器片段和函数
🔗 参考资源
继续你的着色器开发之旅,下一章我们将深入学习材质系统!