2D 精灵着色器:Gradient

默认的情况下,UI 和 2D 组件会使用引擎内置的着色器,这些着色器放在 资源管理器 面板的 internal -> effects 目录下,可查看 内置着色器 来了解其作用。

对于任何持有 CustomMaterial 属性的 UI 和 2D 组件,都可在 属性检查器 内通过该属性的下拉框选择或者从 资源管理器 内拖拽实现自定义材质。

引擎规定 UI 组件的自定义材质只能有一个。

内置的 UI 着色器

注意

  1. 在使用自定义材质后,可能会打断合批。
  2. 关于 UI 和 2D 组件合批的使用,可以参考:2D 渲染组件合批规则说明

本文将通过实现一个精灵的渐变着色器来演示如何为 UI 和 2D 组件使用自定义着色器。

搭建工程

通过 CocosDashBoard 创建一个新的 2D 项目。

创建一个新的场景并在场景内添加一个 Sprite:

create sprite

资源管理器 内执行以下的操作:

  • 创建名为 gradient.effect 的着色器文件
  • 拷贝 资源管理器 -> intenal -> effects 内的 builtin-sprite 着色器的内容到 gradient.effect 内
  • 创建名为 gradient.mtl 的材质并在 属性查看器 内的 Effect 栏选择 gradient.effect
  • 导入任意的纹理

材质和着色器的创建可通过在 资源管理器 内任意空白处点击鼠标右键,或单击 资源管理 上的 + 按钮

create-mtl-effect

创建好的工程如图示:

create asset

选中创建好的精灵,将 gradient.mtl 材质和导入的纹理分别赋予给精灵的对应属性:

custom-material

CCEffect

通过观察可得知,渐变可以理解为在某个轴上,随着坐标变化,颜色发生变化的现象。因此在 CCEffect 段内给着色器的 properties 增加两个颜色属性分别代表渐变的起始颜色和结束颜色。

  1. startColor: { value: [1.0, 1.0, 1.0, 1.0], # gradientA 的 RGBA 通道的默认值
  2. editor: {type: color} } # 在属性查看器内展示的样式,此处展示为颜色
  3. endColor: { value: [1.0, 1.0, 1.0, 1.0],
  4. editor: {type: color} }}

此时的 CCEffect:

  1. CCEffect %{
  2. techniques:
  3. - passes:
  4. - vert: sprite-vs:vert
  5. frag: sprite-fs:frag
  6. depthStencilState:
  7. depthTest: false
  8. depthWrite: false
  9. blendState:
  10. targets:
  11. - blend: true
  12. blendSrc: src_alpha
  13. blendDst: one_minus_src_alpha
  14. blendDstAlpha: one_minus_src_alpha
  15. rasterizerState:
  16. cullMode: none
  17. properties:
  18. alphaThreshold: { value: 0.5 }
  19. startColor: { value: [1.0, 1.0, 1.0, 1.0], editor: {type: color} }
  20. endColor: { value: [1.0, 1.0, 1.0, 1.0], editor: {type: color} }
  21. }%

注意这里定义了两个颜色值 startColorendColor,如果要将这两个颜色正确的传入给着色器片段,则需要增加对应的 Uniform。

引擎规定,不允许离散使用 Uniform,因此在 CCProgram sprite-fs 段内添加下列的代码:

  1. uniform Constant{
  2. vec4 startColor;
  3. vec4 endColor;
  4. };

此时引擎会自动将 properties 内定义的属性和 Constant 内的 Uniform 进行关联。

顶点着色器

通常不用对顶点着色器做额外的处理,因此保留系统内置的 sprite-vs

顶点着色器

片元着色器

在默认的精灵着色器内,精灵顶点的 XY 轴和纹理坐标的 UV 是对应的,因此可考虑使用纹理坐标的变化来达成渐变,在 USE_TEXTURE 宏定义的范围内增加下列代码:

  1. #if USE_TEXTURE
  2. o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
  3. #if IS_GRAY
  4. float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
  5. o.r = o.g = o.b = gray;
  6. #endif
  7. // 根据 UV 的变化来调整渐变色
  8. o.rgb *= mix(startColor, endColor, vec4(uv0.x)).rgb;
  9. #endif

