【问题标题】:Implementing trapezoidal sprites in LibGDX在 LibGDX 中实现梯形精灵
【发布时间】:2015-04-02 03:33:11
【问题描述】:

我正在尝试为一个简单的 2D 游戏创建一个程序动画引擎,它可以让我用少量图像创建漂亮的动画(类似于这种方法,但对于 2D:http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach

目前我有保存不同动画对象数据的关键帧,关键帧是表示以下内容的浮点数组:

translateX、translateY、scaleX、scaleY、旋转(度)

我想将 skewX、skewY、taperTop 和 taperBottom 添加到此列表中,但无法正确渲染它们。

这是我尝试在精灵顶部实现锥形以使其具有梯形形状:

float[] vert = sprite.getVertices();
vert[5] += 20; // top-left vertex x co-ordinate
vert[10] -= 20; // top-right vertex x co-ordinate

batch.draw(texture, vert, 0, vert.length);

不幸的是,这会产生一些奇怪的纹理变形。

我有一点谷歌和查看 StackOverflow 并发现了这个,这似乎是我遇到的问题:

http://www.xyzw.us/~cass/qcoord/

但是我不明白它背后的数学原理(什么是 s、t、r 和 q?)。

谁能解释的简单一点?

【问题讨论】:

    标签: java opengl animation libgdx


    【解决方案1】:

    基本上,四边形越不像矩形,由于在形状上线性插值纹理坐标的影响,外观越差。构成四边形的两个三角形被拉伸到不同的大小,因此线性插值使接缝非常明显。

    对于片段着色器处理的每个片段,每个顶点的纹理坐标都是线性插值的。纹理坐标通常与已经划分的对象的大小一起存储,因此坐标在 0-1 的范围内,对应于纹理的边缘(并且此范围之外的值被钳制或环绕)。这也是任何 3D 建模程序导出网格的典型方式。

    对于梯形,我们可以通过将纹理坐标预乘以宽度,然后在线性插值后将宽度从纹理坐标中除以来限制失真。这就像弯曲两个三角形之间的对角线,使其斜率在梯形较宽边的角处更水平。这是一张有助于说明它的图片。

    纹理坐标通常表示为具有分量 U 和 V 的 2D 向量,也称为 S 和 T。但是如果您想从分量中除以大小,则需要另外一个分量来除以插值后,这称为 Q 分量。 (如果您在 3D 纹理而不是 2D 纹理中查找某些东西,则 P 分量将用作纹理中的第三个位置。

    现在困难的部分来了... libgdx 的 SpriteBatch 不支持 Q 组件所需的额外顶点属性。因此,您可以克隆 SpriteBatch 并仔细检查并修改它以在 texCoord 属性中添加一个额外的组件,或者您可以尝试重新利用现有的颜色属性,尽管它存储为无符号字节。

    无论如何,您都需要预先划分宽度的纹理坐标。简化此问题的一种方法是,不使用四个顶点的四边形的实际大小,而是获取梯形的顶部和底部宽度的比率,因此我们可以将顶部部分视为宽度 1,因此将它们保留为一个人。

    float bottomWidth = taperBottom / taperTop;
    

    然后您需要修改 TextureRegion 的现有纹理坐标以将它们预乘以宽度。由于上面的简化,我们可以不考虑梯形顶边的顶点,但是两个窄边顶点的U和V坐标需要乘以bottomWidth。每次更改 TextureRegion 或锥度值之一时,您都需要重新计算它们并将它们放入顶点数组中。

    在顶点着色器中,您需要将额外的 Q 分量传递给片段着色器。在片段着色器中,我们通常使用按大小划分的纹理坐标来查找纹理颜色,如下所示:

    vec4 textureColor = texture2D(u_texture, v_texCoords);
    

    但在我们的例子中,我们仍然需要除以 Q 分量:

    vec4 textureColor = texture2D(u_texture, v_texCoords.st / v_texCoords.q);
    

    但是,这会导致读取依赖纹理,因为我们在将向量传递到纹理函数之前对其进行了修改。 GLSL 提供了一个自动执行上述操作的函数(我假设不会导致依赖纹理读取):

    vec4 textureColor = texture2DProj(u_texture, v_texCoords); //first two components automatically divided by last component
    

    【讨论】:

    • “所以这个问题的解决方案是在它们被线性插值之后,而不是之前,将它们的大小分开。”,你能详细说明一下吗?在过程的不同阶段将纹理宽度/高度乘以标量如何影响结果?
    • 您知道,由于您指出的原因,我认为我所说的仅在水平方向上执行此操作没有意义。结果不同的原因是我们在两个维度上这样做。在垂直方向上,我们除以宽度,而不是高度,这给出了一个透视拉伸,实际上弯曲了四边形的对角线。也许我可以制作一个显示差异的情节。
    • 好的,我想我已经纠正了解释。现在我想知道我们是否可以做一些其他的数学运算来消除垂直失真。我们在这里所做的只是将水平失真换成垂直失真,尽管它看起来要好得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多