将 GIMP 的透视变换矩阵应用到 GLSL 着色器中
Applying a perspective transformation matrix from GIMP into a GLSL shader
所以我试图在顶点着色器中为图像添加旋转和透视效果。旋转效果很好,但我无法制作透视效果。我在 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 透视工具的几乎所有内容都来自 http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch10.ps 这表明我可以在阅读后重现 GIMP 所做的事情,但事实证明我做不到。结果显示什么都没有(没有像素),而删除透视部分显示图像正确旋转。
如 link 中所述,我除以 vec.z
以将齐次坐标转换回二维点。我没有使用原点移动进行透视变换,因为 link 中提到左上角被用作原点。第 11 页:
There is one thing to be careful about - the origin of GIMP
coordinates is at the upper left, with y increasing downwards.
编辑:
感谢@Rabbid76 的回答,它现在显示了一些东西!但是,它并没有像矩阵在 GIMP 上转换我的图像那样转换我的纹理。
我在 GIMP 上的转换矩阵应该做一些类似的事情:
但是,它看起来像这样:
这是我从实际看到的结果得出的想法:
https://imgur.com/X56rp8K (Image 使用)
(正如指出的那样,它的纹理参数是 clamp to edge 而不是 clamp to border,但这不是重点)
它看起来与我正在寻找的完全相反。在应用矩阵但没有成功之前,我尝试将原点偏移到图像的中心和左下角。这是一个新的结果,但它仍然是同样的问题:How to apply the GIMP perspective matric into a GLSL shader?
EDIT2:
通过更多测试,我可以确认它正在执行 "opposite"。使用这个简单的缩小变换矩阵:
private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
0.75f, 0f, 50f,
0f, 0.75f, 50f,
0f, 0f, 1.0f
});
结果是图像的放大版本:
如果我以编程方式反转矩阵,它适用于简单的缩放矩阵!但是对于透视矩阵,表明:
EDIT3:
再次感谢@Rabbid76,结果是在透视矩阵之前进行旋转之后应用旋转,我最终得到这样的结果:https://imgur.com/n1vWq0M
差不多了!唯一的问题是图像非常压扁。就像多次应用透视矩阵一样。但是如果你仔细看,你会看到它在透视中旋转,就像我想要的那样。现在的问题是如何取消压缩以获得像我在 GIMP 中那样的结果。 (根本问题还是一样,如何获取 GIMP 矩阵并将其应用到着色器中)
This perspective matrix was doing the result I want in GIMP using a 500x500 image. I'm then trying to apply this same matrix on texture coordinates. That's why I'm multiplying by 500 before and dividing by 500 after.
矩阵
0.58302 -0.29001 103.0
-0.00753 0.01827 203.0
-0.00002 -0.00115 1.0f
是二维透视变换矩阵。它以 2D Homogeneous coordinate.
操作
参见 2D affine and perspective transformation matrices
由于GIMP中显示的矩阵是透视到正交视图的变换,所以需要用到逆矩阵来进行变换。
逆矩阵可以通过调用inv()
.
来计算
矩阵设置为在 [0, 500] 范围内执行 Cartesian coordinate in the range [0, 500], to a Homogeneous coordinates 的操作。
您的假设是正确的,您必须将输入从 [0, 1] 范围缩放到 [0, 500],将输出从 [0, 500] 缩放到 [0, 1]。
但是你必须缩放二维笛卡尔坐标
此外,您必须在透视投影和 Perspective divide.
之后进行旋转
可能有必要(取决于位图和纹理坐标属性),您必须翻转纹理坐标的 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;
}
当然你也可以在顶点着色器中进行转换。
但是你必须将二维齐次坐标从顶点着色器传递到片段着色器
这类似于将剪辑 space 坐标设置为 gl_Position
。
不同之处在于你有一个 2d 齐次坐标而不是 3d。并且您必须在片段着色器中手动执行 Perspective divide:
顶点着色器:
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 投影矩阵,它是 GIMP 中显示的矩阵的逆矩阵:
2.452f, 2.6675f, -388.0f,
0.0f, 7.7721f, -138.0f,
0.00001f, 0.00968f, 1.0f
另外请注意,与 u_projTrans
相比,u_perspective
是按行主要顺序初始化的。
因此你必须将向量从左边乘以 u_perspective
:
vec_h = vec3(vec.xy * 500.0, 1.0) * u_perspective;
但是你必须将向量从右边乘以u_projTrans
:
gl_Position = u_projTrans * a_position;
见GLSL Programming/Vector and Matrix Operations
和 Data Type (GLSL)
当然,如果您在按 glUniformMatrix
*
设置矩阵时转置矩阵,这可能会改变
所以我试图在顶点着色器中为图像添加旋转和透视效果。旋转效果很好,但我无法制作透视效果。我在 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 透视工具的几乎所有内容都来自 http://www.math.ubc.ca/~cass/graphics/manual/pdf/ch10.ps 这表明我可以在阅读后重现 GIMP 所做的事情,但事实证明我做不到。结果显示什么都没有(没有像素),而删除透视部分显示图像正确旋转。
如 link 中所述,我除以 vec.z
以将齐次坐标转换回二维点。我没有使用原点移动进行透视变换,因为 link 中提到左上角被用作原点。第 11 页:
There is one thing to be careful about - the origin of GIMP coordinates is at the upper left, with y increasing downwards.
编辑:
感谢@Rabbid76 的回答,它现在显示了一些东西!但是,它并没有像矩阵在 GIMP 上转换我的图像那样转换我的纹理。
我在 GIMP 上的转换矩阵应该做一些类似的事情:
但是,它看起来像这样:
这是我从实际看到的结果得出的想法:
https://imgur.com/X56rp8K (Image 使用)
(正如指出的那样,它的纹理参数是 clamp to edge 而不是 clamp to border,但这不是重点)
它看起来与我正在寻找的完全相反。在应用矩阵但没有成功之前,我尝试将原点偏移到图像的中心和左下角。这是一个新的结果,但它仍然是同样的问题:How to apply the GIMP perspective matric into a GLSL shader?
EDIT2:
通过更多测试,我可以确认它正在执行 "opposite"。使用这个简单的缩小变换矩阵:
private final Matrix3 perspectiveTransform = new Matrix3(new float[] {
0.75f, 0f, 50f,
0f, 0.75f, 50f,
0f, 0f, 1.0f
});
结果是图像的放大版本:
如果我以编程方式反转矩阵,它适用于简单的缩放矩阵!但是对于透视矩阵,表明:
EDIT3:
再次感谢@Rabbid76,结果是在透视矩阵之前进行旋转之后应用旋转,我最终得到这样的结果:https://imgur.com/n1vWq0M
差不多了!唯一的问题是图像非常压扁。就像多次应用透视矩阵一样。但是如果你仔细看,你会看到它在透视中旋转,就像我想要的那样。现在的问题是如何取消压缩以获得像我在 GIMP 中那样的结果。 (根本问题还是一样,如何获取 GIMP 矩阵并将其应用到着色器中)
This perspective matrix was doing the result I want in GIMP using a 500x500 image. I'm then trying to apply this same matrix on texture coordinates. That's why I'm multiplying by 500 before and dividing by 500 after.
矩阵
0.58302 -0.29001 103.0
-0.00753 0.01827 203.0
-0.00002 -0.00115 1.0f
是二维透视变换矩阵。它以 2D Homogeneous coordinate.
操作
参见 2D affine and perspective transformation matrices
由于GIMP中显示的矩阵是透视到正交视图的变换,所以需要用到逆矩阵来进行变换。
逆矩阵可以通过调用inv()
.
矩阵设置为在 [0, 500] 范围内执行 Cartesian coordinate in the range [0, 500], to a Homogeneous coordinates 的操作。
您的假设是正确的,您必须将输入从 [0, 1] 范围缩放到 [0, 500],将输出从 [0, 500] 缩放到 [0, 1]。
但是你必须缩放二维笛卡尔坐标
此外,您必须在透视投影和 Perspective divide.
之后进行旋转
可能有必要(取决于位图和纹理坐标属性),您必须翻转纹理坐标的 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;
}
当然你也可以在顶点着色器中进行转换。
但是你必须将二维齐次坐标从顶点着色器传递到片段着色器
这类似于将剪辑 space 坐标设置为 gl_Position
。
不同之处在于你有一个 2d 齐次坐标而不是 3d。并且您必须在片段着色器中手动执行 Perspective divide:
顶点着色器:
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 投影矩阵,它是 GIMP 中显示的矩阵的逆矩阵:
2.452f, 2.6675f, -388.0f,
0.0f, 7.7721f, -138.0f,
0.00001f, 0.00968f, 1.0f
另外请注意,与 u_projTrans
相比,u_perspective
是按行主要顺序初始化的。
因此你必须将向量从左边乘以 u_perspective
:
vec_h = vec3(vec.xy * 500.0, 1.0) * u_perspective;
但是你必须将向量从右边乘以u_projTrans
:
gl_Position = u_projTrans * a_position;
见GLSL Programming/Vector and Matrix Operations
和 Data Type (GLSL)
当然,如果您在按 glUniformMatrix
*