2012-11-14 47 views
9

我想用一些叠加纹理混合相机预览SurfaceTexture。我正在使用这些着色器进行处理:Android OpenGL SurfaceTexture(外部图像)和普通纹理的组合

private final String vss = "attribute vec2 vPosition;\n" 
     + "attribute vec2 vTexCoord;\n" 
     + "varying vec2 texCoord;\n" 
     + "void main() {\n" 
     + " texCoord = vTexCoord;\n" 
     + " gl_Position = vec4 (vPosition.x, vPosition.y, 0.0, 1.0);\n" 
     + "}"; 

private final String fss = "#extension GL_OES_EGL_image_external : require\n" 
     + "precision mediump float;\n" 
     + "uniform samplerExternalOES sTexture;\n" 
     + "uniform sampler2D filterTexture;\n" 
     + "varying vec2 texCoord;\n" 
     + "void main() {\n" 
     +" vec4 t_camera = texture2D(sTexture,texCoord);\n" 
     //+" vec4 t_overlayer = texture2D(filterTexture, texCoord);\n" 
     //+ " gl_FragColor = t_overlayer;\n" + "}"; 
     + " gl_FragColor = t_camera;\n" + "}"; 

我的目标是混合t_camera和t_overlayer。当我单独显示t_camera或t_overlayer时,它可以工作(显示相机预览或纹理)。但是当我取消t_overlayer的注释时,那么t_camera变成了黑色(不知何故采样很严重)。我的覆盖层纹理是512x512和CLAMPT_TO_EDGE。 仅在例如:Android模拟器,HTC Evo 3D上发生此问题。 但在SGS3上,HTC One X,它工作得很好。

出了什么问题?它是Evo 3D缺少一些扩展还是什么?

+0

你提到它不适用于Android模拟器。你试过哪个操作系统版本?你尝试过x86吗? –

+0

我在MacOSX 10.8.2上使用x64模拟器。 –

回答

3

我在Nexus 7上遇到了同样的问题,它让我疯狂。访问samplerExternalOES或sampler2D是完全正常的,但在同一着色器中访问它们会产生意想不到的结果。有时输出会是黑色的。有时,其中一个查找的输出会产生不良的量化伪像。行为也会根据采样器绑定到的纹理单元而变化。我没有检查每个opengl错误和validateProgram结果。

最终,有效的方法是使用单独的着色器来访问摄像机输出并将其渲染到纹理中。然后可以通过常规的sampler2D访问生成的纹理,并且所有的工作都完全按照预期进行。我怀疑有一个与samplerExternalOES相关的bug。

+1

我已经通过不使用samplerExternalOES解决了问题:/ –

+0

如何将相机缓冲区传递给OpenGL?我能找到的只是通过SurfaceTexture,这迫使我使用samplerExternalOES。 – user1924406

+1

想法: 我使用onPreviewFrame捕获摄像头缓冲区,缓冲区采用YUV21格式。然后,我将缓冲区分隔成缓冲区 - Y,U,V - YUV21格式很简单。然后我使用纹理将这些缓冲区上传到GPU中 - GL_ALPHA。在片段着色器中,我有3个统一的sample2d(y,u,v)。然后在片段着色器中,我从YUV-> RGB进行如下转换: –

3

我想你有这个问题,因为你没有在你的代码上设置正确的纹理ID。这是一个逻辑上错误的假设,但实际上在文档中没有这样定义。如果选中该扩展你看到以下(编)TEXT的文档:对于每个纹理单元

每个TEXTURE_EXTERNAL_OES纹理对象可能需要高达3纹理 图像单元到它结合。 设置为TEXTURE_EXTERNAL_OES 时,此值将介于1和3之间。对于其他有效的纹理目标,此值始终为 1.请注意,当绑定TEXTURE_EXTERNAL_OES纹理对象时,单个纹理单元所需的纹理单元数可能是 1,2或3,而对于其他纹理对象每个纹理单元需要 正好1纹理图像单元。

这意味着,如果您使用id 0,那么在leas下一个额外的工作。你的情况:

GLES20.glUniform1i(sTextureHandle, 1); 
GLES20.glActiveTexture(GLES20.GL_TEXTURE1); 
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 
     sTextureId); 

为了您的2D纹理:

GLES20.glUniform1i(filterTextureHandle, 0); 
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, filterTextureID); 

我敢肯定,你这样会锻炼。

+0

好吧,我得到完全相同的问题,GL_TEXTURE_EXTERNAL_OES和纹理ID排序的东西根本不起作用。我想和其他人一样,这是一个与硬件相关的问题,不同的手机支持这个问题。 – flankechen

+0

目前还不清楚'你使用id 0'的'id'是什么意思。我在制服中看到0,这不是一个身份证,而是一个单位。纹理ID在sTextureId和filterTextureId,我们不能任意设置。 –

+0

