4

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

据我了解,这应该可以通过在 ImageView 上使用我当前设置的 getImageMatrix() 来实现,然后将其应用于原始图像顶部的纹理位图。

编辑和解决:(与下面所选答案的强力助手)

目前,纹理中的平移速度与 ImageView 的平移速度不同,但当这个问题得到解决后,我将通过额外的编辑更新此帖子,并为整个应用程序提供解决方案。然后只保留解决方案和快速问题描述段落。也许我什至会发布使用可缩放表面视图的表面视图和渲染器源代码。

为了完成 ImageView 到 OpenGL 纹理的映射,需要做几件事才能正确完成。希望这将有助于其他可能希望在未来使用可缩放 SurfaceView 的人。

着色器代码如下所示。gl_FragColor 从任何 ImageView 的 getImageMatrix 中获取一个 3x3 的平移矩阵,以便在 onDraw() 方法中更新屏幕。

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 渲染器的位置错误之外,我们还有一个问题是我们正在处理我们在 imageHeight 和 imageWidth 的比例上的转换,而不是标准化的 [0, 1] 范围OpenGL 期望。因此,为了纠正这一点,我们必须以宽度和高度的可整除数作为目标。

matrixValues[6] = matrixValues[6] / getImageWidth();
matrixValues[7] = matrixValues[7] / getImageHeight();

然后,完成此操作后,您可以开始使用 ImageView 矩阵函数或更新的 ImageTouchView 矩阵函数(在 Android 上处理图像时具有一些额外的好功能)进行映射。

效果如下图所示,它后面的 ImageView 占据了整个屏幕,前面的纹理根据 ImageView 的更新进行更新。这处理两者之间的缩放和平移翻译。

imageview 更新了 opengl 纹理

该过程中唯一的其他步骤是更改 SurfaceView 的大小以匹配 ImageView 的大小,以便在用户放大时获得全屏体验。为了只显示一个表面视图,您可以简单地将一个'看不见的在背景中它后面只需让 ImageTouchView 更新改变您的 OpenGL SurfaceView 的显示方式。

为下面的答案投票,因为他们通过聊天显着帮助我离线了解如何将图像视图与纹理链接起来。他们应得的。

如果您最终使用下面的顶点着色器修改,而不是片段着色器分辨率,那么似乎不再需要反转这些值来使用捏缩放(实际上,如果您离开它,实际上会向后工作照原样)。此外,平移似乎也比预期的倒退。

4

1 回答 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。

于 2013-06-28T15:08:26.167 回答