2D Sprite Effect:Gradient

By default, UI and 2D components use the engine’s built-in shaders, which are placed in the internal -> effects directory of the Assets panel.

For any UI and 2D component that holds a CustomMaterial property, custom materials can be implemented within the Inspector panel by selecting from the drop-down box for that property or by dragging and dropping from within the Assets panel.

Engine specifies that there can be only one custom material for UI/2D components.

built-in ui effect

Note.

  1. The batching may be interrupted after using custom materials.
  2. For UI and 2D component batching, please refer to: 2D Renderable Components Batching Guidelines

This article will demonstrate how to use custom shaders for UI and 2D components by implementing a gradient shader for sprites.

Project creation

Create a new 2D project via CocosDashBoard.

And Create a new scene and add a Sprite to the scene.

create sprite

Perform the following actions within Assets panel.

  • Create a effect file named gradient.effect;
  • Copy the contents of the builtin-sprite shader in Assets -> intenal -> effects to gradient.effect;
  • Create a material named gradient.mtl and select gradient.effect in the Effect column of the Inspector;
  • Import any valid texture.

Materials and shaders can be created by right-clicking anywhere in Assets panel, or by clicking the + button on Assets panel.

create-mtl-effect

The created project is shown in the figure below:

create asset

Select the created sprite and assign the gradient.mtl material and the imported texture to the corresponding properties of the sprite.

custom-material

CCEffect

By observation, a gradient can be understood as a change in color on an axis as the coordinates change. Therefore, we add two color properties to the properties of the shader in the CCEffect paragraph to represent the start color and end color of the gradient.

  1. startColor: { value: [1.0, 1.0, 1.0, 1.0], # The default value for the RGBA channel of gradientA
  2. editor: {type: color} } # The style displayed in the Inspector, shown here as a color
  3. endColor: { value: [1.0, 1.0, 1.0, 1.0],
  4. editor: {type: color} }}

The CCEffect code is as follows:

  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. }%

Note that two color values startColor and endColor are defined here, and the corresponding Uniform needs to be added if these two colors are to be properly passed to the shader fragment.

Engine states that discrete use of Uniform is not allowed, so add the following code to the CCProgram sprite-fs segment.

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

In this case, the engine will automatically associate the properties defined in properties with the Uniform in Constant.

Vertex shader

Additional processing of vertex shaders is usually not needed, so the system’s built-in sprite-vs is retained.

vs

Fragment shader

Within the default sprite shader, the XY axis of the sprite vertices corresponds to the UV of the texture coordinates, so consider using a change in texture coordinates to reach a gradient by adding the following code to the scope defined by the USE_TEXTURE macro:

  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. // Adjust the gradient color according to the UV change
  8. o.rgb *= mix(startColor, endColor, vec4(uv0.x)).rgb;
  9. #endif

Check the USE_TEXTURE option on the original material.

use-texture

By adjusting the startColor and endColor on the material, different gradients can be observed: startColor and endColor.

material-color

The color the sprite is as follows:

preview

Use preprocessing macros to define

Within the above fragment shader, only the gradient of the texture coordinate axis U is considered. For flexibility and to support more functionality, the gradient of the different axes is implemented through preprocessing macro definitions, so the following code is deleted.

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

And add the following code:

  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

The preprocessing macros USE_HORIZONTAL and USE_VERTICAL are declared here, representing horizontal and vertical gradients respectively, and can be conveniently used on demand.

macro-preview

The complete effect code is as follows:

  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 <builtin/uniforms/cc-global>
  26. #if USE_LOCAL
  27. #include <builtin/uniforms/cc-local>
  28. #endif
  29. #if SAMPLE_FROM_RT
  30. #include <common/common-define>
  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 <builtin/internal/embedded-alpha>
  60. #include <builtin/internal/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. }%