Сохранить скриншот с наложенным изображением OpenGLES
Я использую Vuforia для приложения дополненной реальности. Когда я обнаруживаю изображение, я могу отображать или визуализировать 3D-объект и UIImageView, тогда я могу сделать скриншот 3D-объекта, но не могу сохранить нормальное изображение. Я просто показываю обычные изображения UIImageview. Нужно ли визуализировать 2D-изображение вместо обычного UIImageView?
Визуализация 3D:
- (void)renderFrameQCAR
{
[self setFramebuffer];
// Clear colour and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render video background and retrieve tracking state
QCAR::State state = QCAR::Renderer::getInstance().begin();
QCAR::Renderer::getInstance().drawVideoBackground();
glEnable(GL_DEPTH_TEST);
// We must detect if background reflection is active and adjust the culling direction.
// If the reflection is active, this means the pose matrix has been reflected as well,
// therefore standard counter clockwise face culling will result in "inside out" models.
if (offTargetTrackingEnabled) {
glDisable(GL_CULL_FACE);
} else {
glEnable(GL_CULL_FACE);
}
glCullFace(GL_BACK);
if(QCAR::Renderer::getInstance().getVideoBackgroundConfig().mReflection == QCAR::VIDEO_BACKGROUND_REFLECTION_ON)
glFrontFace(GL_CW); //Front camera
else
glFrontFace(GL_CCW); //Back camera
for (int i = 0; i < state.getNumTrackableResults(); ++i) {
// Get the trackable
// _numResults = state.getNumTrackableResults();
[self performSelectorOnMainThread:@selector(DisplayPhotoButton
) withObject:nil waitUntilDone:YES];
const QCAR::TrackableResult* result = state.getTrackableResult(i);
const QCAR::Trackable& trackable = result->getTrackable();
//const QCAR::Trackable& trackable = result->getTrackable();
QCAR::Matrix44F modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(result->getPose());
// OpenGL 2
QCAR::Matrix44F modelViewProjection;
if (offTargetTrackingEnabled) {
SampleApplicationUtils::rotatePoseMatrix(90, 1, 0, 0,&modelViewMatrix.data[0]);
SampleApplicationUtils::scalePoseMatrix(kObjectScaleOffTargetTracking, kObjectScaleOffTargetTracking, kObjectScaleOffTargetTracking, &modelViewMatrix.data[0]);
} else {
SampleApplicationUtils::translatePoseMatrix(0.0f, 0.0f, kObjectScaleNormal, &modelViewMatrix.data[0]);
SampleApplicationUtils::scalePoseMatrix(kObjectScaleNormal, kObjectScaleNormal, kObjectScaleNormal, &modelViewMatrix.data[0]);
}
SampleApplicationUtils::multiplyMatrix(&vapp.projectionMatrix.data[0], &modelViewMatrix.data[0], &modelViewProjection.data[0]);
glUseProgram(shaderProgramID);
if (offTargetTrackingEnabled) {
glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)buildingModel.vertices);
glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)buildingModel.normals);
glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)buildingModel.texCoords);
} else {
glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)teapotVertices);
glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)teapotNormals);
glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)teapotTexCoords);
}
glEnableVertexAttribArray(vertexHandle);
glEnableVertexAttribArray(normalHandle);
glEnableVertexAttribArray(textureCoordHandle);
// Choose the texture based on the target name
int targetIndex = 0; // "stones"
if (!strcmp(trackable.getName(), "chips"))
targetIndex = 1;
else if (!strcmp(trackable.getName(), "tarmac"))
targetIndex = 2;
glActiveTexture(GL_TEXTURE0);
if (offTargetTrackingEnabled) {
glBindTexture(GL_TEXTURE_2D, augmentationTexture[3].textureID);
} else {
glBindTexture(GL_TEXTURE_2D, augmentationTexture[targetIndex].textureID);
}
glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (const GLfloat*)&modelViewProjection.data[0]);
glUniform1i(texSampler2DHandle, 0 /*GL_TEXTURE0*/);
if (offTargetTrackingEnabled) {
glDrawArrays(GL_TRIANGLES, 0, buildingModel.numVertices);
} else {
glDrawElements(GL_TRIANGLES, NUM_TEAPOT_OBJECT_INDEX, GL_UNSIGNED_SHORT, (const GLvoid*)teapotIndices);
}
SampleApplicationUtils::checkGlError("EAGLView renderFrameQCAR");
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisableVertexAttribArray(vertexHandle);
glDisableVertexAttribArray(normalHandle);
glDisableVertexAttribArray(textureCoordHandle);
QCAR::Renderer::getInstance().end();
[self presentFramebuffer];
}
Отображение UIImageView при открытии камеры:
- (id)initWithFrame:(CGRect)frame appSession:(SampleApplicationSession *) app
{
self = [super initWithFrame:frame];
if (self) {
vapp = app;
// takePhotoFlag = NO;
// [self DisplayPhotoButton];
// Enable retina mode if available on this device
if (YES == [vapp isRetinaDisplay]) {
[self setContentScaleFactor:2.0f];
}
// Load the augmentation textures
for (int i = 0; i < NUM_AUGMENTATION_TEXTURES; ++i) {
augmentationTexture[i] = [[Texture alloc] initWithImageFile:[NSString stringWithCString:textureFilenames[i] encoding:NSASCIIStringEncoding]];
}
// Create the OpenGL ES context
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// The EAGLContext must be set for each thread that wishes to use it.
// Set it the first time this method is called (on the main thread)
if (context != [EAGLContext currentContext]) {
[EAGLContext setCurrentContext:context];
}
// Generate the OpenGL ES texture and upload the texture data for use
// when rendering the augmentation
for (int i = 0; i < NUM_AUGMENTATION_TEXTURES; ++i) {
GLuint textureID;
glGenTextures(1, &textureID);
[augmentationTexture[i] setTextureID:textureID];
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [augmentationTexture[i] width], [augmentationTexture[i] height], 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)[augmentationTexture[i] pngData]);
}
offTargetTrackingEnabled = NO;
[self loadBuildingsModel];
[self initShaders];
_takePhotoFlag = NO;
[self DisplayPhotoButton];
}
return self;
}
- (void)DisplayPhotoButton
{
UIImage *closeButtonImage = [UIImage imageNamed:@"back.png"];
// UIImage *closeButtonTappedImage = [UIImage imageNamed:@"button_close_pressed.png"];
CGRect aRect = CGRectMake(20,20,
closeButtonImage.size.width,
closeButtonImage.size.height);
photo = [UIButton buttonWithType:UIButtonTypeCustom];
photo.frame = aRect;
photo.userInteractionEnabled=YES;
[photo setImage:closeButtonImage forState:UIControlStateNormal];
// photo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[photo addTarget:self action:@selector(takePhoto) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:photo];
UIButton *arrowButton = [UIButton buttonWithType:UIButtonTypeCustom];
[arrowButton setImage:[UIImage imageNamed:@"back_btn.png"] forState:UIControlStateNormal];
// [overlayButton setFrame:CGRectMake(80, 420, 60, 30)];
[arrowButton setFrame:CGRectMake(100, 10, 40, 40)];
[arrowButton addTarget:self action:@selector(showActionSheet:forEvent:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:arrowButton];
}
Скриншот OpenGL:
- (UIImage*) glToUIImage
{
UIImage *outputImage = nil;
CGFloat scale = [[UIScreen mainScreen] scale];
CGRect s = CGRectMake(0, 0, 320.0f * scale, 480.0f * scale);
uint8_t *buffer = (uint8_t *) malloc(s.size.width * s.size.height * 4);
glReadPixels(0, 0, s.size.width, s.size.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
UIImage *imageFromRawData(uint8_t *data, int width, int height) {
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,data,width*height*4,NULL);
CGImageRef imageRef = CGImageCreate(width, height, 8, 32, width*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaLast, provider, NULL, NO, kCGRenderingIntentDefault);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
return newImage;
}
CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, buffer, s.size.width * s.size.height * 4, NULL);
CGImageRef iref = CGImageCreate(s.size.width, s.size.height, 8, 32, s.size.width * 4, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault, ref, NULL, true, kCGRenderingIntentDefault);
size_t width = CGImageGetWidth(iref);
size_t height = CGImageGetHeight(iref);
size_t length = width * height * 4;
uint32_t *pixels = (uint32_t *)malloc(length);
CGContextRef context1 = CGBitmapContextCreate(pixels, width, height, 8, width * 4,
CGImageGetColorSpace(iref), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big);
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0.0f, height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(context1, transform);
CGContextDrawImage(context1, CGRectMake(0.0f, 0.0f, width, height), iref);
CGImageRef outputRef = CGBitmapContextCreateImage(context1);
outputImage = [UIImage imageWithCGImage: outputRef];
CGDataProviderRelease(ref);
CGImageRelease(iref);
CGContextRelease(context1);
CGImageRelease(outputRef);
free(pixels);
free(buffer);
UIImageWriteToSavedPhotosAlbum(outputImage, nil, nil, nil);
return outputImage;
}
Сохранить из текущего буфера кадра:
- (BOOL)presentFramebuffer
{
if (_takePhotoFlag)
{
[self glToUIImage];
_takePhotoFlag = NO;
}
// setFramebuffer must have been called before presentFramebuffer, therefore
// we know the context is valid and has been set for this (render) thread
// Bind the colour render buffer and present it
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
return [context presentRenderbuffer:GL_RENDERBUFFER];
}
1 ответ
У вас может быть скриншот openGL или скриншот пользовательского интерфейса. Для объединения 2 я предлагаю вам сделать оба изображения. Это будет звучать глупо, но это, вероятно, самый быстрый и мощный способ сделать это:
- Сделайте скриншот из openGL (как вы уже сделали)
- Создайте вид изображения из этого скриншота
- Вставьте это изображение на ваш основной вид
- Сделайте скриншот пользовательского интерфейса * основного вида
- Удалить вид изображения
* Под снимком экрана пользовательского интерфейса я имею в виду что-то вроде этого:
+ (UIImage *)imageFromView:(UIView *)view {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, .0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Если у вас возникнет проблема, связанная с отображением черного фона вместо фонового изображения, возможно, возникла проблема с генерацией CGImage
где-то в конвейере, где он пропускает или предварительно умножает альфа-канал. (очень распространенная ошибка)
РЕДАКТИРОВАТЬ: Получение изображения из считанных пикселей RGBA-данных:
Это то, что я использовал для получения UIImage
из необработанных данных RGBA. Обратите внимание, что эта процедура не будет обрабатывать какую-либо ориентацию, но вы можете немного ее изменить, взять ориентацию в качестве аргумента, а затем использовать imageWithCGImage:scale:orientation:
UIImage *imageFromRawData(uint8_t *data, int width, int height) {
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,data,width*height*4,NULL);
CGImageRef imageRef = CGImageCreate(width, height, 8, 32, width*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaLast, provider, NULL, NO, kCGRenderingIntentDefault);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
return newImage;
}