编译
ndk-build
adb push ../libs/armeabi-v7a/libopengles_module /data/local/tmp/ && adb push shaders.h /data/local/tmp/testVertex.vert
adb shell
LD_LIBRARY_PATH=. ./libopengles_module && cp targetImage960.yuv /sdcard/
显示结果
adb pull /sdcard/targetImage960.yuv .
ipython tesyuv2.py targetImage960.yuv 1920 960 NV21
一些资料
替换纹理
android - Why OpenGL ES functions cannot be called from another thread - Stack Overflow
#es2.0与es3.0版本差异:
~~GLSL:
类型声明 attribute varying —>in out~~
non-nomalized texture
Rectangle Texture - OpenGL.org
opengl es 2.0 - Is rectangle texture supported by GLES 2.0 - Stack Overflow
Is it possible to get texture Integer Coordinate?
Image Load Store - OpenGL.org
但是GLES没有image2D.
GLES去除了整型索引纹理的特性,可能原本整型索引就是浮点型纹理坐标索引的封装,效率不高.所以在我们的程序中也放弃使用了.
关于glTexImage2D函数和它的参数
glTexImage2D - OpenGL ES 3 Reference Pages
纹理是GPGPU计算中重要数据输入.虽然感觉有点奇怪,但是GLSL做大数据输入并行计算确实只能这么做.另一种小量数据可以用uniform type 数组,然后一个个uniform变量设置.但是uniform数组有限制大小.不能大量输入.所以只能把无序数据封装成纹理.在glsl中定位纹理坐标使用.以输入YUV411(NV21)数据为例:
Y分量:
glBindTexture ( GL_TEXTURE_2D, texture );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer); buffer);
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glBindTexture(GL_TEXTURE_2D, 0);
注意glTexImage2D函数.Internal Format Format Type参数要一一对应.参考上面链接的表1和表2.在我的测试中用表2的有些参数数据出不来,例如GL_RG8UI/GL_RG_INTEGER/GL_UNSIGNED_BYTE应该输出像素的整型格式数据(0~255),结果映射回来全为0.所以,尽量用表1吧.那么UV数据这样表示:
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, buffer);
当需要3分量时用GL_RGB,需要4分量时用GL_RGBA.
glsl中取出数据:
vec2 y_coord = ;//设置纹理坐标
yuv.x = texture(Sampler_A_Y, y_coord).x;
yuv.y = texture(Sampler_A_UV,y_coord).r;
yuv.z = texture(Sampler_A_UV,y_coord).w;
GL_LUMINANCE_ALPHA 对应的是rw分量.GL_LUMINANCE对应x分量.其它分量为0或1填充.
顶点反馈
refer to:
Exploring GPGPU on iOS - Bartosz Ciechanowski
以往把计算数据取出来的做法是在片段着色器中填充结果纹理,然后readPixel读出来.但是ES3有个新特性-顶点反馈能方便GPGPU计算把结果读出来.
步骤:
1. 在glsl中把要反馈的变量声明为out修饰:
flat out uint ty;
flat out uint tuv;
- 编译shader后,调用glTransformFeedbackVaryings设置反馈变量,再链接一次.
glLinkProgram(program);
checkGlError("link");
const char * varyings[] = {"ty","tuv"}; //NOTE! 这些变量必须在shader中有赋值.
glTransformFeedbackVaryings(program, sizeof(varyings)/sizeof(*varyings), varyings, GL_INTERLEAVED_ATTRIBS);// size = 3 * 4 / 4
for (GLint error = glGetError(); error; error = glGetError()) {
printf("after () glError (0x%x)\n", error);
}
//relink!
glLinkProgram(program);
- 分配一个输出VBO:
glBindBuffer(GL_ARRAY_BUFFER,dstVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof ( YUV_t ) * I_TARGET_SIZE, destBuffer, GL_DYNAMIC_COPY);
- draw输出到缓冲区
在帧更新函数加入:
glBindBuffer ( GL_TRANSFORM_FEEDBACK_BUFFER, dstVBO );
checkGlError("calculateByGPU:glBindBuffer");
glBindBufferBase ( GL_TRANSFORM_FEEDBACK_BUFFER, 0, dstVBO );
checkGlError("calculateByGPU:glBindBufferBase");
glBeginTransformFeedback ( GL_POINTS );
glDrawArrays ( GL_POINTS, 0, I_TARGET_SIZE );
glEndTransformFeedback();
- 最后把VBO映射回内存:
glBindBuffer(GL_ARRAY_BUFFER, dstVBO);
YUV_t *memoryBuffer = (YUV_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(YUV_t) * I_TARGET_SIZE , GL_MAP_READ_BIT);
glUnmapBuffer(GL_ARRAY_BUFFER);