Javascript webgl tilemap渲染不正确的UV计算

Javascript webgl tilemap渲染不正确的UV计算,javascript,webgl,Javascript,Webgl,我正在尝试在单个四边形上渲染平铺贴图 我的方法使用一种“瓷砖贴图”纹理,其中每个像素在瓷砖集中存储瓷砖的X和Y索引 渲染片段时,其目的是: 使用顶点纹理坐标对“平铺贴图”纹理进行采样 从纹理的R和G通道检索X和Y索引 计算选定瓷砖的UV 使用UV对纹理图集进行采样 我在工作上遇到了问题 以下是我试图用于渲染此对象的着色器代码: 顶点 #版本300 es 精密中泵浮子; 统一mat4视图; 均匀mat4反射; 统一mat3模型; vec2位置中的布局(位置=0); vec2 aTEXCOORD中的

我正在尝试在单个四边形上渲染平铺贴图

我的方法使用一种“瓷砖贴图”纹理,其中每个像素在瓷砖集中存储瓷砖的X和Y索引

渲染片段时,其目的是:

  • 使用顶点纹理坐标对“平铺贴图”纹理进行采样
  • 从纹理的R和G通道检索X和Y索引
  • 计算选定瓷砖的UV
  • 使用UV对纹理图集进行采样
  • 我在工作上遇到了问题

    以下是我试图用于渲染此对象的着色器代码:

    顶点

    #版本300 es
    精密中泵浮子;
    统一mat4视图;
    均匀mat4反射;
    统一mat3模型;
    vec2位置中的布局(位置=0);
    vec2 aTEXCOORD中的布局(位置=1);
    出vec2 vTEXCOORD;
    void main()
    {
    //翻转uv并将其传递给碎片着色器
    vTEXCOORD=vec2(aTEXCOORD.x,1.0f-aTEXCOORD.y);
    //变换顶点位置
    vec3转化=uMODEL*vec3(位置,1.0);
    gl_Position=uPROJECTION*uVIEW*vec4(transformed.xy,0.0,1.0);
    }
    
    碎片

    #版本300 es
    精密中泵浮子;
    精密医疗泵usampler2D;
    统一的usampler2D uMAP;
    均匀采样;
    均匀的vec2 uATLAS_尺寸;
    在vec2 vTEXCOORD中;
    RAG的vec4;
    void main()
    {
    //“平铺贴图”纹理示例
    vec4数据=vec4(纹理(uMAP,vTEXCOORD));
    //计算紫外线
    vec2 uv=(data.xy*32.0/uATLAS_尺寸)+(vTEXCOORD*32.0/uATLAS_尺寸);
    //试一下瓷砖
    oFRAG=纹理(uATLAS,uv);
    }
    
    我相信这就是罪魁祸首:

    vec2 uv=(data.xy*32.0/uATLAS_尺寸)+(vTEXCOORD*32.0/uATLAS_尺寸);
    
    这里的公式是
    uv=(平铺xy指数*平铺大小)+(texcoord*平铺大小)
    ,其中:

    • texcoord
      是顶点uv(标准[0,1]、[0,0]、[1,0]、[1,1])
    • tile\u xy\u索引
      是tile集合中tile的X,Y坐标
    • tile\u size
      是tileset中一个tile的规格化大小
    因此,如果我有值
    texcoord=(0,0)
    tile\u xy\u index=(7,7)
    tile\u size=(32/1024,32/1024)
    ,那么这个片段的UV应该是
    (0.21875,0.21875)
    ,如果
    texcoord=(1,1)
    ,那么它应该是
    (0.25,0.25)
    。这些值对我来说似乎是正确的,为什么它们会产生错误的结果,以及如何修复它?

    以下是一些额外的上下文:

    • 平铺贴图纹理(夸张的颜色):

    • 预期结果(减去网格线):

    • 实际结果:

    代码将这两行中的3个内容合并在一起

    // sample "tile map" texture
    vec4 data = vec4(texture(uMAP, vTEXCOORD));
    // calculate UV
    vec2 uv = (data.xy * 32.0 / uATLAS_SIZE) + (vTEXCOORD * 32.0 / uATLAS_SIZE);
    // sample the tileset
    
    第一个,对于您正在绘制的整个四边形,将覆盖整个平铺贴图。那可能不是你想要的。通常,使用tilemap的应用程序希望显示它的一部分,而不是全部

    第二个问题是第二行需要知道一个平铺将覆盖多少像素,而不是一个平铺是多少像素。换句话说,如果您有一个32x32平铺,并且您以4x4像素绘制它,那么您的纹理坐标需要在该平铺上以4像素(而不是32像素)从0.0变为1.0

    第三个问题是除以32不会穿过32像素的平铺,除非纹理上有32个平铺。假设您有一个32x32像素的平铺,但平铺集中有8x4个平铺。您需要从0到1跨越1/8和1/4,而不是跨越1/32

    它有效地使用了2个矩阵。绘制四边形可以旋转、缩放、3D投影等,但是对于平铺贴图来说,通常的做法是绘制一个覆盖画布的四边形

    第二个是纹理矩阵(或瓷砖矩阵),其中每个单元为1块瓷砖。因此,给定一个0到1的四边形,计算一个矩阵,将该四边形展开并旋转到上面的四边形上

    假设你不旋转,你仍然需要决定在四边形上画多少块瓷砖。如果要在四边形上放置4个平铺,向下放置3个平铺,则应将比例设置为x=4和y=3

    这样,每个磁贴在其自己的空间中都会自动从0变为1。或者换一种说法,平铺2x7从U中的2.03.0和V中的7.08.0开始。然后,我们可以从地图平铺2,7中查找,并使用
    fract
    在平铺在四元空间中占据的空间中覆盖该平铺

    const vs=`#版本300 es
    精密中泵浮子;
    统一mat4视图;
    均匀mat4反射;
    统一mat4模型;
    均匀mat4-uTEXMATRIX;
    vec4位置中的布局(位置=0);
    vec4 aTEXCOORD中的布局(位置=1);
    出vec2 vTEXCOORD;
    void main()
    {
    vTEXCOORD=(uTEXMATRIX*aTEXCOORD).xy;
    gl_位置=拔出*视图*模型*位置;
    }
    `;
    常数fs=`#版本300 es
    精密中泵浮子;
    精密医疗泵usampler2D;
    统一的usampler2D uMAP;
    均匀采样;
    均匀vec2 uTILESET_大小;//瓷砖组上下有多少瓷砖
    在vec2 vTEXCOORD中;
    RAG的vec4;
    void main()
    {
    //vTEXCOORD的整数部分是tilemap坐标
    ivec2马库德=ivec2(vTEXCOORD);
    uvec4数据=texelFetch(uMAP,mapCoord,0);
    //vTEXCOORD的分数部分是瓷砖上的UV
    vec2 texcoord=分形(vTEXCOORD);
    vec2uv=(vec2(data.xy)+texcoord)/uTILESET_大小;
    //试一下瓷砖
    oFRAG=纹理(uATLAS,uv);
    }
    `;
    常数tileWidth=32;
    常数tileHeight=32;
    常数tilesAcross=8;
    常数tilesDown=4;
    常数m4=twgl.m4;
    const gl=document.querySelector('canvas').getContext('webgl2');
    如果(!gl)警报('需要WebGL2');
    //编译着色器、链接、查找位置
    const programInfo=twgl.createProgramInfo(gl[vs,fs]);
    //gl.createBuffer、bindBuffer、bufferData
    const bufferInfo=twgl.createBufferInfoFromArrays(gl{
    位置:{
    NUM组件:2,