【问题标题】:Tear when not expected after re-sizing the texture重新调整纹理大小后意外撕裂
【发布时间】:2013-10-08 22:38:03
【问题描述】:

在某些情况下,当用户放大图像直到最短边填满整个屏幕(如标准图库中所见)时,需要扩展纹理。但是,OpenGL 着色器的输入是来自 ImageView 的矩阵,因此需要进行某些转换以及针对不同空间的校正计算。

编辑:

顶点着色器代码比较简单,如下:

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";

其中 a_position 引用下面的 verticesData_。加载到屏幕上时,图像的位置是根据显示宽度和所占用的屏幕部分计算得出的准确位置。

另外,我有以下片段着色器代码:

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";

mat3 转换的输入是来自 ImageView 的输入。本质上,OpenGLSurfaceView 下面有一个 ImageView。这是一个分辨率低得多的图像,当用户滑动时,SurfaceView 被隐藏,ImageView 位于其下方,与用户在 SurfaceView 中的位置相同。

但是,稍后当我想扩展此纹理时,我发现自己会得到意想不到的结果。

来回平移时,屏幕会移动到预期移动的位置。因此,矩阵中 x 和 y 坐标的平移正常。但是,放大时,图像会撕裂。只有当纹理的边界超出屏幕尺寸时才会撕裂。如下图所示,高度在增长时并没有引入额外的像素,但宽度在增长时会撕裂。

为了传递适当的矩阵,从 ImageView 到 OpenGL SurfaceView 的值被反转和转置,因为空间需要它进行适当的转换。将比例因子传递给 Activity 正在侦听的侦听器。

@Override
public void onTranslation(float imageScaleF, float[] matrixTranslationF)
{   
    currentScaleForImage_ = imageScaleF;
    //If we are currently using the entire screen, then there is not need anymore to resize the texture itself.
    if(surfaceViewWidth_ * imageScaleF > displayViewWidth_ && surfaceViewHeight_ * imageScaleF > displayViewHeight_)
    {

    }
    else //Expand the size of the texture to be displayed on the screen.
    {
        maximumScaleForImage_ = imageScaleF;
        photoViewRenderer_.updateTextureSize(imageScaleF);
        photoSurfaceView_.requestRender();
    }
    matrixTranslationF = updateTranslationBasedOnTextureSize(matrixTranslationF);
    photoViewRenderer_.updateTranslationOfTexture(matrixTranslationF);
    photoSurfaceView_.requestRender();
}

但是,使用上面的以下代码,滚动的图像部分总是被剪短,如果我试图通过取消注释更新翻译的行来根据比例更正它,它会导致眼泪。所以,似乎在这一点上,下面的用户通过他们的输入让我处于一个更好的位置,但我仍然只有一步之遥,我认为它在这个函数中。以下现在更新的代码进行了必要的更正,以便在 ImageView 中的矩阵和 OpenGL 纹理/坐标之间提供适当的转换。

private float[] updateTranslationBasedOnTextureSize(float[] matrixTranslationsF)
    { 
        if(scaleDirection_ == ScaleDirection.WIDTH)
        {
            matrixTranslationsF[0] = matrixTranslationsF[0] * maximumScaleForImage_;
        }
        else if(scaleDirection_ == ScaleDirection.HEIGHT)
        {
            matrixTranslationsF[4] = matrixTranslationsF[4] * maximumScaleForImage_;
        }

        return matrixTranslationsF;
    }

我在第一次迭代中导致照片撕裂,如下所示。或者由于两个空间之间的不正确转换,它正在剪裁照片。

宽度似乎保持不变,所以它暗示我可能会在标准范围之外的值发生某些事情,但我不确定要尝试什么。

【问题讨论】:

  • 纹理坐标范围实际上不是 (-1,-1) - (1,1)。它们被归一化为 (0,0) - (1,1)。此范围之外的任何内容都受您的纹理环绕模式的影响,在这种情况下,您看起来像是在使用CLAMP_TO_EDGE。如果您期望它们在 (-1,-1) 到 (1,1) 的范围内,这可以解释一些奇怪的行为 - 坐标空间实际上是用于投影后的顶点,就在它们映射到视口之前.
  • 我不确定您在这里尝试产生的行为是什么。如果您正在平移,那么在桌面 GL 中您可以使用GL_CLAMP_TO_BORDER 并设置黑色边框颜色,当然 GLES 中不存在这种钳制模式。您必须通过检测超出范围的纹理坐标在片段着色器中手动实现它。
  • 我很想赢得你的赏金,但我不知道你还在寻找什么。想一想……您可以通过放大图片的宽度和高度、使用矩阵数学放大或缩小纹理坐标来放大图片。我在回答中给你的公式是我在我的应用程序中用来放大/缩小和平移图像的公式。就像我在我的 cmets 中所说的那样。我的回答是正确的。你的纹理坐标超出了界限——正如我和安东所说的那样。但您似乎在寻找其他东西 - 不确定是什么。
  • 事实上,我需要使用其中的多个进行扩展。这对我来说有点棘手;特别是因为我不会在诸如此类的小时刻之外使用 OpenGL。我需要放大纹理以使其填充屏幕,但从那里我需要使用来自 Android ImageView 的矩阵数学来匹配我传递给 OpenGL 代码的矩阵。当然,在纹理调整大小的过程中也会发生矩阵变化以进行补偿。并且发现最新纹理大小和矩阵输入之间的相关性给我带来了问题。