嗨,莱昂,我的意思是: sTextureId = GL_TEXTURE1,它依次需要glUniform1i(sTextureHandle,1);为着色器。相同的0.不知道你的意思是“我们不能任意设置它”。我只是通过使用0和1来告诉着色器GL_TEXTURE0和GL_TEXTURE1。对我有用。 – Marco

3

这不是一个答案,而是一个问题的阐述 - 也许这将有助于OpenGl ES专家了解这个问题。


我有3个纹理用于​​覆盖,和一个外部纹理,用于捕获媒体播放器的输出。如果我仅使用外部纹理,则输出如预期的那样,来自MPlayer的帧。在Nexus4,Samsung Galaxy S3,S4等设备上完全相同的代码工作正常(所有设备都使用adreno gpus或Arm's Mali400)。硬件的区别在于Nexus 7使用Nvidia Tegra 3主板。


编辑(是如何解决在我身边)

的Nvidia Tegra 3的要求,外部纹理采样器被称为与采样中最低的字母顺序的名称,同时为Adreno 220似乎需要相反。另外,T3要求最后采样外部纹理。对于使用Android 4.3及更新版本的设备,这些错误可能会得到解决。在Nvidia方面,这是一个bug,很久以前就解决了,但Nexus驱动程序只是在稍后才更新。所以我不得不检查一下哪个GPU存在,并相应地调整代码。

+0

我认为4.3没有解决它。太疯狂了。这是Nvidia Tegra 3 vs - 任何其他 - ? –

3

以上方法节省了我很多时间。谢谢大师:

GLES20.glUniform1i(sTextureHandle, 1); 
GLES20.glActiveTexture(GLES20.GL_TEXTURE1); 
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 
    sTextureId); 

为了您的2D纹理:

GLES20.glUniform1i(filterTextureHandle, 0); 
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, filterTextureID); 

改变纹理指数是解决这个的好办法。

+0

这是关于使用最高级别的OES纹理? “从上面”是什么意思?哦,对不起,这些帽子。阅读“从上面”时会自动激活。 :) –

3

它似乎是OpenGl实现中的一个错误。相同的代码在Samsung Note上运行良好,而不是在nexus 4上运行。似乎getUniformLocation在某些设备上中断了位于samplerExternalOES之后的所有变量。

它也似乎是编译器按字母顺序排列统一变量,所以使它在两个设备上都能正常工作的解决方案是将samplerExternalEoz重命名为zzzTexture或其他东西。

+0

解决的办法是在某些设备上设置它为aaaTexture(例如:Motorola Razr),在其他设备上设置为zzzTexture(例如:Xperia) –

1

我可能也有同样的问题。经过几天的尝试,我在这里提出我的解决方案。希望这可以帮助其他人。

首先,问题陈述。就像LukášJezný一样,我有一个预览纹理和一个覆盖纹理。它适用于nexus 4/5和大多数其他类型,但在OPPO上找不到5,Lenovo A820,Lenovo A720。

溶液:

(1)一样的LukasJezný,使用YUV数据,并在着色它们转换为RGB。 (2)multipass drawing,预览纹理一次画到framebuffer,然后读取,然后再画到屏幕上。

(3)您使用自己的程序之前使用其它程序,

GLES20.glUseProgram(another one); 
    GLES20.glUseProgram(your "real" program); 

,它只是适用于OPPO找到5,联想A820,联想A720等。没有人知道为什么......

1

参考user1924406的文章(https://stackoverflow.com/a/14050597/3250829)关于拆分访问sampler2D纹理和samplerExternalOES纹理,这是我不得不这样做,因为我正在开发的应用程序正在阅读文件或从服务器流式传输,而不是使用设备上的相机。在同一着色器中使用两种纹理都会导致非常奇怪的着色伪像(Galaxy S3上的情况)或饱和度和对比度问题(Nexus 4上的情况)。

因此,解决samplerExternalOES纹理错误(从我目前看到的)的唯一方法就是执行两个着色器程序:一个将samplerExternalOES纹理中包含的内容写入FBO,其他从FBO获取内容并将其直接写入表面。

你需要检查的一件事是,有时当你写一个FBO时,纹理会协调翻转。在我的情况下,V(或T或Y)坐标被翻转,导致水平轴上出现镜像。在第二阶段编写片段着色器时,我必须考虑到这一点。

这是一个我想分享的战争故事,以防有些人可能需要从服务器读取文件或数据流,而不是直接从相机中读取数据。

+0

在大约一百万年的时间里搜索了如何实现这一点,请允许我放下这个链接。 https://github.com/harism/android_instacam/blob/master/src/fi/harism/instacam/InstaCamRenderer.java –

+1

@LéonPelletier - 你可以刚给我留言,并问我代码:)顺便说一句,谢谢为那个环节。很高兴在那里看到它。 – rayryeng

+0

我会在家尝试。我只是想知道如果将它应用于视频流,它将如何用FPS来表示。我计划让Android MediaPlayer和iOS AVFoundation将YUV帧发送到常见的OpenTK便携式应用程序,并使用上面提到的流程(fbo贴图)应用着色器。这将使跨平台视频效果变得非常简单。 –