CocosCreator transform atlas uv coord into frame uv coord in shader

"开发笔记"

Posted by A-SHIN on March 17, 2019

“Yeah It’s on. ”

前言

shader编写时很多效果会用到uv坐标,creator中uv左上角为(0,0),右下角为(1,1)。然而如果我们使用的不是单独的texture,而是TexturePacker这些三方软件打出来的图集或者使用了creator的自动图集功能。那么shader中获取的内置uv0变量表示的是该像素在图集中的坐标,并不是在该spriteframe中的坐标。此时左上,右下uv并不是(0,0),(1,1).从而造成错误的效果。本篇介绍如何在任何情况下(使用图集(包含旋转)或非图集)在shader中得到标准uv坐标(左上(0,0),右下(1,1))。

正文

思路

  • application阶段,通过spriteFrame中的rect信息获取该frame在图集中的偏移位置及大小信息。然后根据frame是否旋转计算出该frame在图集中的归一化的上下左右偏移率,将是否旋转和上下左右偏移率传给shader进行下一步处理。
  • shader阶段,获取上阶段传入的变量。根据偏移率可将图集中的uv0坐标转换到SpriteFrame下的标准uv坐标,如果图集中的该frame进行过旋转,则还需进行逆旋转uv转化。最后便得到了我们想要的frame uv坐标。
偏移率计算
setUVoffset(frame: cc.SpriteFrame)
{
    let rect = frame.getRect();
    let texture = frame.getTexture();
    let texw = texture.width;
    let texh = texture.height;
    let l = 0, r = 0, b = 1, t = 1;
    l = texw && rect.x / texw;
    t = texh && rect.y / texh;
    if(frame.isRotated())
    {
        r = texw && (rect.x+rect.height)/texw;
        b = texh && (rect.y+rect.width)/texh;
    }
    else
    {
        r = texw && (rect.x+rect.width)/texw;
        b = texh && (rect.y+rect.height)/texh;
    }
    this._UVoffset.x = l;
    this._UVoffset.y = t;
    this._UVoffset.z = r;
    this._UVoffset.w = b;
    this._rotated = frame.isRotated() ? 1.0 : 0.0;
    
    this._effect.setProperty('UVoffset', this._UVoffset);
    this._effect.setProperty('rotated', this._rotated);
}
atlas uv转换frame uv
vec2 UVnormalize;
UVnormalize.x = (uv0.x-UVoffset.x)/(UVoffset.z-UVoffset.x);
UVnormalize.y = (uv0.y-UVoffset.y)/(UVoffset.w-UVoffset.y);
旋转uv转换
if(rotated > 0.5)
{
    float temp = UVnormalize.x;
    UVnormalize.x = UVnormalize.y;
    UVnormalize.y = 1.0 - temp;
}
示例展示


后记

示例源码点此下载