Nanfeng

Notes on software development, code, and curious ideas

Creating a Playing-Card Perspective Effect with a Cocos Creator Shader

This example uses Cocos Creator 3.7.2. Shader syntax and built-in assets may differ in other versions.

The original screenshots were hosted by a third party and are no longer available.

Understanding the vertex transformation

Start with a copy of Cocos Creator’s builtin-sprite.effect. Do not edit the engine’s original built-in file. Find:

1
vec4 pos = vec4(a_position, 1);

As a quick experiment, add the vertex’s Y coordinate to X:

1
2
vec4 pos = vec4(a_position, 1);
pos.x += pos.y;

The lower edge remains in place when its Y coordinate is zero, while the upper edge shifts. This demonstrates that a_position can be used to deform the sprite according to its screen-space height.

Make the top edge converge

Choose a convergence point near the upper center of a 1920 × 1080 canvas:

1
2
vec2 point = vec2(1920.0 * 0.5, 1080.0 * 0.85);
pos.x += (point.x - pos.x) * (pos.y / point.y);

For each vertex, the horizontal displacement is the difference between its X coordinate and the target X, scaled by the ratio between the vertex height and the target height. Upper vertices move more than lower vertices, creating the perspective effect.

Use a different baseline

If the cards begin 100 pixels above the bottom of the canvas, subtract that baseline from the Y coordinate:

1
pos.x += (point.x - pos.x) * ((pos.y - 100.0) / point.y);

Expose the values as material properties

Add the target point and baseline to the effect properties:

1
2
3
4
properties:
alphaThreshold: { value: 0.5 }
u_point: { value: [1, 1] }
u_starty: { value: 0 }

Use them in the vertex program:

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
uniform Constant {
vec2 u_point; // Perspective convergence point
float u_starty; // Distance from the canvas bottom to the card baseline
};

vec4 vert () {
vec4 pos = vec4(a_position, 1);
pos.x += (u_point.x - pos.x) * ((pos.y - u_starty) / u_point.y);

#if USE_LOCAL
pos = cc_matWorld * pos;
#endif

#if USE_PIXEL_ALIGNMENT
pos = cc_matView * pos;
pos.xyz = floor(pos.xyz);
pos = cc_matProj * pos;
#else
pos = cc_matViewProj * pos;
#endif

uv0 = a_texCoord;
color = a_color;
return pos;
}

The fragment program can remain the standard sprite texture and alpha-test implementation.

Apply the effect

  1. Find ui-sprite-material.mtl, copy it into your own assets folder, and assign the copy to the sprite’s Custom Material property.
  2. Save the custom shader as an effect file such as gradient.effect.
  3. Set the copied material’s Effect property to that file.
  4. Enable USE TEXTURE; otherwise the sprite texture will not be displayed.
  5. Adjust u_point and u_starty until the cards have the desired perspective.
+