如何在Android上使用OpenGL ES 2.0渲染大尺寸图像
问题描述:
我试图在Android OS上通过OpenGL ES 2.0更改图像的颜色。这是我的代码。如何在Android上使用OpenGL ES 2.0渲染大尺寸图像
德州类
public class Tex {
private FloatBuffer mVertexBuffer;
private ShortBuffer mDrawListBuffter;
protected FloatBuffer mUvBuffer;
protected static float mUvs[];
private final float[] mMtrxView = new float[16];
public static final String vs_Image
= "uniform mat4 uMVPMatrix;"
+ "attribute vec4 vPosition;"
+ "attribute vec2 a_texCoord;"
+ "varying vec2 v_texCoord;"
+ "void main() {"
+ " gl_Position = uMVPMatrix * vPosition;"
+ " v_texCoord = a_texCoord;"
+ "}";
public static final String fs_Image
= "precision mediump float;"
+ "varying vec2 v_texCoord;"
+ "uniform sampler2D s_texture;"
+ "void main() {"
+ " vec4 tex = texture2D(s_texture, v_texCoord);"
+ " float tintR = 0.6;"
+ " float tintG = 0.3;"
+ " float tintB = 0.0;"
+ " float tr = clamp(tex.r * (1.0 - tintR) + tintR, 0.0, 1.0);"
+ " float tg = clamp(tex.g * (1.0 - tintG) + tintG, 0.0, 1.0);"
+ " float tb = clamp(tex.b * (1.0 - tintB) + tintB, 0.0, 1.0);"
+ " gl_FragColor = vec4(tr, tg, tb, tex.a);"
+ "}";
float mSquareCoords[] = {
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
private short mDrawOrder[] = {0, 1, 2, 0, 2, 3};
private final int mProgram;
int[] mTextureNames;
MainGLRenderer mMainGLRenderer;
int mWidth, mHeight;
Bitmap mBitmap;
public Tex(MainGLRenderer mainGLRenderer, Bitmap bitmap) {
mMainGLRenderer = mainGLRenderer;
ByteBuffer bb = ByteBuffer.allocateDirect(mSquareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
mVertexBuffer = bb.asFloatBuffer();
mVertexBuffer.put(mSquareCoords);
mVertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(mDrawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
mDrawListBuffter = dlb.asShortBuffer();
mDrawListBuffter.put(mDrawOrder);
mDrawListBuffter.position(0);
mUvs = new float[] {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
};
ByteBuffer bbUvs = ByteBuffer.allocateDirect(mUvs.length * 4);
bbUvs.order(ByteOrder.nativeOrder());
mUvBuffer = bbUvs.asFloatBuffer();
mUvBuffer.put(mUvs);
mUvBuffer.position(0);
int vertexShader = mMainGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vs_Image);
int fragmentShader = mMainGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fs_Image);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);
initTexture(bitmap);
}
private void initTexture(Bitmap bitmap) {
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
GLES20.glViewport(0, 0, mWidth, mHeight);
mTextureNames = new int[1];
GLES20.glGenTextures(1, mTextureNames, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureNames[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
draw();
}
public void draw() {
GLES20.glUseProgram(mProgram);
Matrix.setIdentityM(mMtrxView, 0);
int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
int texCoordLoc = GLES20.glGetAttribLocation(mProgram, "a_texCoord");
GLES20.glEnableVertexAttribArray(texCoordLoc);
GLES20.glVertexAttribPointer(texCoordLoc, 2, GLES20.GL_FLOAT, false, 0, mUvBuffer);
int mtrxHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mtrxHandle, 1, false, mMtrxView, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureNames[0]);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mDrawOrder.length, GLES20.GL_UNSIGNED_SHORT, mDrawListBuffter);
GLES20.glDisableVertexAttribArray(positionHandle);
GLES20.glDisableVertexAttribArray(texCoordLoc);
mBitmap = getBitmap();
}
public Bitmap getBitmap() {
IntBuffer intBuffer = IntBuffer.allocate(mWidth * mHeight);
GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, intBuffer);
int[] intArrayO = intBuffer.array();
int[] intArrayR = new int[mWidth * mHeight];
for (int i = 0; i < mHeight; i++) {
for (int j = 0; j < mWidth; j++) {
intArrayR[(mHeight - i - 1) * mWidth + j] = intArrayO[i * mWidth + j];
}
}
Bitmap postBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
postBitmap.copyPixelsFromBuffer(intBuffer.wrap(intArrayR));
return postBitmap;
}
public Bitmap getmBitmap() {
return mBitmap;
}
}
在MainActivity
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_run:
Bitmap bitmap = mGLSurfaceView.mRenderer.mTex.getmBitmap();
mImageView.setImageBitmap(bitmap);
String storageDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + Environment.DIRECTORY_DCIM + "/Camera";
File dir = new File(storageDir);
if (!dir.exists()) dir.mkdir();
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String path = storageDir + "/IMG_" + timeStamp + ".jpg";
File file = new File(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteData = stream.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(byteData);
fos.flush();
fos.close();
}
catch (Exception e) {
}
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.parse("file://" + path);
intent.setData(uri);
sendBroadcast(intent);
break;
}
}
正如你可以看到上面的代码中的onClick方法,onClick()
方法调用getmBitmap()
方法,然后我可以得到渲染图像。它适用于小分辨率图像。但是,渲染图像对于像3840 x 2160这样的高分辨率是不正常的。在渲染图像中,小尺寸和颜色变化的原始图像位于左下角。其余区域以黑色填充。我不知道什么是错的。我必须为高分辨率图像设置一些东西吗?我需要建议。请帮忙。
答
我通过这两个链接解决了这个问题。
- OpenGL ES ReadPixels to bitmap From Texture larger than screen
- GLES20.glReadPixels gets all zero in android
点是...
- 为了得到比从纹理显示尺寸大
Bitmap
,生成新的帧缓冲器,而不是使用默认缓冲。默认缓冲区不大于设备的显示分辨率。 - 在初始化OpenGL ES的同一个
Context
中调用glReadPixels
。
答
你在渲染什么?
您正在上传较大的纹理,但如果您没有更改要渲染的帧缓冲区的大小,那么您将无法获得更大的图像。
如何更改framebuffer的大小?我必须使用什么方法来做到这一点?请给我更多的提示。我是OpenGL ES编程的初学者。 –
对于帧缓冲区对象(屏幕外渲染),您需要在分配存储区中附加较大的纹理或渲染缓冲区附件。对于屏幕上的渲染,我怀疑你运气不好 - 你不可能得到比设备的物理显示更大的窗口表面。 – solidpixel