携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
开局一份代码,和文章关系不大就是觉得好玩。本来也想写一下距离场的,奈何鄙人也没搞明白到足以输出的程度,暂且跳过吧。
下面要说的是图形变换,二维的,当然三维其实是一样的。
在fragmentshader中的变换和我们平常使用的模型 视图 投影 矩阵有所不同,这些矩阵都是作用在顶点上,顶点的位置变了 ,带动整个图形变化, 而在片元着色器中,我们已经改变不了顶点了,我们只能决定当前这个位置到底上什么色。
所以,一般有两种变换方式
1 变换整个画布,让画布内的图形跟着变 2 就是逆推这个点在变换之前位置。
为了方便大家编辑,下面是编辑器;
平移
先上代码,书上用的是个十字,我们简单一点用上面那个吧。稍微改造一下,关键代码如下。
vec3 shape1(float angle, vec2 st, float r) {
float c = length(st) ;
float h = angle/pi/2. + .5;
h = fract(h);
// float b = c+ 1. ;
float b = c/r+1. ;
vec3 color = vec3(h ,1.8- c*2., b );
return hsbToRgb(color);
}
void main(){
vec2 st =gl_FragCoord.xy / u_CanvasSize ;
float ratio = u_CanvasSize.x/u_CanvasSize.y ;
float t = u_Time/1000.;
st -= vec2(0.5) ;
st.x*=ratio ;
//st.x+=.5;//平移
float angle = atan(st.y,st.x);
angle+=pi;
angle *= 1.;
angle+=t/100.;
gl_FragColor = vec4(shape1(angle, st, .1), 1.0) ;
}
虽然,是用极坐标画,但这并不影响我们的笛卡尔坐标变换。
我们还是先来简单的向右平移。 st.x+=.5; 结果发现图形往左边跑了, 这是因为,直接在原坐标上的变换实际上是视口的变换, +.5实际上就是把可视区域往右挪了0.5个单位,所以画布可以说是反方向往左移动。
暂时不改,我们继续在这个基础上加一个交互,那就是鼠标动画布反向动。
我已经处理好了,u_Mouse是以画布左下角为原点的物理像素坐标。 关键代码如下。
function handleMove({clientX, clientY}){
const {left,bottom} = canvas.getBoundingClientRect()
gl.uniform2fv( u_Mouse,[clientX-left ,bottom - clientY] );
}
.....
结果就是这样,但是我们会感觉只是这个圆盘在动,因为背景全白,无从参照,我们来加上一点背景。
之前的函数没限制区域,这里做一点小小的改动,圆外就是0 ,便于后面用加法合成
现在可以清晰的觉察到,是整个画布在动,而不是单独的图形在动。 美观问题等会儿再解决。
部分位移
上面是整个画布,而实际作图肯定是针对某个图形单独变换。这就是我认为第二种方法,现找到目标变换的逆。 光从结果上说,前面已经说的很明显了,要变换某个图形实际上要对当前坐标进行逆变换。
直接来说就是, 我们认为当前坐标是已经变换后的坐标,那么我们就需要知道,当前坐标在变换前是多少,这样我们就知道当前坐标该上什么色。 具体实现就是给我们造型函数传入逆变换之后的坐标,或者在造型函数里面进行逆变换。
只需要注释掉整体的坐标变化,在传参时逆变换即可。结果并不是我想要的,但是意思已经到了,圆盘在动而背景没动。 之所以图形没动,是因为我们是用极坐标绘制的,绘图的关键参数极角和极径没动,只是界限的逻辑跟着变了,所以出现这个效果
缩放
没有什么特别的,同样是预期效果的逆变换。
旋转
我们这里是极坐标,所以可以直接给极角坐加减法就能完成旋转,文章开头代码示例,其旋转就是用这种方式实现的。
然后,我们回到直角坐标系, 用旋转公式,旋转的中心点是原点。 如果要改变中心点,那就先进行位移,然后旋转,然后位移回去,但是这里恐怕不一定适用。 我们先用缩放和位移稍稍修改一下之前的函数。 把要进行的变换传给函数,在函数内部求出原坐标后再转为极坐标。
位移和缩放都用上了,至于旋转,可以直接用极坐标,这个肯定是没有意外的。
float angle = atan(st.y,st.x) ;
angle+=rotation ;
.....
vec4 color2 = shape2(st,0.4, point, t) ;
然后再试试,一般的旋转方法,那就是使用旋转公式。
mat2 mat2 rotate(float angle){
return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
}
可以看到效果完全相同,这种旋转永远是以当前的基点为基点,要想改变基点,最好是在前面的逆变换里面处理好。
最后
缩放和位移当然也可以用矩阵,不过仅仅是二维而已,个人觉得不太必要,何况位移还得多加一个齐次。
mat2 scale(vec2 scale){
return mat2(scale.x, 0.0, 0.0, scale.y);
}