Matrix 将GIMP中的透视变换矩阵应用于GLSL着色器

Matrix 将GIMP中的透视变换矩阵应用于GLSL着色器,matrix,libgdx,glsl,gimp,perspectivecamera,Matrix,Libgdx,Glsl,Gimp,Perspectivecamera,因此,我尝试将旋转和透视效果添加到顶点着色器的图像中。旋转效果很好,但我无法产生透视效果。我在2D工作 旋转矩阵由代码生成,但透视矩阵是我使用透视工具从GIMP获得的一组硬编码值 private final Matrix3 perspectiveTransform = new Matrix3(new float[] { 0.58302f, -0.29001f, 103.0f, -0.00753f, 0.01827f, 203.0f, -0.00002f, -0.00115

因此,我尝试将旋转和透视效果添加到顶点着色器的图像中。旋转效果很好,但我无法产生透视效果。我在2D工作

旋转矩阵由代码生成,但透视矩阵是我使用透视工具从GIMP获得的一组硬编码值

private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
    0.58302f, -0.29001f, 103.0f,
    -0.00753f, 0.01827f, 203.0f,
    -0.00002f, -0.00115f, 1.0f
});
这个透视矩阵使用500x500图像在GIMP中实现了我想要的结果。然后我尝试在纹理坐标上应用相同的矩阵。这就是为什么我之前乘以500,之后除以500

attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;

uniform mat4 u_projTrans;
uniform mat3 u_rotation;
uniform mat3 u_perspective;

varying vec4 v_color;
varying vec2 v_texCoords;

void main() {
    v_color = a_color;

    vec3 vec = vec3(a_texCoord0 * 500.0, 1.0);

    vec = vec * u_perspective;

    vec = vec3((vec.xy / vec.z) / 500.0, 0.0);

    vec -= vec3(0.5, 0.5, 0.0);
    vec = vec * u_rotation;
    v_texCoords = vec.xy + vec2(0.5);

    gl_Position = u_projTrans * a_position;
}
对于旋转,我偏移原点,使其围绕中心而不是左上角旋转

我所知道的关于GIMP透视工具的几乎所有信息都来自于这篇文章,这篇文章建议我在阅读GIMP之后能够重现它的功能,但事实证明我不能。结果显示没有任何内容(没有像素),而移除透视部分则显示图像正确旋转

如链接中所述,我将除以
vec.z
将齐次坐标转换回2D点。我不是在透视变换中使用原点移动,因为链接中提到左上角用作原点。p、 11:

有一件事需要注意——GIMP的起源 坐标位于左上角,y向下增加

编辑

多亏@rabbi76的回答,它现在显示了一些东西!然而,它并不像矩阵在GIMP上变换我的图像那样变换我的纹理

我在GIMP上的转换矩阵应该是这样做的:

但实际上,它看起来是这样的:

这是我从实际结果中看到的想法:

(已使用)

(如前所述,它的纹理参数是“钳制到边”,而不是“钳制到边”,但这在点旁边)

它看起来和我想要的正好相反。在应用矩阵之前,我尝试将原点偏移到图像的中心和左下角,但没有成功。这是一个新的结果,但仍然是相同的问题:如何将GIMP透视矩阵应用到GLSL着色器中

EDIT2

通过更多的测试,我可以确认它做的是“相反的”。使用此简单的小尺度变换矩阵:

private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
        0.75f, 0f, 50f,
        0f, 0.75f, 50f,
        0f, 0f, 1.0f
});
结果是图像的放大版本:

如果我以编程方式反转矩阵,它适用于简单的缩放矩阵!但对于透视矩阵,它表明:

EDIT3

再次感谢@rabbi76,在透视矩阵之前进行旋转之后应用旋转,结果如下:

差不多了!唯一的问题是图像被压扁了。就像透视矩阵被多次应用一样。但如果你仔细观察,你可以看到它在透视图中旋转,就像我想要的一样。现在的问题是,如何像我在GIMP中所做的那样,解开它以获得结果。(根本问题仍然是一样的,如何获取GIMP矩阵并将其应用于着色器)

这个透视矩阵使用500x500图像在GIMP中实现了我想要的结果。然后我尝试在纹理坐标上应用相同的矩阵。这就是为什么我之前乘以500,之后除以500

attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;

uniform mat4 u_projTrans;
uniform mat3 u_rotation;
uniform mat3 u_perspective;

varying vec4 v_color;
varying vec2 v_texCoords;

