2D Matrices(二维矩阵)
前面章节
平移
之前的章节我们学习了如何制作一些图形 - 而如何移动它们的技巧则是借助移动它们自身的参考坐标系。我们只需要给 st 变量加上一个包含每个片段的位置的向量。这样就移动了整个坐标系。
还是画着比较更容易解释,如上图所示:
- 取消下面代码中的注释( color = vec3(st.x,st.y,0.0);),看下坐标空间是如何平移的。
// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
//生成一个长方形
float box(in vec2 _st, in vec2 _size){
//这里得到盒子的点的坐标
_size=vec2(0.5)-_size*0.5;
//差值平滑,如果st小于_size,则返回0,如果大于_size+0.001,则返回1
vec2 uv=smoothstep(_size,_size+vec2(0.001),_st);
//
uv*=smoothstep(_size,_size+vec2(0.001),vec2(1.0)-_st);
//x,y的值都是0,1。都为0才是0,都为1才是1
return uv.x*uv.y;
}
//生成横的长方形和竖的长方形
float cross(in vec2 _st, float _size){
return box(_st, vec2(_size,_size/4.)) +
box(_st, vec2(_size/4.,_size));
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// To move the cross we move the space
vec2 translate = vec2(cos(u_time),sin(u_time));
st += translate*0.35;
// Show the coordinates of the space on the background
// color = vec3(st.x,st.y,0.0);
// Add the shape on the foreground
color += vec3(cross(st,0.25));
gl_FragColor = vec4(color,1.0);
}
位移的代码主要是通过下面两句代码
vec2 translate = vec2(cos(u_time),sin(u_time));
st += translate*0.35;
我们根据时间来改变其位移,通过sin,cos获取从0-1的坐标范围。然后赋值给st,其实就是把我们生成的盒子的中心点改变,使其根据函数来变化其坐标从而达到绕着圆位移的作用,我们在第一句话加点参数
vec2 translate=vec2(cos(speedcc_time.x),sin(speedcc_time.x))*translateRadius;
其中speed是位移的速度,translateRadius就是围绕圆的半径
现在尝试下下面的练习:
- 结合 u_time 和造型函数来移动十字,并试着让它有趣一点。找一个你觉得你感兴趣的某种运动形式,让这个十字也这样运动。记录“真实世界”的一些现象或许对你有所启发 — 可以是波的运动,摆动,弹球,汽车的加速运动,一辆自行车的刹车。
旋转
要移动物体,我们同样需要移动整个空间(坐标)系统。为此我们将使用一个矩阵。矩阵是一个通过行和列定义的一组数。用矩阵乘以一个向量是用一组精确的规则定义的,这样做是为了以一组特定的方式来改变向量的值。
GLSL本身支持2维,3维和4维方阵(mm矩阵):mat2 (2x2), mat3 (3x3) 和 mat4 (4x4)。GLSL同样支持矩阵相乘 ()和特殊矩阵函数([matrixCompMult()
](…/glossary/?search=matrixCompMult))。
基于矩阵的特性,我们便有可能构造一个矩阵来产生特定的作用。比如我们可以用一个矩阵来平移一个向量:
更有趣的是,我们可以用矩阵来旋转坐标系统:
看下下面构成2维旋转的矩阵的代码。这个函数根据上面的公式,将二维向量绕 vec2(0.0) 点旋转。
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
上面是一个2d旋转的矩阵公式,我在这里就不详细展开来说了。
根据以往我们画形状的方式,这并不是我们想要的。我们的十字是画在画布中心的,对应于点 vec2(0.5) 。所以,再旋转坐标空间之前,我们需要先把图形移到中心点,坐标 vec2(0.0) ,再旋转坐标空间,最后在移动回原点。
就像下面的代码:
// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_resolution;
uniform float u_time;
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
float box(in vec2 _st, in vec2 _size){
_size = vec2(0.5) - _size*0.5;
vec2 uv = smoothstep(_size,
_size+vec2(0.001),
_st);
uv *= smoothstep(_size,
_size+vec2(0.001),
vec2(1.0)-_st);
return uv.x*uv.y;
}
float cross(in vec2 _st, float _size){
return box(_st, vec2(_size,_size/4.)) +
box(_st, vec2(_size/4.,_size));
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
// move space from the center to the vec2(0.0)
st -= vec2(0.5);
// rotate the space
st = rotate2d( sin(u_time)*PI ) * st;
// move it back to the original place
st += vec2(0.5);
// Show the coordinates of the space on the background
// color = vec3(st.x,st.y,0.0);
// Add the shape on the foreground
color += vec3(cross(st,0.4));
gl_FragColor = vec4(color,1.0);
}
我们可以把旋转的代码与位移结合起来,在cocos上的代码就是如此:
coffeescript
vec2 translate=vec2(cos(speed*cc_time.x),sin(speed*cc_time.x))*translateRadius;
st+=translate;
st-=vec2(.5);
st=rotate2d(sin(cc_time.x)*PI)*st;
st+=vec2(.5);
_color = vec3(st.x,st.y,0.0);
float t=cross2(st,0.2);
_color+=vec3(t);
gl_FragColor = vec4(_color,1.);
缩放
我们看到了如何用矩阵平移和旋转物体。(或者更准确的说,如何通过变换坐标系统来旋转和移动物体。)如果你用过3D建模软件或者 Processing中的 pushmatrix 和 popmatrix 函数,你会知道矩阵也可以被用来缩放物体的大小。
根据上面的公式,我们知道如何构造一个2D缩放矩阵:
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}
// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_resolution;
uniform float u_time;
mat2 scale(vec2 _scale){
return mat2(_scale.x,0.0,
0.0,_scale.y);
}
float box(in vec2 _st, in vec2 _size){
_size = vec2(0.5) - _size*0.5;
vec2 uv = smoothstep(_size,
_size+vec2(0.001),
_st);
uv *= smoothstep(_size,
_size+vec2(0.001),
vec2(1.0)-_st);
return uv.x*uv.y;
}
float cross(in vec2 _st, float _size){
return box(_st, vec2(_size,_size/4.)) +
box(_st, vec2(_size/4.,_size));
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
st -= vec2(0.5);
st = scale( vec2(sin(u_time)+1.0) ) * st;
st += vec2(0.5);
// Show the coordinates of the space on the background
// color = vec3(st.x,st.y,0.0);
// Add the shape on the foreground
color += vec3(cross(st,0.2));
gl_FragColor = vec4(color,1.0);
}
同样的,我们把位移旋转和缩放的效果放在一起,看下其效果
/**********位移操作*******/
vec2 translate=vec2(cos(speed*cc_time.x),sin(speed*cc_time.x))*translateRadius;
st+=translate;
/**********旋转缩放操作*******/
st-=vec2(.5);
st=rotate2d(sin(cc_time.x)*PI)*st;
st=scale(vec2(sin(cc_time.x)+1.))*st;
st+=vec2(.5);
_color = vec3(st.x,st.y,0.0);
float t=cross2(st,0.2);
_color+=vec3(t);
gl_FragColor = vec4(_color,1.);
Other uses for matrices: YUV color 矩阵的其他应用:YUV 颜色
YUV 是个用来模拟照片和视频的编码的色彩空间。这个色彩空间考虑人类的感知,减少色度的带宽。
下面的代码展现一种利用GLSL中的矩阵操作来切换颜色模式的有趣可能。
// Author @patriciogv - 2015
// http://patriciogonzalezvivo.com
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
// YUV to RGB matrix
mat3 yuv2rgb = mat3(1.0, 0.0, 1.13983,
1.0, -0.39465, -0.58060,
1.0, 2.03211, 0.0);
// RGB to YUV matrix
mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722,
-0.09991, -0.33609, 0.43600,
0.615, -0.5586, -0.05639);
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
// UV values goes from -1 to 1
// So we need to remap st (0.0 to 1.0)
st -= 0.5; // becomes -0.5 to 0.5
st *= 2.0; // becomes -1.0 to 1.0
// we pass st as the y & z values of
// a three dimensional vector to be
// properly multiply by a 3x3 matrix
color = yuv2rgb * vec3(0.5, st.x, st.y);
gl_FragColor = vec4(color,1.0);
}
正如你所见,我们用对向量乘以矩阵的方式对待色彩。用这种方式,我们“移动”这些值。
这章我们学习如何运用矩阵变换来移动,旋转和缩放向量。除了之前章节学的图形,这些变换是创作的基础。在接下来的章节我们会应用我们所学的制作漂亮的程序纹理。你会发现编程的重复性和多样性是种令人兴奋的实践。
源代码:
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
texture: { value: white }
alphaThreshold: { value: 0.5 }
translateRadius: {value: 0.5 }
speed: {value: 1.0}
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
#include <cc-local>
in vec3 a_position;
in vec4 a_color;
out vec4 v_color;
#if USE_TEXTURE
in vec2 a_uv0;
out vec2 v_uv0;
#endif
void main () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
v_uv0 = a_uv0;
#endif
v_color = a_color;
gl_Position = pos;
}
}%
CCProgram fs %{
precision highp float;
#include <alpha-test>
#include <texture>
#include <cc-local>
#include <cc-global>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
uniform ScriptVal{
float translateRadius;
float speed;
};
float PI=3.1415726;
mat2 scale(vec2 _scale)
{
return mat2(_scale.x,0.,0.,_scale.y);
}
mat2 rotate2d(float angle)
{
return mat2(cos(angle),-sin(angle),sin(angle),cos(angle));
}
float box(vec2 _st,vec2 _size)
{
//这里得到盒子的点的坐标
_size=vec2(0.5)-_size*0.5;
//差值平滑,如果st小于_size,则返回0,如果大于_size+0.001,则返回1
vec2 uv=smoothstep(_size,_size+vec2(0.001),_st);
//
uv*=smoothstep(_size,_size+vec2(0.001),vec2(1.0)-_st);
//x,y的值都是0,1。都为0才是0,都为1才是1
return uv.x*uv.y;
}
float cross1(vec2 _st,float _size)
{
return box(_st,vec2(_size,_size/4.));
}
float cross2(vec2 _st,float _size)
{
return box(_st,vec2(_size,_size/4.))+box(_st,vec2(_size/4.,_size));
}
void main () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
CCTexture(texture, v_uv0, o);
#endif
o *= v_color;
ALPHA_TEST(o);
vec2 st=v_uv0;
vec3 _color=vec3(0.5);
vec2 translate=vec2(cos(speed*cc_time.x),sin(speed*cc_time.x))*translateRadius;
// translate=vec2(abs(cos(2.*cc_time.x)),abs(sin(2.*cc_time.x)));
st+=translate;
st-=vec2(.5);
st=rotate2d(sin(cc_time.x)*PI)*st;
st=scale(vec2(sin(cc_time.x)+1.))*st;
st+=vec2(.5);
_color = vec3(st.x,st.y,0.0);
float t=cross2(st,0.2);
_color+=vec3(t);
gl_FragColor = vec4(_color,1.);
}
}%