勾选原有材质上的 USE_TEXTURE 选项:

use-texture

此时通过调整材质上的 startColorendColor 则可以观察到不同的渐变:

material-color

精灵着色的变化:

preview

使用预处理宏定义

在上述的片元着色器内,只考虑了纹理坐标轴 U 的渐变,为了灵活性和支持更多的功能,通过预处理宏定义来实现不同轴的渐变,因此删掉下列代码:

  1. o.rgb *= mix(startColor, endColor, vec4(uv0.x)).rgb;

添加下列代码:

  1. #if USE_HORIZONTAL
  2. o.rgb *= mix(startColor, endColor, vec4(uv0.x)).rgb;
  3. #endif
  4. #if USE_VERTICAL
  5. o.rgb *= mix(startColor, endColor, vec4(uv0.y)).rgb;
  6. #endif

这里声明了 USE_HORIZONTALUSE_VERTICAL 这两个预处理宏定义,分别代表了水平方向和垂直方向的渐变,可以方便地按需使用:

macro-preview

完整的着色器代码:

  1. // Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
  2. CCEffect %{
  3. techniques:
  4. - passes:
  5. - vert: sprite-vs:vert
  6. frag: sprite-fs:frag
  7. depthStencilState:
  8. depthTest: false
  9. depthWrite: false
  10. blendState:
  11. targets:
  12. - blend: true
  13. blendSrc: src_alpha
  14. blendDst: one_minus_src_alpha
  15. blendDstAlpha: one_minus_src_alpha
  16. rasterizerState:
  17. cullMode: none
  18. properties:
  19. alphaThreshold: { value: 0.5 }
  20. startColor: { value: [1.0, 1.0, 1.0, 1.0], editor: {type: color} }
  21. endColor: { value: [1.0, 1.0, 1.0, 1.0], editor: {type: color} }
  22. }%
  23. CCProgram sprite-vs %{
  24. precision highp float;
  25. #include <cc-global>
  26. #if USE_LOCAL
  27. #include <cc-local>
  28. #endif
  29. #if SAMPLE_FROM_RT
  30. #include <common>
  31. #endif
  32. in vec3 a_position;
  33. in vec2 a_texCoord;
  34. in vec4 a_color;
  35. out vec4 color;
  36. out vec2 uv0;
  37. vec4 vert () {
  38. vec4 pos = vec4(a_position, 1);
  39. #if USE_LOCAL
  40. pos = cc_matWorld * pos;
  41. #endif
  42. #if USE_PIXEL_ALIGNMENT
  43. pos = cc_matView * pos;
  44. pos.xyz = floor(pos.xyz);
  45. pos = cc_matProj * pos;
  46. #else
  47. pos = cc_matViewProj * pos;
  48. #endif
  49. uv0 = a_texCoord;
  50. #if SAMPLE_FROM_RT
  51. CC_HANDLE_RT_SAMPLE_FLIP(uv0);
  52. #endif
  53. color = a_color;
  54. return pos;
  55. }
  56. }%
  57. CCProgram sprite-fs %{
  58. precision highp float;
  59. #include <embedded-alpha>
  60. #include <alpha-test>
  61. in vec4 color;
  62. uniform Constant{
  63. vec4 startColor;
  64. vec4 endColor;
  65. };
  66. #if USE_TEXTURE
  67. in vec2 uv0;
  68. #pragma builtin(local)
  69. layout(set = 2, binding = 11) uniform sampler2D cc_spriteTexture;
  70. #endif
  71. vec4 frag () {
  72. vec4 o = vec4(1, 1, 1, 1);
  73. #if USE_TEXTURE
  74. o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
  75. #if IS_GRAY
  76. float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
  77. o.r = o.g = o.b = gray;
  78. #endif
  79. #if USE_HORIZONTAL
  80. o.rgb *= mix(startColor, endColor, vec4(uv0.x)).rgb;
  81. #endif
  82. #if USE_VERTICAL
  83. o.rgb *= mix(startColor, endColor, vec4(uv0.y)).rgb;
  84. #endif
  85. #endif
  86. o *= color;
  87. ALPHA_TEST(o);
  88. return o;
  89. }
  90. }%