标签: android opengl-es opengl-es-2.0


【解决方案1】:

出现这种条纹是因为您将纹理环绕设置为夹到边缘。你有类似的东西:

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

GL_REPEAT 是默认值,将重复图像。但是您希望在底部没有任何“影响”。由于您的数学,这个底部正在发生。你只在底部而不是侧面得到条纹。正如有人在评论中所说,您的顶点范围不正确。最小值和最大值是 0 和 1 .. 不是 -1 和 1。所以在图片的底部它是条纹的,因为您正在计算 0-1 边界之外的纹理坐标数,并且可能进入 1 区域。当 texture2D(u_textureSampler, v_fragmentTexCoord0) 获得 0-1 范围之外的纹理坐标时,它将落在 WRAP_S / WRAP_T 设置上以知道要给出什么值。它正在重复边缘像素,因为您有 GL_CLAMP_TO_EDGE。

你的实际问题不是这个设置。那是您正在计算超出范围的 S 或 T。这不是好的形式,因为即使您在那里写黑色,它也会浪费片段着色器上的周期,并且会减慢您的应用程序。基本上,你的公式是错误的。

至于你放大数学......这是数学。无论你想做什么,你都必须解决它。 (或寻求帮助。)您可以这样做,这是我在新照片编辑应用中所做的简化版本:

    GLfloat aspect = photo.width / photo.height;
    if (aspect > 1) {
      displayWidth = panelWidth;
      displayHeight = panelWidth/aspect;
    }
    else {
      displayHeight = panelHeight;
      displayWidth = panelHeight*destAspect;
    }

其中 panelWidth 和 panelHeight 是您正在显示照片的区域的大小 - 可以是全屏或更小。然后根据显示的高度和宽度构建您的顶点。如果您放大到超出面板的大小,则需要进行裁剪以将图像保留在面板内,既可以通过减小显示宽度或高度(哪个太大),也可以将纹理坐标更改为匹配。这很粗略,但是是这样的:

GLfloat sSize = (destSMax-destSMin) / panelZoom;
GLfloat tSize = (destTMax-destTMin) / panelZoom;

displaySMin = destSMin + panelPanX/coreTextureSize;
displayTMin = destTMin + panelPanY/coreTextureSize;

displaySMax = displaySMin + sSize;
displayTMax = displayTMin + tSize;

然后当然构建您的顶点数据,使用矩阵数学将正方形移动到您想要的位置,然后绘制它。

我建议您重新考虑使用类似于我上面所做的方法的方法。当照片离开屏幕时,不要删除照片的一部分。而是计算将要看到的内容并以此为基础构建数据。

编辑 - 另外,我意识到我的代码是 ios 代码,这是一个 android 问题。但同样的概念也适用。 glTexParameteri 调用可能略有不同,它可能是 float 而不是 GLfloat。

【讨论】:

  • 对不起,当我写答案时已经晚了,我把它倒退了..clamp to edge 确实做了你所展示的事情,所以我更正了我的答案。首先计算纹理坐标的数学仍然是你应该看的地方。这是你的问题。
  • 如果它仍然在底部显示重复的像素,听起来好像它不起作用。 -1 的纹理坐标绝对超出界限,将根据您设置 GL_TEXTURE_WRAP 的方式返回颜色值。使用 CLAMP_TO_EDGE,它会像上面显示的那样“撕裂”。使用 REPEAT,您会看到图像被“平铺”并重复。当您放大照片时 - 看起来就像您想要的那样,因此请限制图像不要向上滚动到您可以看到黑色的位置。但这是你的应用程序。从技术上讲,我的回答是正确的。
  • 不确定您的“-1 绝对超出范围”。这些坐标用于显示原始图像,以便纹理在开始时仅占据我想要的屏幕部分(居中并按比例缩放)。根据图像的纵横比从边缘重新调整大小后,使用这些纹理看起来很好。在使用这些顶点之前,纹理没有显示在正确的位置。在增长到最大屏幕尺寸后,我不再触摸这些顶点,这似乎工作得很好。出现问题的其余部分是片段着色器代码。
  • 上面的“updateTranslationBasedOnTextureSize”代码试图弥补我在没有它的情况下看到的效果。如果没有这个调用,当纹理增长时,矩阵会导致图像拉伸,直到纹理停止增长。然后图像被适当地缩放和增长。然而,这种修改并没有提供我想要的解决方案。但是,如果图像占据了大部分或全部屏幕尺寸,则代码可以完美运行;所以在纹理增长的同时有些事情发生了......可能是我必须处理的矩阵补偿。
  • 它实际上只在没有'updateTranslationBasedOnTextureSize'的情况下沿纹理增长的方向延伸。这反过来会导致边缘处图像的那一侧被剪裁。这就是为什么我试图用这种方法来补偿。只有在我尝试使用这种方法找到有效的补偿来改变矩阵之后,钳位误差才会出现。如果图像在达到 x2 缩放最大值之前不会停止增长,它似乎会在该方向上剪掉一半的图像。
猜你喜欢
  • 2018-10-30
  • 1970-01-01
  • 2013-07-07
  • 2014-12-18
  • 2018-10-17
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多