shader变换(二维)

284 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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) ;
      
}

image.png 虽然,是用极坐标画,但这并不影响我们的笛卡尔坐标变换。

我们还是先来简单的向右平移。 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] );
    }
    .....
    

image.png

结果就是这样,但是我们会感觉只是这个圆盘在动,因为背景全白,无从参照,我们来加上一点背景。 image.png

之前的函数没限制区域,这里做一点小小的改动,圆外就是0 ,便于后面用加法合成

image.png 现在可以清晰的觉察到,是整个画布在动,而不是单独的图形在动。 美观问题等会儿再解决。

image.png

部分位移

上面是整个画布,而实际作图肯定是针对某个图形单独变换。这就是我认为第二种方法,现找到目标变换的逆。 光从结果上说,前面已经说的很明显了,要变换某个图形实际上要对当前坐标进行逆变换。

直接来说就是, 我们认为当前坐标是已经变换后的坐标,那么我们就需要知道,当前坐标在变换前是多少,这样我们就知道当前坐标该上什么色。 具体实现就是给我们造型函数传入逆变换之后的坐标,或者在造型函数里面进行逆变换。

只需要注释掉整体的坐标变化,在传参时逆变换即可。结果并不是我想要的,但是意思已经到了,圆盘在动而背景没动。 之所以图形没动,是因为我们是用极坐标绘制的,绘图的关键参数极角和极径没动,只是界限的逻辑跟着变了,所以出现这个效果

image.png

image.png

缩放

image.png 没有什么特别的,同样是预期效果的逆变换。

image.png

旋转

我们这里是极坐标,所以可以直接给极角坐加减法就能完成旋转,文章开头代码示例,其旋转就是用这种方式实现的。

然后,我们回到直角坐标系, 用旋转公式,旋转的中心点是原点。 如果要改变中心点,那就先进行位移,然后旋转,然后位移回去,但是这里恐怕不一定适用。 我们先用缩放和位移稍稍修改一下之前的函数。 把要进行的变换传给函数,在函数内部求出原坐标后再转为极坐标。

image.png

image.png

位移和缩放都用上了,至于旋转,可以直接用极坐标,这个肯定是没有意外的。

  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));
}

可以看到效果完全相同,这种旋转永远是以当前的基点为基点,要想改变基点,最好是在前面的逆变换里面处理好。 image.png

最后

缩放和位移当然也可以用矩阵,不过仅仅是二维而已,个人觉得不太必要,何况位移还得多加一个齐次。

mat2 scale(vec2 scale){
      return mat2(scale.x, 0.0, 0.0, scale.y);
}