Cocos2dx-x tiledmap缝隙问题分析以及解决方案

343 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

一个三角形3个顶点,一个8个三角形,4个四边形

  • 如果瓦片在同一张纹理上不会出现裂缝问题 image.png image.png

  • 如果瓦片在不同的纹理上就会会出现裂缝问题 image.pngimage.png

tiled.tml文件内容,tmx格式

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.6.0" orientation="orthogonal" renderorder="right-down" width="2" height="1" tilewidth="32" tileheight="32" infinite="0" mapScale="1" nextlayerid="2" nextobjectid="1">
 <tileset firstgid="1" name="2" tilewidth="32" tileheight="32" tilecount="1" columns="1">
  <image source="2.png" width="32" height="32"/>
 </tileset>
 <tileset firstgid="2" name="1" tilewidth="32" tileheight="32" tilecount="1" columns="1">
  <image source="1.png" width="32" height="32"/>
 </tileset>
 <layer id="1" name="图块层 1" width="2" height="1">
  <data encoding="csv">2,1</data>
 </layer>
</map>

  • CCFastTMXTiledMap.cpp
bool TMXTiledMap::initWithTMXFile(const std::string& tmxFile)
{
    TMXMapInfo *mapInfo = TMXMapInfo::create(tmxFile);
    buildWithMapInfo(mapInfo);
    return true;
}
  • CCTMXXMLParser.cpp
bool TMXMapInfo::initWithTMXFile(const std::string& tmxFile)
{
    return parseXMLFile(_TMXFileName); // xml解析,使用的SAX解析流
}
void TMXMapInfo::startElement(void* /*ctx*/, const char *name, const char **atts)
{    
    TMXMapInfo *tmxMapInfo = this;
    std::string elementName = name;
    ValueMap attributeDict;
    if (atts && atts[0])
    {
        // 属性是一个一维数组,所以要+2
        for (int i = 0; atts[i]; i += 2)
        {
            std::string key = atts[i];
            std::string value = atts[i+1];
            attributeDict.emplace(key, Value(value));
        }
    }
}
TMXTilesetInfo
tmxMapInfo->getTilesets().pushBack(tileset);
TMXLayerInfo
tmxMapInfo->getLayers().pushBack(layer);

在endElement的时候会设置每一层的tiles
layer->_tiles
void TMXLayer::setupTiles(){
    int length = _TileSetGroup.size();
    for(int i=0; i<length; ++i){
        _TileSetGroup[i]._set->_imageSize = _TileSetGroup[i]._texture->getContentSizeInPixels();
        // 设置图集纹理参数
        _TileSetGroup[i]._texture->setAliasTexParameters();
    }
}

TMX下边会有TMXLayer这个节点 image.png

void TMXLayer::updateTotalQuads(const Rect& culledRect)


渲染命令
 std::vector<PrimitiveCommand> _renderCommands;

在线框模式下,很明显看到提交的顶点是分离的

image.png

  • CCPrimitive.cpp
void Primitive::draw(){
        if(_verts){
            _verts->use();
        }
}
  • CCVertexIndexData.cpp
void VertexData::use()
{
    uint32_t flags(0);
    for(auto& element : _vertexStreams)
    {
        flags = flags | (1 << element.second._stream._semantic);
    }
    
    GL::enableVertexAttribs(flags);

    int lastVBO = -1;
    for(auto& element : _vertexStreams)
    {
        //glEnableVertexAttribArray((GLint)element.second._stream._semantic);
        auto vertexStreamAttrib = element.second._stream;
        auto vertexBuffer = element.second._buffer;

        // don't call glBindBuffer() if not needed. Expensive operation.
        int vbo = vertexBuffer->getVBO();
        if (vbo != lastVBO) {
            // 顶点的数据来源,vertexBuffer的vbo
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer->getVBO());
            lastVBO = vbo;
        }
        glVertexAttribPointer(GLint(vertexStreamAttrib._semantic),
                              vertexStreamAttrib._size,
                              vertexStreamAttrib._type,
                              vertexStreamAttrib._normalize,
                              vertexBuffer->getSizePerVertex(),
                              (GLvoid*)((long)vertexStreamAttrib._offset));
    }
}
  • 更新顶点的逻辑
void TMXLayer::updateVertexBuffer()
{
    GL::bindVAO(0);
    if(nullptr == _vData)
    {
        _vertexBuffer = VertexBuffer::create(sizeof(V3F_C4B_T2F), (int)_totalQuads.size() * 4);
        _vData = VertexData::create();
        _vData->setStream(_vertexBuffer, VertexStreamAttribute(0, GLProgram::VERTEX_ATTRIB_POSITION, GL_FLOAT, 3));
        _vData->setStream(_vertexBuffer, VertexStreamAttribute(offsetof(V3F_C4B_T2F, colors), GLProgram::VERTEX_ATTRIB_COLOR, GL_UNSIGNED_BYTE, 4, true));
        _vData->setStream(_vertexBuffer, VertexStreamAttribute(offsetof(V3F_C4B_T2F, texCoords), GLProgram::VERTEX_ATTRIB_TEX_COORD, GL_FLOAT, 2));
        CC_SAFE_RETAIN(_vData);
        CC_SAFE_RETAIN(_vertexBuffer);
    }
    if(_vertexBuffer)
    {
        // 顶点信息来源:_totalQuads,顶点坐标没有问题
        _vertexBuffer->updateVertices((void*)&_totalQuads[0], (int)_totalQuads.size() * 4, 0);
    }
    
}

使用的shader: SHADER_NAME_POSITION_TEXTURE_COLOR

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
    gl_Position = CC_MVPMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
}

CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL

struct CC_DLL V3F_C4B_T2F
{
    /// vertices (3F)
    Vec3     vertices;            // 12 bytes

    /// colors (4B)
    Color4B      colors;              // 4 bytes

    // tex coords (2F)
    Tex2F        texCoords;           // 8 bytes
};

测试使用sprite拼接,是没有缝隙的

最终焦点聚集到了纹理参数上

image.png

GL_TEXTURE_MIN_FILTER

缩小

  • GL_NEAREST: OpenGL会选择中心点最接近纹理坐标的那个像素
  • GL_LINEAR
  • GL_NEAREST_MIPMAP_NEAREST
  • GL_LINEAR_MIPMAP_NEAREST
  • GL_NEAREST_MIPMAP_LINEAR:默认值
  • GL_LINEAR_MIPMAP_LINEAR

GL_TEXTURE_MAG_FILTER

放大

  • GL_NEAREST
  • GL_LINEAR:默认值

发现调整为LINER也没有解决缝隙的问题

cocos creator 2.4.8没有这个问题

const auto& matrixP = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

Mat4 matrixMVP = matrixP * matrixMV;

确认是MPV矩阵的问题

gl_Position = CC_MVPMatrix * a_position;

当我尝试将顶点的范围控制在[-1, 1]时,将CC_MVPMatrix矩阵移除计算过程,发现线框模式下,就没有再出现缝隙了

image.png

至此,可以确定就是MVP矩阵出现了问题。

观察有bug和没有bug的矩阵value

平移会影响12,13 image.png 缩放会影响0,10,11

image.png

再次追击问题

发现MVP矩阵都是相同的,再次验证矩阵是没有问题的

image.png 仔细观察顶点,出现缝隙时,两个四边形顶点的z是不一样,很可能问题就出现在这里,因为顶点着色器里面:

gl_Position = CC_MVPMatrix * a_position;

很可能z的不同,在经过矩阵计算后,导致x、y也发生了变化,之前的焦点一直聚集在x、y上,导致忽略了这个问题。

那么z是如何产生的呢?