void main() {
    v_color = a_color;

    vec3 vec = vec3(a_texCoord0 * 500.0, 1.0);

    vec = vec * u_perspective;

    vec = vec3((vec.xy / vec.z) / 500.0, 0.0);

    vec -= vec3(0.5, 0.5, 0.0);
    vec = vec * u_rotation;
    v_texCoords = vec.xy + vec2(0.5);

    gl_Position = u_projTrans * a_position;
}
矩阵

 0.58302 -0.29001 103.0
-0.00753  0.01827 203.0
-0.00002 -0.00115 1.0f
是一个二维透视变换矩阵。它使用2D操作。

由于中显示的矩阵是从透视图到正交视图的变换,因此必须使用逆矩阵进行变换。 可以通过调用
inv()
计算逆矩阵

矩阵被设置为执行范围[0,500]到范围[0,500]的操作

您的假设是正确的,您必须将输入从[0,1]缩放到[0,500],将输出从[0,500]缩放到[0,1]。 但是您必须缩放二维笛卡尔坐标
此外,还必须在透视投影和投影后进行旋转。
可能需要翻转纹理坐标的V坐标(取决于位图和纹理坐标属性)

最重要的是,必须在片段着色器中对每个片段进行转换。 请注意,由于此变换不是线性变换(它是透视变换),因此仅计算角点上的纹理坐标是不够的

vec2 Project2D( in vec2 uv_coord )
{
    vec2 v_texCoords;

    const float scale = 500.0;

    // flip Y
    //vec2 uv = vec2(uv_coord.x, 1.0 - uv_coord.y);
    vec2 uv = uv_coord.xy;

    // uv_h: 3D homougenus in range [0, 500] 
    vec3 uv_h = vec3(uv * scale, 1.0) * u_perspective;

    // uv_h: perspective devide and downscale [0, 500] -> [0, 1]
    vec3 uv_p = vec3(uv_h.xy / uv_h.z / scale, 1.0);

    // rotate
    uv_p = vec3(uv_p.xy - vec2(0.5), 0.0) * u_rotation + vec3(0.5, 0.5, 0.0);

    return uv_p.xy; 
}
当然,也可以在顶点着色器中进行变换。 但随后必须将2d齐次坐标从顶点着色器传递到片段着色器 这类似于将剪辑空间坐标设置为
gl\u位置
。 不同之处在于,您有二维齐次坐标,而不是三维坐标。您必须在片段着色器中手动执行以下操作:

varying vec3 v_texCoords_h;

uniform mat3 u_rotation;

void main()
{
    // perspective divide
    vec2 uv = vertTex.xy / vertTex.z;

    // rotation
    uv = (vec3(uv.xy - vec2(0.5), 0.0) * u_rotation + vec3(0.5, 0.5, 0.0)).xy;

    .....
}
顶点着色器:

attribute vec2 a_texCoord0;
varying   vec3 v_texCoords_h;

uniform mat3 u_perspective

vec3 Project2D( in vec2 uv_coord )
{
    vec2 v_texCoords;

    const float scale = 500.0;

    // flip Y
    //vec2 uv = vec2(uv_coord.x, 1.0 - uv_coord.y);
    vec2 uv = uv_coord.xy;

    // uv_h: 3D homougenus in range [0, 500] 
    vec3 uv_h = vec3(uv * scale, 1.0) * u_perspective;

    // downscale
    return vec3(uv_h.xy / scale, uv_h.z);
}

void main()
{
    v_texCoords_h = Project2D( a_texCoord0 );

    .....
}
片段着色器:

varying vec3 v_texCoords_h;

uniform mat3 u_rotation;

void main()
{
    // perspective divide
    vec2 uv = vertTex.xy / vertTex.z;

    // rotation
    uv = (vec3(uv.xy - vec2(0.5), 0.0) * u_rotation + vec3(0.5, 0.5, 0.0)).xy;

    .....
}
请参见预览,其中我使用了以下2D投影矩阵,该矩阵与中显示的矩阵相反:


进一步注意,与
u_projTrans
相比,
u_透视图
按行主顺序初始化

因此,必须将向量从左侧乘至
u\u透视图

vec_h = vec3(vec.xy * 500.0, 1.0) * u_perspective;
但是你必须将向量从右边乘以u_projTrans:

gl_Position = u_projTrans * a_position;


当然,如果您在通过

设置矩阵时仅通过旁注对其进行转置,这可能会发生变化,为了使它与我从GIMP获得的矩阵一起工作,我必须通过调用LibGDX中的
inv()
方法来反转矩阵。答案中的矩阵不是这样的。