将android.graphics.matrix翻译为OpenGL 2.0 ES纹理

4

目前,该应用程序显示一个ImageView,可以在屏幕上成功地进行缩放和平移。在该图像上方,我想提供一个纹理,并希望当其下方的图像缩放或平移时,该纹理更新自身以进行缩放或平移。

据我所知,使用当前设置中的ImageView的getImageMatrix(),然后将其应用于位于原始图像上方的文本化位图,应该是可能的。

编辑和解决方案:(在下面选择的答案的帮助下)

目前,纹理中的平移发生的速度与ImageView的速度不同,但是当已解决该问题后,我将使用附加编辑更新此帖子并提供整个应用程序的解决方案。只有解决方案和快速问题描述段落才会留下。也许我还会发布一个表面视图和渲染器源代码,用于使用可缩放表面视图。

要将ImageView映射到OpenGL纹理,需要完成一些工作,以正确完成此操作。希望这将对未来可能想要使用可缩放SurfaceView的其他人有所帮助。

着色器代码如下所示。gl_FragColor使用3x3的平移矩阵从onDraw()方法内部的任何ImageView的getImageMatrix来更新屏幕。

private final String vertexShader_ =
        "attribute vec4 a_position;\n" +
        "attribute vec4 a_texCoord;\n" +
        "varying vec2 v_texCoord;\n" +
        "void main() {\n" +
        "  gl_Position = a_position;\n" +
        "  v_texCoord = a_texCoord.xy;\n" +
        "}\n";

private final String fragmentShader_ =
        "precision mediump float;\n" +
        "varying vec2 v_texCoord;\n" +
        "uniform sampler2D texture;\n" +
        "uniform mat3 transform;\n" +
        "void main() {\n" +
        "  vec2 uv = (transform * vec3(v_texCoord, 1.0)).xy;\n" +
        "  gl_FragColor = texture2D( texture, uv );\n" +
        "}\n";

开始时,OpenGL的翻译矩阵只是一个标识矩阵,我们的ImageView将通过监听器更新这些共享值与着色器进行通信。

private float[] translationMatrix = {1.0f, 0.0f, 0.0f,
                                     0.0f, 1.0f, 0.0f,
                                     0.0f, 0.0f, 1.0f};
...
uniforms_[TRANSLATION_UNIFORM] = glGetUniformLocation(program_, "transform");
checkGlError("glGetUniformLocation transform");
if (uniforms_[TRANSLATION_UNIFORM] == -1) {
    throw new RuntimeException("Could not get uniform location for transform");
}
....
glUniformMatrix3fv(uniforms_[TRANSLATION_UNIFORM], 1, false, FloatBuffer.wrap(translationMatrix));
....

然而,OpenGL代码需要Android提供的OpenGL矩阵计算的逆,并在将其交给渲染器显示在屏幕上之前对它们执行转置操作。这与信息存储的方式有关。

protected void onMatrixChanged()
{
    //...
    float[] matrixValues = new float[9];
    Matrix imageViewMatrix = getImageViewMatrix();
    Matrix invertedMatrix = new Matrix();
    imageViewMatrix.invert(invertedMatrix);
    invertedMatrix.getValues(matrixValues);
            transpose(matrixValues);
    matrixChangedListener.onTranslation(matrixValues);
    //...
}

除了值在输入OpenGL渲染器时位置不正确的问题,我们还面临着处理图像高度和宽度比例而非OpenGL所期望的归一化[0, 1]范围内的转换的问题。因此,为了解决这个问题,我们需要定位到最后一列,同时使其宽度和高度可被整除。
matrixValues[6] = matrixValues[6] / getImageWidth();
matrixValues[7] = matrixValues[7] / getImageHeight();

然后,在完成此操作之后,您可以开始使用ImageView矩阵函数或更新的ImageTouchView矩阵函数(在Android上处理图像时具有一些附加的好功能)进行映射。如下图所示,ImageView在后面占据整个屏幕,而纹理位于其前面,根据来自ImageView的更新进行更新。这会处理两者之间的缩放和平移转换。
唯一的其他步骤是更改SurfaceView的大小以匹配ImageView的大小,以便在用户缩放时获得全屏体验。为了只显示surfaceview,您可以在后台将一个'invisible' imageview置于其后面。让ImageTouchView更新更改OpenGL SurfaceView显示的方式。
如果您最终选择使用下面的顶点着色器修改而不是片段着色器分辨率,则似乎不再需要反转值才能与捏合缩放配合使用(实际上,如果保留原样,它们会向后工作)。此外,平移也似乎与预期的方向相反。请投票支持下面的答案,因为他们通过聊天线下帮助我大大理解了连接ImageView和纹理所需执行的操作。他们值得得到认可。

1
你已经有使用着色器绘制纹理矩形的OpenGL代码了吗?你只需要知道如何计算适当的矩阵并在着色器中使用它吗? - GuyRT
@GuyRT 没错。也许对于其他人来说这很简单,但我不确定。目前我正在使用着色器显示图像,并需要更新它,以便我可以获得与 ImageView 背后的相同图像上已经获得的缩放效果类似的效果。 - Jay Snayder
1个回答

3
你可以为此编写片段着色器,只需将该矩阵作为片段着色器的统一参数设置,并使用该矩阵修改纹理坐标。
precision mediump float;
uniform   sampler2D   samp;
uniform   mat3        transform;
varying   vec2        texCoord;

void main()
{
   vec2 uv = (transform * vec3(texCoord, 1.0)).xy;
   gl_FragColor = texture2D(samp, uv);
}

注意,你从图像视图中获取的矩阵是按行主序排列的,你需要进行转换才能获得OpenGL列主序排列,同时你需要分别除以宽度和高度来获得x和y平移分量。
更新:我突然想到修改顶点着色器可能更好,你可以使用与片段着色器中使用的相同矩阵缩放你的四边形,如果太大,它会被夹到你的屏幕上,并且当它变小时就不会有边缘夹紧的问题,矩阵乘法仅会发生4次而不是每个像素都要一次。只需在顶点着色器中执行以下操作:
gl_Position = vec4((transform * vec3(a_position.xy, 1.0)).xy, 0.0, 1.0);

并在片段着色器中使用未经转换的texCoord。


1
@JaySnayder,你能上传一个视频展示一下现在的工作情况吗?这将有助于理解你正在做什么。 - Vasaka
当屏幕上出现一张照片时,图像和纹理的大小相同。然后用户通过捏合缩放来放大,因此背景中的图像会增大。纹理应该随之增大,使用图像矩阵作为其输入。但正如上面所示,它却缩小了。 - Jay Snayder
1
@JaySnayder 尝试传递反转矩阵,Android Matrix类提供了invert方法来获取一个。 - Vasaka
1
@JaySnayder 很可能意味着 Android 矩阵与 OpenGL 期望的矩阵不同,您应该在将矩阵设置到着色器时设置标志以将其转置回来,这可以通过设置矩阵的命令标志来完成。 - Vasaka
1
@JaySnayder 我认为调整变换以使翻译正常工作应该很容易。 - Vasaka
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接