cocosCreator Shader 扑克牌的透视效果

我这里用的是cocos creator 3.7.2版本,不同版本可能存在差异

顶点着色器

开始实现这个功能,对定点着色器应该有了基本的了解,这里不过多说明。
这里我们先看下cocosCreator默认使用的着色器代码:builtin-sprite.effect,可以直接在资源管理器中搜索

想要达到效果,其实修改就是扑克牌上边缘两个顶点的x轴坐标

但是想要修改,最起码得知道现在的值,做个小测试看一下。让顶点坐标的x值+=y值,观察x坐标的变化规律.
复制粘贴builtin-sprite.effect文件(必须要复制粘贴,系统最开始的不能被修改),在复制出来的文件中搜索vec4 pos = vec4(a_position, 1)
并在该代码后面添加代码pos.x += pos.y;,如下:

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

这里可以看到,蓝色框区域是牌原本显示的边框,底边的x轴没有变化,所以底边的+=pos.y这里的y值为0。

调整下牌的位置,让牌的左上角顶点和画布左上角顶点重合
从图上可以看出,当牌的左上角顶点和画布左上角顶点重合时,上边两个顶点的值往右偏了屏幕高度那么多的值。看下蓝框,很直观,一个正方形,偏移宽度=屏幕高度。

这说明顶点在画布上边缘位置时,顶点坐标y值是画布高。
这里测试x,y也是一样的(自己可以动手试下)其实片段着色器中我们拿到的a_position就是一个屏幕左下角(0,0) 到 屏幕右上角(画布宽,画布高)这样的vec2变量,有了定点的具体值,后面就好操作了。来看下顶点的屏幕坐标:

顶部偏移

我来定一个顶点point,我要让所有的扑克都往这个点变形。设定point坐标vec2(屏幕宽/2,屏幕高0.85).
对每个点来说,点的x轴偏移值 = `(顶点x和pointx差值)
(顶点y和point.y的比例)
把前面的pos.x += pos.y;代码改为下面2行:

1
2
vec2 point = vec2(1920. * .5,1080. * 0.85);
pos.x += (point.x - pos.x) * (pos.y / point.y);

图片会往箭头所指的方向偏移。

修改起点y值

有个问题,就是这里的计算 都是用屏幕底边y值0计算的,我牌桌变如果不在屏幕底边咋办呢,我要指定一个起点的y值可以计算这个三角形。

比如我牌摆在离底边100px的位置,将参与计算的y坐标 减100就行了。

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

shader代码

放一下最终代码,让起点y值和终点坐标都用可变变量,从材质传值进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- passes:
- vert: sprite-vs:vert
frag: sprite-fs:frag
depthStencilState:
depthTest: false
depthWrite: false
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
alphaThreshold: { value: 0.5 }
u_point: { value: [1,1] }
u_starty: {value: 0 }
}%

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
71
72
73
74
75
CCProgram sprite-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#include <builtin/internal/embedded-alpha>
#if USE_LOCAL
#include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
#include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;

out vec4 color;
out vec2 uv0;
uniform Constant{
vec2 u_point; // 自己定义的顶点
float u_starty; // 扑克牌底边离屏幕下边的距离
};
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;
#if SAMPLE_FROM_RT
CC_HANDLE_RT_SAMPLE_FLIP(uv0);
#endif
color = a_color;

return pos;
}
}%

CCProgram sprite-fs %{
precision highp float;
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>

in vec4 color;

#if USE_TEXTURE
in vec2 uv0;
#pragma builtin(local)
layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;
#endif

vec4 frag () {
vec4 o = vec4(1, 1, 1, 1);

#if USE_TEXTURE
o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
#if IS_GRAY
float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
o.r = o.g = o.b = gray;
#endif
#endif

o *= color;
ALPHA_TEST(o);
return o;
}
}%

使用方法

在cocos creator 中找到精灵默认使用的材质ui-sprite-material.mtl,将材质拷贝出来放在自己的资源文件夹下(必须拷贝出来,不然无法修改)。将上面代码保存为gradient.effect,名字按照自己的想法来。最后修改下面4个地方即可

  1. 将精灵的”CustomMaterial”属性设置为你刚刚拷贝出来的材质
  2. 将材质的Effect属性设置为刚刚保存的gradient.effectshader文件
  3. 这里USE TEXTURE一定要勾选上,不然没有纹理,只会显示一张空白图
  4. 调整这三个参数,获得自己想要的透视效果