最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 2D shader 小打小闹 之像素化

    正文概述 掘金(这是上帝的杰作)   2020-12-01   736

    万恶从马赛克说起

    说起像素,其实并不是仅仅出现在那些考研资料的视频之中,让某些器官不能清晰的呈现。其实很多时候,低清是为了性能考虑,比如我们建好了一个3D模型,摆好了镜头需要贴图了。此时距离镜头近处的贴图使用高清无码的自然无可厚非。那么远处的呢?那些可能只占画面10几个像素点的建筑,我们需要用一张1920+的贴图来绘制?这是没有必要的。

    2D shader 小打小闹 之像素化

    对于性能已经十分优良的现代设备尚且如此,在那个整个游戏容量还没有如今一个小姐姐图片大的年代(初代超级玛丽卡带容量为 40KB)更是如此,用更少的像素变现场景也成为了一种风格---“像素风”

    2D shader 小打小闹 之像素化

    其实如果你仔细看,初代马里奥为了节省空间不仅仅使用了像素风格,很多东西都是左右对称的(马里奥的身体),甚至天空的云和地上的草也只是涂上了不同颜色(这种调色的shader以后也会说到)。

    当然如今像素风早已不是为了节省空间的拖鞋,他俨然成了一种风格。比如前些人的游戏“歧路旅人”就是一款用虚幻4引擎开发的奢华像素大餐。

    2D shader 小打小闹 之像素化

    扯了这么多,让我们回到正题,今天我们要做shader就是将一张原本高清的图片像素画化。

    如何打码?

    要让图片形成马赛克的效果很容易,假设一张1920*1080的图像,那么就有1920*1080个像素点,如果4*4个像素都呈现一个颜色,图片就模糊了一点,如果扩大成8*8,图片就又模糊了一点,以此类推,这就是厚码与薄码。不过打码依旧是一门学问,就说这4*4个像素要选谁的颜色?我们可以取全部平均值,可以抽样区平均,也可以使用特殊的卷积核(这个概念之前的文章有略微提到)这是上帝的杰作:《前端图形学从入门到放弃》2.5 画皮:纹理贴图要让图片形成马赛克的效果很容易,假设一张100*100的图像,那么就有10000个像素点,如果4*4个像素都呈现一个颜色,图片就模糊了一点,如果扩大成10*10,图片就又模糊了一点,以此类推,这就是厚码与薄码。

    2D shader 小打小闹 之像素化

    不过打码依旧是一门学问,就说这4*4个像素要选谁的颜色?我们可以取全部平均值,可以抽样区平均,也可以使用特殊的卷积核(这个概念之前的文章有略微提到),不过我们今天简单粗暴一点就用像素区域左下角的颜色来填充正片区域。

    2D shader 小打小闹 之像素化

    原理解释

    当然这里最关键的就是如何像素画,我们采用floor函数来实现。我们都知道在GLSL中floor函数在[n,n+1)区间y值是唯一且递增的如图:

    2D shader 小打小闹 之像素化

    我们就可以使用这个性质来处理shader中的uv坐标:

    uv = floor(uv);
    

    然鹅这样写并没什么卵用,因为我们的uv本来就是在[0,1]之间的,所以这里我们引入浮点型step将floor形成的阶梯细分,例如:

    float step = 10.;
    y = floor(x*step)/step; 
    

    这样得到的函数如下图所示:

    2D shader 小打小闹 之像素化

    那么我们的GLSL代码:

    2D shader 小打小闹 之像素化

    这里我们使用uv的xy值绘制了一个颜色图像(step设为10即最终的图片会是一个10*10的图片):

    uv = floor(uv*10.0)/10.0;
    vec3 color = vec3(uv,1.0);
    

    代码实现:

    对于webgl我们需要实现一个物体(position)和一个贴图(uv)的绑定。

    这里需要注意webgl的空间是xyz[-1,1]而UV的坐标是[0,1],且仅需要在canvas上画出图片:

    // .......
    var positionData = [
     -1, 1, 
      1, 1, 
     -1, -1, 
     -1, -1, 
      1, 1, 
      1, -1, 
    ];
     var uvData = [
     0.0, 1.0,
     1.0, 1.0,
     0.0, 0.0,
     0.0, 0.0,
     1.0, 1.0,
     1.0, 0.0
    ];
    // ......
    var a_vert_position = gl.getAttribLocation(program, 'a_vert_position');
    gl.vertexAttribPointer(
     a_vert_position,
     2,
     gl.FLOAT,
     gl.FALSE,
     0,
     0
    );
    // ......
    var a_uv_coordinate = gl.getAttribLocation(program, 'a_uv_coordinate');
    gl.vertexAttribPointer(
     a_uv_coordinate,
     2,
     gl.FLOAT,
     gl.FALSE,
     0,
     0
    );
    // ......
    

    如上代码,我们声明一个a_vert_position用来存放位置,a_uv_coordinate用来存放纹理坐标。在shader中也有同名的两个变量。下面我们来看看顶点着色器:

        attribute vec2 a_vert_position;
        attribute vec2 a_uv_coordinate;
        varying vec2 fragColor;
        void main() {
          vec3 position = vec3(a_vert_position,1.0);
          fragColor = floor(a_uv_coordinate*1.0)/1.0;
          fragColor.y = 1.0 - fragColor.y;
          gl_Position = vec4(position,1.0);
        }
    

    其中a_vert_position是一个2维坐标所以我们拼接一个向量vec3(a_vert_position,1.0),这里的z值不论是0,1,0.5都是没有差别的。大家可以动手试试,这里我就不解释了留给大家思考。

    另外一个需要注意的是由于图片(贴图)的坐标和webgl坐标是相反的,所以需要对y轴做一个翻转fragColor.y = 1.0 - fragColor.y

    最后我们看看片段着色器:

        precision mediump float;
        uniform sampler2D sampler;
        varying vec2 fragColor;
        uniform float u_pixelate_size;
        void main() {
          vec2 uv = fragColor;
          uv = floor(uv*u_pixelate_size)/u_pixelate_size;
          vec4 color = texture2D(sampler, uv);
          gl_FragColor = color;
        }
    

    这里的fragColor是从定点着色器传递过来的,并使用一个floor函数构造uv坐标,最后我们使用texture2D方法将sampler2D的图像和uv坐标关联在一起。

    上面代码中我们传入一个变量u_pixelate_size,作为step,在js代码中实现如下:

    var u_pixelate_size = gl.getUniformLocation(program, "u_pixelate_size");
    gl.uniform1f(u_pixelate_size, 10);
    

    最终结果如下:

    2D shader 小打小闹 之像素化

    这里我们可以看到一张小骑士的图片随着u_pixelate_size改变而不断变化。到此为止,我们的像素画shader基本大功告成,但是通常我们在生产中拿到的图片都是png格式的,上例中也是。png图在处理时,会有带透明度的像素,这种白边的感觉会在像素化的过程中被放大到肉眼可见。

    优化

    因此我们需要对透明度进行一定的剔除。

    2D shader 小打小闹 之像素化

    如上图左侧是未对剔除的结果,右边是做了剔除的结果。

    优化

    代码实现也很简单。我们在js脚本中传入一个变量u_alphe_cut:

    var u_alphe_cut = gl.getUniformLocation(program, "u_alphe_cut");
    gl.uniform1f(u_alphe_cut,0.9);
    

    在片段着色器中:

    // .......
    uniform float u_alphe_cut;
    // .......
    bool clip = color.a - u_alphe_cut <=0.01 ? true : false;
    if(clip){
       color.a = 0.0;
    }
    color.rgb *= color.a;
    gl_FragColor = color;
    // .......
    

    定义一个bool值(GLSL中尽量少写if,即便要写if中的逻辑也尽量简单),如果u_alphe_cut的值大于alpha,或是两者之间的差值小于0.01,此时alpha值就变为0,之后在用color.a与rbg相乘。就实现了对alpha通道的剔除。

    2D shader 小打小闹 之像素化

    上例中我们对一个u_pixelate_size为70的像素画,传入不同的cut可以看到不同的结果。

    但这个shader还有一个缺点就是目前他只能处理正方形图片,如果图片是长宽比不同的,那么马赛克的点也是方形的:

    2D shader 小打小闹 之像素化

    如图所示,我们传入的小姐姐不是一张方形的图,像素化后每个马赛克点也不是方形的,此时如果介意我们可以对step进行处理:

    uniform vec2 u_texture_size;
    // ......
    float rate = u_texture_size.y/u_texture_size.x;
    vec2 step = vec2(u_pixelate_size,u_pixelate_size*rate);
    uv = floor(uv*step)/step;
    // ......
    

    我们传入一个记录图像长宽信息的全局变量u_texture_size = vec2(width,height)得到一个宽度和长度的比值,来修正我们的step:

    2D shader 小打小闹 之像素化

    现在我们的小姐姐就是正方形码赛克了(话说这个小姐姐也不用打马赛克吧)

    至此这个像素画shader就制作完毕了,下起我们来说说如何给2D图像加反光。


    起源地下载网 » 2D shader 小打小闹 之像素化

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元