referred to this question
i convert the yv12 frame data to rgb data using glsl shader,the raw image below:
but the result image is not same with the former,attached below:
following is my code for uploading the three planar data to textures:
- (GLuint) textureY: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureU: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureV: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (void) readYUVFile
{
NSString *file = [[NSBundle mainBundle] pathForResource:#"video" ofType:#"yv12"];
NSLog(#"%#",file);
NSData* fileData = [NSData dataWithContentsOfFile:file];
//NSLog(#"%#",[fileData description]);
NSInteger width = 352;
NSInteger height = 288;
NSInteger uv_width = width / 2;
NSInteger uv_height = height / 2;
NSInteger dataSize = [fileData length];
NSLog(#"%i\n",dataSize);
GLint nYsize = width * height;
GLint nUVsize = uv_width * uv_height;
GLint nCbOffSet = nYsize;
GLint nCrOffSet = nCbOffSet + nUVsize;
Byte *spriteData = (Byte *)malloc(dataSize);
[fileData getBytes:spriteData length:dataSize];
Byte* uData = spriteData + nCbOffSet;
//NSLog(#"%#\n",[[NSData dataWithBytes:uData length:nUVsize] description]);
Byte* vData = spriteData + nCrOffSet;
//NSLog(#"%#\n",[[NSData dataWithBytes:vData length:nUVsize] description]);
/**
Byte *YPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<nYsize; i++) {
YPlanarData[i]= spriteData[i];
}
Byte *UPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
UPlanarData[i*width+j]=uData[numInUVsize];
}
}
Byte *VPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
VPlanarData[i*width+j]=vData[numInUVsize];
}
}
**/
_YPlanarTexture = [self textureY:spriteData widthType:width heightType:height];
_UPlanarTexture = [self textureU:uData widthType:uv_width heightType:uv_height];
_VPlanarTexture = [self textureV:vData widthType:uv_width heightType:uv_height];
free(spriteData);
}
and my fragment shaders code:
precision highp float;
uniform sampler2D SamplerY;
uniform sampler2D SamplerU;
uniform sampler2D SamplerV;
varying highp vec2 coordinate;
void main()
{
highp vec3 yuv,yuv1;
highp vec3 rgb;
yuv.x = texture2D(SamplerY, coordinate).r;
yuv.y = texture2D(SamplerU, coordinate).r-0.5;
yuv.z = texture2D(SamplerV, coordinate).r-0.5 ;
rgb = mat3( 1, 1, 1,
0, -.34414, 1.772,
1.402, -.71414, 0) * yuv;
gl_FragColor = vec4(rgb, 1);
}
my confusion is the conversion formula
while i using this formula directly converting the yv12 data to rgb24,and draw a image with the
CGImageCreate(iwidth,
iheight,
8,
24,
iwidth*3,
colorSpace,
bitmapInfo,
provider,
NULL,
NO,
kCGRenderingIntentDefault);
the result image is correct.
but using the shader (for the direct transform approach running on iOS device is dump) turns to this problem ,i've tried some tricks(expand the UV planers to (2*uv_width)*2(uv_height) rectangle and then upload the texture),but failed in the same more red image.
How to resolve this issue?
attached with my whole glView.m code:
#import "OpenGLView.h"
typedef struct {
float Position[3];
float TexCoord[2];
} Vertex;
const Vertex Vertices[] = {
{{1, -1, 0},{1,1}},
{{1, 1, 0},{1,0}},
{{-1, 1, 0},{0,0}},
{{-1, -1, 0},{0,1}}
};
const GLubyte Indices[] = {
0, 1, 2,
2, 3, 0
};
#interface OpenGLView ()
- (void)setupLayer;
- (void)setupContext;
- (void)setupRenderBuffer;
- (void)setupFrameBuffer;
- (void)render;
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType;
- (void)setupVBOs;
- (void)compileShaders;
- (void) readYUVFile;
#end
#implementation OpenGLView
- (void)setupVBOs {
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code[]
self.backgroundColor = [UIColor redColor];
[self setupLayer];
[self setupContext];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self setupVBOs];
[self compileShaders];
[self readYUVFile];
[self render];
}
return self;
}
+ (Class)layerClass{
return [CAEAGLLayer class];
}
-(void)setupLayer{
_eaglLayer = (CAEAGLLayer *)self.layer;
_eaglLayer.opaque = YES;
}
- (void)setupContext{
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[[EAGLContext alloc] initWithAPI:api] autorelease];
if (!_context) {
NSLog(#"Failed to initialize OpenGLES 2.0 context");
exit(1);
}
if (![EAGLContext setCurrentContext:_context]) {
NSLog(#"Failed to set current OpenGL context");
exit(1);
}
}
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
- (void)setupFrameBuffer {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
}
- (GLuint) textureY: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureU: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RED_EXT, width, height, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (GLuint) textureV: (Byte*)imageData
widthType: (int) width
heightType: (int) height
{
GLuint texName;
glGenTextures( 1, &texName );
glBindTexture(GL_TEXTURE_2D, texName);
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);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RED_EXT, width, height, 0, GL_RED_EXT, GL_UNSIGNED_BYTE, imageData );
//free(imageData);
return texName;
}
- (void) readYUVFile
{
NSString *file = [[NSBundle mainBundle] pathForResource:#"video" ofType:#"yv12"];
NSLog(#"%#",file);
NSData* fileData = [NSData dataWithContentsOfFile:file];
//NSLog(#"%#",[fileData description]);
NSInteger width = 352;
NSInteger height = 288;
NSInteger uv_width = width / 2;
NSInteger uv_height = height / 2;
NSInteger dataSize = [fileData length];
NSLog(#"%i\n",dataSize);
GLint nYsize = width * height;
GLint nUVsize = uv_width * uv_height;
GLint nCbOffSet = nYsize;
GLint nCrOffSet = nCbOffSet + nUVsize;
Byte *spriteData = (Byte *)malloc(dataSize);
[fileData getBytes:spriteData length:dataSize];
Byte* uData = spriteData + nCbOffSet;
//NSLog(#"%#\n",[[NSData dataWithBytes:uData length:nUVsize] description]);
Byte* vData = spriteData + nCrOffSet;
//NSLog(#"%#\n",[[NSData dataWithBytes:vData length:nUVsize] description]);
Byte *YPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<nYsize; i++) {
YPlanarData[i]= spriteData[i];
}
Byte *UPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
UPlanarData[i*width+j]=uData[numInUVsize];
}
}
Byte *VPlanarData = (Byte *)malloc(nYsize);
for (int i=0; i<height; i++) {
for (int j=0; j<width; j++) {
int numInUVsize = (i/2)*uv_width+j/2;
VPlanarData[i*width+j]=vData[numInUVsize];
}
}
_YPlanarTexture = [self textureY:YPlanarData widthType:width heightType:height];
_UPlanarTexture = [self textureU:UPlanarData widthType:width heightType:height];
_VPlanarTexture = [self textureV:VPlanarData widthType:width heightType:height];
free(spriteData);
}
- (void)render {
glClearColor(0,0,0 , 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// 1
glViewport(0, 200, self.frame.size.width, 558);
// 2
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) *3));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _YPlanarTexture);
glUniform1i(_textureUniformY, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _UPlanarTexture);
glUniform1i(_textureUniformU, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _VPlanarTexture);
glUniform1i(_textureUniformV, 2);
// 3
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
// 1
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName
ofType:#"glsl"];
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath
encoding:NSUTF8StringEncoding error:&error];
if (!shaderString) {
NSLog(#"Error loading shader: %#", error.localizedDescription);
exit(1);
}
// 2
GLuint shaderHandle = glCreateShader(shaderType);
// 3
const char* shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
// 4
glCompileShader(shaderHandle);
// 5
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
return shaderHandle;
}
- (void)compileShaders {
// 1
GLuint vertexShader = [self compileShader:#"SimpleVertex"
withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:#"SimpleFragment"
withType:GL_FRAGMENT_SHADER];
// 2
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 3
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
// 4
glUseProgram(programHandle);
// 5
_positionSlot = glGetAttribLocation(programHandle, "position");
glEnableVertexAttribArray(_positionSlot);
_texCoordSlot = glGetAttribLocation(programHandle, "textureCoordinate");
glEnableVertexAttribArray(_texCoordSlot);
_YPlanarTexture = glGetUniformLocation(programHandle, "SamplerY");
_UPlanarTexture = glGetUniformLocation(programHandle, "SamplerU");
_VPlanarTexture = glGetUniformLocation(programHandle, "SamplerV");
}
#end
my foolish mistake,the three uploading-texture process and fragment shader are correct ,but the following code are not compatible:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _YPlanarTexture);
glUniform1i(_textureUniformY, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _UPlanarTexture);
glUniform1i(_textureUniformU, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _VPlanarTexture);
glUniform1i(_textureUniformV, 2);
and below:
_YPlanarTexture = glGetUniformLocation(programHandle, "SamplerY");
_UPlanarTexture = glGetUniformLocation(programHandle, "SamplerU");
_VPlanarTexture = glGetUniformLocation(programHandle, "SamplerV");
so replace with this:
_textureUniformY = glGetUniformLocation(programHandle, "SamplerY");
_textureUniformU = glGetUniformLocation(programHandle, "SamplerU");
_textureUniformV = glGetUniformLocation(programHandle, "SamplerV");
then it will do right thing .
Related
I created an iOS app using OpenGL to render yuv420p from ffmpeg.
It works fine on iPad, but on iPhone, it looks like the image below,
the picture looks italic and the right bottom triangle part like below should be on the left.
I can not found the reason, has anyone met this before?
does OpenGL on iPad and iPhone different?
below is my OpenGL view. SDL_Overlay is a struct that holds YUV plane data from ffmpeg.
#import "EAGLView.h"
// Uniform index.
enum
{
UNIFORM_Y,
UNIFORM_U,
UNIFORM_V,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
// Attribute index.
enum
{
ATTRIB_VERTEX,
ATTRIB_TEXCOORD,
NUM_ATTRIBUTES
};
const GLubyte VertexIndexStruct[] = {
0, 1, 2,
2, 3, 0
};
#interface EAGLView () {
// The pixel dimensions of the CAEAGLLayer.
GLint _backingWidth;
GLint _backingHeight;
EAGLContext *m_context;
GLuint _frameBufferHandle;
GLuint _colorBufferHandle;
BOOL _pause;
}
#property GLuint program;
- (void)setupBuffers;
- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
#end
#implementation EAGLView
+(Class) layerClass {
return [CAEAGLLayer class];
}
- (id) initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
return self;
}
-(void) applicationDidEnterBackground:(NSNotification *) notification {
_pause = YES;
}
-(void) applicationWillEnterForeground:(NSNotification *) notification {
_pause = NO;
}
-(void) destroyFrameBuffer {
// tear down GL
if (_frameBufferHandle) {
glDeleteFramebuffers(1, &_frameBufferHandle);
_frameBufferHandle = 0;
}
if (_colorBufferHandle) {
glDeleteRenderbuffers(1, &_colorBufferHandle);
_colorBufferHandle = 0;
}
if(self.program) {
glDeleteProgram(self.program);
}
}
-(void) setFrame:(CGRect)frame {
[super setFrame:frame];
[self setupGL];
}
# pragma mark - OpenGL setup
- (void)setupGL {
[self destroyFrameBuffer];
self.contentScaleFactor = [UIScreen mainScreen].scale;
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = TRUE;
[eaglLayer setContentsScale:self.contentScaleFactor];
eaglLayer.drawableProperties = #{ kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8};
if(m_context) {
[m_context release];
}
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if(!m_context) {
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
if (!m_context || ![EAGLContext setCurrentContext:m_context] || ![self loadShaders]) {
return;
}
[EAGLContext setCurrentContext:m_context];
[self setupBuffers];
[self loadShaders];
glUseProgram(self.program);
// 0 and 1 are the texture IDs of _lumaTexture and _chromaTexture respectively.
glUniform1i(uniforms[UNIFORM_Y], 0);
glUniform1i(uniforms[UNIFORM_U], 1);
glUniform1i(uniforms[UNIFORM_V], 2);
}
#pragma mark - Utilities
- (void)setupBuffers {
glDisable(GL_DEPTH_TEST);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
glGenFramebuffers(1, &_frameBufferHandle);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
glGenRenderbuffers(1, &_colorBufferHandle);
glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = TRUE;
[eaglLayer setContentsScale:self.contentScaleFactor];
[m_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBufferHandle);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(#"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
}
- (void)dealloc {
[self destroyFrameBuffer];
[super dealloc];
}
#pragma mark - OpenGL ES 2 shader compilation
- (BOOL)loadShaders {
GLuint vertShader, fragShader;
NSURL *vertShaderURL, *fragShaderURL;
// Create the shader program.
self.program = glCreateProgram();
// Create and compile the vertex shader.
vertShaderURL = [[NSBundle mainBundle] URLForResource:#"shader" withExtension:#"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER URL:vertShaderURL]) {
NSLog(#"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderURL = [[NSBundle mainBundle] URLForResource:#"shader" withExtension:#"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER URL:fragShaderURL]) {
NSLog(#"Failed to compile fragment shader");
return NO;
}
// Attach vertex shader to program.
glAttachShader(self.program, vertShader);
// Attach fragment shader to program.
glAttachShader(self.program, fragShader);
// Bind attribute locations. This needs to be done prior to linking.
glBindAttribLocation(self.program, ATTRIB_VERTEX, "position");
glBindAttribLocation(self.program, ATTRIB_TEXCOORD, "texCoord");
// Link the program.
if (![self linkProgram:self.program]) {
NSLog(#"Failed to link program: %d", self.program);
if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (self.program) {
glDeleteProgram(self.program);
self.program = 0;
}
return NO;
}
// Get uniform locations.
uniforms[UNIFORM_Y] = glGetUniformLocation(self.program, "SamplerY");
uniforms[UNIFORM_U] = glGetUniformLocation(self.program, "SamplerU");
uniforms[UNIFORM_V] = glGetUniformLocation(self.program, "SamplerV");
// Release vertex and fragment shaders.
if (vertShader) {
glDetachShader(self.program, vertShader);
glDeleteShader(vertShader);
}
if (fragShader) {
glDetachShader(self.program, fragShader);
glDeleteShader(fragShader);
}
return YES;
}
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL {
NSError *error;
NSString *sourceString = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
if (sourceString == nil) {
NSLog(#"Failed to load vertex shader: %#", [error localizedDescription]);
return NO;
}
GLint status;
const GLchar *source;
source = (GLchar *)[sourceString UTF8String];
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &source, NULL);
glCompileShader(*shader);
#if defined(DEBUG)
GLint logLength;
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(*shader, logLength, &logLength, log);
NSLog(#"Shader compile log:\n%s", log);
free(log);
}
#endif
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
if (status == 0) {
glDeleteShader(*shader);
return NO;
}
return YES;
}
- (BOOL)linkProgram:(GLuint)prog {
GLint status;
glLinkProgram(prog);
#if defined(DEBUG)
GLint logLength;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(prog, logLength, &logLength, log);
NSLog(#"Program link log:\n%s", log);
free(log);
}
#endif
glGetProgramiv(prog, GL_LINK_STATUS, &status);
if (status == 0) {
return NO;
}
return YES;
}
- (BOOL)validateProgram:(GLuint)prog {
GLint logLength, status;
glValidateProgram(prog);
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(prog, logLength, &logLength, log);
NSLog(#"Program validate log:\n%s", log);
free(log);
}
glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
if (status == 0) {
return NO;
}
return YES;
}
-(CGSize) renderSize {
return CGSizeMake(_backingWidth/self.contentScaleFactor, _backingHeight/self.contentScaleFactor);
}
-(void) render:(SDL_Overlay*) overlay {
if(_pause) {
return;
}
//Create Y and UV textures from the pixel buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_Y]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
overlay->w,
overlay->h,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
overlay->data[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// U-plane.
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_U]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
overlay->w/2,
overlay->h/2,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
overlay->data[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// V-plane.
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_V]);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_LUMINANCE,
overlay->w/2,
overlay->h/2,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
overlay->data[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);
CGFloat ratio = (CGFloat)overlay->w / overlay->h;
GLfloat actualWidth = _backingWidth;
GLfloat actualHeight = actualWidth / ratio;
if(actualHeight > _backingHeight) {
actualHeight = _backingHeight;
actualWidth = actualHeight * ratio;
}
// Set the view port to the entire view.
glViewport((_backingWidth - actualWidth) / 2,
(_backingHeight - actualHeight) / 2,
actualWidth,
actualHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Use shader program.
glUseProgram(self.program);
/*
The quad vertex data defines the region of 2D plane onto which we draw our pixel buffers.
Vertex data formed using (-1,-1) and (1,1) as the bottom left and top right coordinates respectively, covers the entire screen.
*/
GLfloat quadVertexData [] = {
1, -1,
1, 1,
-1, 1,
-1, -1
};
// Update attribute values.
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData);
glEnableVertexAttribArray(ATTRIB_VERTEX);
/*
The texture vertices are set up such that we flip the texture vertically. This is so that our top left origin buffers match OpenGL's bottom left texture coordinate system.
*/
GLfloat quadTextureData[] = {
1, 1,
1, 0,
0, 0,
0, 1,
};
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, quadTextureData);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDrawElements(GL_TRIANGLES, sizeof(VertexIndexStruct) / sizeof(VertexIndexStruct[0]), GL_UNSIGNED_BYTE, &VertexIndexStruct);
glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}
#end
I am just trying to draw a full screen .png into openGL as a texture. However, I am met with a black screen. My code works fine with jpegs, so I can only assume it's an issue with transparency.
Here is an example png, which it isn't working for: http://cl.ly/e5x4 (although it is opaque, it still doesn't render) (and no, it needs to be a .png)
Here is my code:
glView.m
struct vertex {
float position[3];
float color[4];
float texCoord[2];
};
typedef struct vertex vertex;
const vertex vertices[] = {
{{1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {1, 0}}, // BR (0)
{{1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {1, 1}}, // TR (1)
{{-1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {0, 1}}, // TL (2)
{{-1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {0, 0}}, // BL (3)
};
const GLubyte indicies[] = {
0, 1, 2,
0, 3, 2
};
#implementation glView {
EAGLContext* context;
GLuint positionSlot, colorSlot, textureCoordSlot;
GLuint texture, textureUniform;
GLuint vertexBuffer, indexBuffer;
}
-(GLuint) compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:#"glsl"];
NSError* err;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&err];
NSAssert(shaderString, #"Failed to load shader string: %#", err.localizedDescription);
GLuint shaderHandle = glCreateShader(shaderType);
const char* shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = (int)[shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString* messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
#throw NSInternalInconsistencyException;
}
return shaderHandle;
}
-(void) complileShaders {
GLuint vertexShader = [self compileShader:#"vertexShader" withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:#"fragmentShader" withType:GL_FRAGMENT_SHADER];
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString* messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
#throw NSInternalInconsistencyException;
}
glUseProgram(programHandle);
positionSlot = glGetAttribLocation(programHandle, "position");
colorSlot = glGetAttribLocation(programHandle, "sourceColor");
textureCoordSlot = glGetAttribLocation(programHandle, "texCoordIn");
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
glEnableVertexAttribArray(textureCoordSlot);
textureUniform = glGetUniformLocation(programHandle, "tex");
}
-(instancetype) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.layer.opaque = YES;
// Setup context
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
NSAssert(context, #"Failed to initialise context.");
NSAssert([EAGLContext setCurrentContext:context], #"Failed to set the current context.");
// Setup render buffer
GLuint colorBuffer;
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.layer];
// Setup frame buffer
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
// Setup vertex buffer
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Setup index buffer
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies, GL_STATIC_DRAW);
[self complileShaders];
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
texture = [self loadTexture:#"justapng.png"];
[self render];
}
return self;
}
-(GLuint) loadTexture:(NSString*)fileName {
UIImage* textureImage = [UIImage imageNamed:fileName];
NSAssert1(textureImage, #"Unable to load texture %#.", fileName);
return [self loadTextureFromImage:textureImage];
}
-(GLuint) loadTextureFromImage:(UIImage*)image {
CGImageRef textureImage = image.CGImage;
size_t width = CGImageGetWidth(textureImage);
size_t height = CGImageGetHeight(textureImage);
GLubyte* spriteData = (GLubyte*) malloc(width*height*4);
CGColorSpaceRef cs = CGImageGetColorSpace(textureImage);
CGContextRef c = CGBitmapContextCreate(spriteData, width, height, 8, width*4, cs, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(cs);
CGContextDrawImage(c, (CGRect){CGPointZero, {width, height}}, textureImage);
CGContextRelease(c);
GLuint glTex;
glGenTextures(1, &glTex);
glBindTexture(GL_TEXTURE_2D, glTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return glTex;
}
-(void) render {
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(textureUniform, 0);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), 0);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*3));
glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*7));
glDrawElements(GL_TRIANGLES, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
#end
vertexShader.glsl
attribute vec4 position;
attribute vec4 sourceColor;
varying vec4 destinationColor;
attribute vec2 texCoordIn;
varying vec2 texCoordOut;
void main() {
destinationColor = sourceColor;
gl_Position = position;
texCoordOut = texCoordIn;
}
fragmentShader.glsl
varying lowp vec4 destinationColor;
varying lowp vec2 texCoordOut;
uniform sampler2D tex;
void main() {
gl_FragColor = destinationColor*texture2D(tex, texCoordOut);
}
Sorry for dumping all this code, but I am unsure where the problem is originating from. Any ideas as to what I am doing wrong?
This appears to be the classic non-power-of-two issue (eg. see Android OpenGL2.0 showing black textures). The texture you linked is 1005x335.
The OpenGL ES 2.0 specification only allows for NPOT textures to be 'complete' with glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S/T, GL_CLAMP_TO_EDGE). Various extensions allow NPOT texturing, however, your device doesn't necessarily support these, and likely doesn't if you're getting black textures. If a texture isn't complete, it is sampled as black per the spec.
I create 8 textures through:
GLuint textures[8];
glGenTextures(8, textures);
for (int i=0; i<num; i++) {
NSString *fileName = [NSString stringWithFormat:#"image0%d", i + 1];
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image %#", fileName);
exit(1);
}
size_t kwidth = CGImageGetWidth(spriteImage);
size_t kheight = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(kwidth*kheight*4, sizeof(GLubyte));
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kwidth, kheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glGenerateMipmap(GL_TEXTURE_2D);
}
But get all black textures:
How could I solve this?
GLubyte * spriteData = (GLubyte *) calloc(kwidth*kheight*4, sizeof(GLubyte));
Creates a GLubyte array filled with zeros. Since you never modify spriteData anywhere, the textures are filled with zeros which is black.
I am interested in writing some code which processes an image using OpenGL ES 2.0 and then reads out the image back to memory again (to eventually be saved). I have some other code which does some more complex processing and just renders the image to display which works. I now want some code which runs the same processing but just saves the image. It doesn't need to be rendered to the screen.
I have created a frame buffer object which outputs to a texture. I then want to use glReadPixels to get the contents of the frame buffer back into memory. The following code snippet should just take a UIImage, resize it to fit onto a 512x512 canvas and then write it out as a 512x512 UIImage. I can't seem to get anything meaningful to display though. If I use an image smaller than 512x512 it does seem to render, but it's as if it is drawing straight from the texture storage (because I pad images to always be a power of 2 size). The image displays but if I change any of the drawing code, it doesn't affect it.
I'd really appreciate if you could give me some insight?
Thanks!
Here is my Vertex Shader:
attribute vec4 Position;
attribute vec4 SourceColour;
varying vec4 DestinationColour;
uniform vec2 ScreenSize;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
void main(void) {
DestinationColour = SourceColour;
vec4 newPosition = Position;
newPosition.x = Position.x / ScreenSize.x;
newPosition.y = Position.y / ScreenSize.y;
gl_Position = Position;
TexCoordOut = TexCoordIn;
}
Here is my fragment shader:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void)
{
lowp vec4 pixel = texture2D(Texture, TexCoordOut);
gl_FragColor = pixel;
}
Here is the main code body:
#import "SERootViewController.h"
#import <OpenGLES/ES2/gl.h>
typedef struct {
CGSize size;
CGPoint percentage;
GLuint id;
} Texture;
typedef struct {
float Position[3];
float TexCoord[2];
} Vertex;
size_t nextPowerOfTwo(size_t n)
{
size_t po2 = 2;
while(po2<n) {
po2 = po2*2;
}
return po2;
}
const GLubyte Indices[] = {
0, 1, 2,
2, 3, 0
};
#interface SERootViewController ()
{
EAGLContext *_context;
//OpenGL
GLuint _vertexBufferHandle;
GLuint _indexBufferHandle;
GLuint _vertexShaderHandle;
GLuint _fragmentShaderHandle;
GLuint _programHandle;
GLuint _positionHandle;
GLuint _texCoordHandle;
GLuint _textureHandle;
GLuint _screenSizeHandle;
GLuint _fbo;
GLuint _fboTexture;
Texture _imageTexture;
GLfloat _defaultScale;
Vertex *_vertices;
CGSize _screenSize;
//Cocoa
UIImageView *_imageView;
}
#end
#implementation SERootViewController
//////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Lifecycle
//////////////////////////////////////////////////////////////////////////
- (id)init
{
if ( (self = [super init]) != nil)
{
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
_vertices = malloc(sizeof(Vertex) * 4);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self __setupFrameBuffer];
[self __setupShaders];
CGSize size = CGSizeMake(512, 512);
[self __setScreenSize:size];
UIImage *image = [UIImage imageNamed:#"christmas.jpg"];
GLubyte *bytes = [self __bytesFromImage:image];
_imageTexture = [self __newTexture:bytes size:image.size];
free(bytes);
[self __setupVBOs:_imageTexture screenSize:size];
[self __renderTexture:_imageTexture];
[self __bindFrameBuffer];
GLubyte *imageBytes = [self __renderImage:size];
UIImage *newImage = [self __imageFromBytes:imageBytes size:size];
free(imageBytes);
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
_imageView.image = newImage;
_imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:_imageView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Methods
//////////////////////////////////////////////////////////////////////////
- (void)__setScreenSize:(CGSize)screenSize
{
_screenSize = screenSize;
glUniform2f(_screenSizeHandle, _screenSize.width, _screenSize.height);
}
- (void)__setupFrameBuffer
{
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &_fboTexture);
glBindTexture(GL_TEXTURE_2D, _fboTexture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _fboTexture, 0);
}
- (void)__bindFrameBuffer
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _fboTexture);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _fboTexture, 0);
}
- (GLuint) __compileShader:(NSString *)shaderStr type:(GLenum) type
{
const char *str = shaderStr.UTF8String;
int shaderStrLength = strlen(str);
GLuint shaderHandle = glCreateShader(type);
glShaderSource(shaderHandle, 1, &str, &shaderStrLength);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE)
{
GLchar messages[512];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSLog(#"Shader Error: %s", messages);
}
return shaderHandle;
}
- (void)__setupVBOs:(Texture)texture screenSize:(CGSize)screenSize
{
glDeleteBuffers(1, &_vertexBufferHandle);
glDeleteBuffers(1, &_indexBufferHandle);
_defaultScale = MIN( (screenSize.height/texture.size.height), (screenSize.width/texture.size.width) );
GLfloat width = texture.size.width*_defaultScale;
GLfloat height = texture.size.height*_defaultScale;
Vertex vertices[] = {{{ width/2, height/2, 0}, {texture.percentage.x, texture.percentage.y}},
{{ width/2, 0, 0}, {texture.percentage.x, 0}},
{{0, 0, 0}, {0, 0}},
{{0, height/2, 0}, {0, texture.percentage.y}}
};
memcpy(_vertices, vertices, sizeof(vertices));
glGenBuffers(1, &_vertexBufferHandle);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferHandle);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), _vertices, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
- (void) __setupShaders
{
NSString *vertexFilePath = [[NSBundle mainBundle] pathForResource:#"vertexShader" ofType:#"glsl"];
NSString *vertexShader = [NSString stringWithContentsOfFile:vertexFilePath encoding:NSUTF8StringEncoding error:nil];
NSString *fragmentFilePath = [[NSBundle mainBundle] pathForResource:#"fragmentShader" ofType:#"glsl"];
NSString *fragmentShader = [NSString stringWithContentsOfFile:fragmentFilePath encoding:NSUTF8StringEncoding error:nil];
_vertexShaderHandle = [self __compileShader:vertexShader type:GL_VERTEX_SHADER];
_fragmentShaderHandle = [self __compileShader:fragmentShader type:GL_FRAGMENT_SHADER];
_programHandle = glCreateProgram();
glAttachShader(_programHandle, _vertexShaderHandle);
glAttachShader(_programHandle, _fragmentShaderHandle);
glLinkProgram(_programHandle);
GLint linkSuccess;
glGetProgramiv(_programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE)
{
GLchar messages[512];
glGetProgramInfoLog(_programHandle, sizeof(messages), 0, &messages[0]);
NSLog(#"GLSL ERROR: %s", messages);
}
glUseProgram(_programHandle);
_positionHandle = glGetAttribLocation(_programHandle, "Position");
_texCoordHandle = glGetAttribLocation(_programHandle, "TexCoordIn");
_screenSizeHandle = glGetUniformLocation(_programHandle, "ScreenSize");
_textureHandle = glGetUniformLocation(_programHandle, "Texture");
glEnableVertexAttribArray(_positionHandle);
glEnableVertexAttribArray(_texCoordHandle);
}
- (Texture)__newTexture:(void *)bytes size:(CGSize)size
{
Texture texture;
size_t wpo2 = nextPowerOfTwo(size.width);
size_t hpo2 = nextPowerOfTwo(size.height);
texture.size = size;
texture.percentage = CGPointMake((float)size.width / (float)wpo2, (float)size.height / (float)hpo2);
void * texData = (GLubyte *) malloc(wpo2*hpo2*4*sizeof(GLubyte));
memset(texData, 1, sizeof(GLubyte)*size.width*4);
for(GLuint i=0;i<size.height;i++)
{
memcpy(&texData[wpo2*i*4], &bytes[(int)size.width*i*4], sizeof(GLubyte)*(int)size.width*4);
}
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &texture.id);
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)wpo2, (GLsizei)hpo2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
free(texData);
return texture;
}
-(void) __renderTexture:(Texture) texture
{
glClearColor(0.0f, 0.5, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glVertexAttribPointer(_positionHandle, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_texCoordHandle, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture.id);
glUniform1i(_textureHandle, 1);
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),GL_UNSIGNED_BYTE, 0);
}
- (GLubyte *)__renderImage:(CGSize)size
{
GLubyte *image = malloc(size.width*size.height*4*sizeof(GLubyte));
glReadPixels(0, 0, size.width, size.height, GL_RGBA, GL_UNSIGNED_BYTE, image);
return image;
}
- (GLubyte *)__bytesFromImage:(UIImage *)image
{
GLint maxTextureSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
CGFloat maxSize = MAX(image.size.width, image.size.height);
CGFloat width = image.size.width;
CGFloat height = image.size.height;
if (maxSize > maxTextureSize)
{
CGFloat scale = maxTextureSize/maxSize;
width = roundf(width*scale);
height = roundf(height*scale);
}
CGImageRef spriteImage = image.CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image");
exit(1);
}
GLubyte * imgData = (GLubyte *) malloc(width*height*4*sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(imgData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
UIGraphicsPushContext(spriteContext);
CGContextSaveGState(spriteContext);
[image drawInRect:CGRectMake(0, 0, width, height)];
CGContextRestoreGState(spriteContext);
UIGraphicsPopContext();
CGContextRelease(spriteContext);
return imgData;
}
- (UIImage *)__imageFromBytes:(GLubyte *)bytes size:(CGSize)size
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bytes, (size.width * size.height * 4), NULL);
// set up for CGImage creation
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * size.width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef imageRef = CGImageCreate(size.width, size.height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// make UIImage from CGImage
UIImage *newUIImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return newUIImage;
}
#end
The following code can be a complete template for color-picking with glkit. I just need to understand why all the pixels in the offscreen buffer are colored (0,0,0) as printed below to the nslog.
Note: the vertices array is defined in an header file as a const array it is displayed well on the screen in another section of my project (my problem is with the offscreen).
Header file:
typedef struct {
float Position[3];
float Color[4];
float TexCoord[2];
float Normal[3];
} Vertex;
//Vertices array format: {{vertex.x, vertex.y, vertex.z}, {color.R, color.G, color.B, color.alpha}, {texture.U, texture.V}, {normal.x, normal.y, normal.z}},
const Vertex parparit51OBJVertices[] = {
{{0.057, -0.088, -0.155},{1,1,1,1},{0.848, 0.810}, {0.329, -0.157, -0.931}},
{{0.056, -0.035, -0.165},{1,1,1,1},{0.848, 0.811}, {0.338, -0.139, -0.931}}, ......
In the viewController code:
GLuint _pickFBO = 0;
int32_t glVertexAttributeBufferID = 0;
- (IBAction) tapGesture:(id)sender
{
if ([(UITapGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded) {
NSLog( #"In tap ended" );
CGPoint tapLocation = [(UITapGestureRecognizer *)sender locationInView:self.view];
int tt = [self findMeshByPoint:tapLocation];
}
}
- (NSUInteger)findMeshByPoint:(CGPoint)point
{
//In openGL the y axis starts from the bottom of the screen
point.y = self.view.bounds.size.height - point.y;
GLKView *glView = (GLKView *)self.view;
NSAssert([glView isKindOfClass:[GLKView class]],
#"View controller's view is not a GLKView");
// Make the view's context current
[EAGLContext setCurrentContext:glView.context];
_height = ((GLKView *)self.view).drawableHeight;
_width = ((GLKView *)self.view).drawableWidth;
self.effect.useConstantColor = GL_TRUE;
self.effect.colorMaterialEnabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(1.0f,1.0f,1.0f,1.0f);
glBindVertexArrayOES(0);
glDisable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glLineWidth(2.0F);
// Important to turn light off !!!
self.effect.light0.enabled = GL_TRUE;
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
//this crashes the code
//glEnableVertexAttribArray(GLKVertexAttribColor);
self.effect.constantColor = GLKVector4Make( 0.0f, //This should be meshId/255.0f
0.8f, 0.8f, 1.0f);
if(0 == _glVertexAttributeBufferID)
{
GLuint glName;
glGenBuffers(1, // STEP 1
&glName);
glBindBuffer(GL_ARRAY_BUFFER, // STEP 2
glName);
glBufferData( // STEP 3
GL_ARRAY_BUFFER, // Initialize buffer contents
sizeof(parparit51OBJVertices), parparit51OBJVertices,
GL_STATIC_DRAW); // Hint: cache in GPU memory
_glVertexAttributeBufferID = glName;
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
NSLog(#"Error creating buffer %i. glError: 0x%04X", glName, err);
}
}
else
{
glBindBuffer(GL_ARRAY_BUFFER,
_glVertexAttributeBufferID);
}
[self buildFBO];
glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);
//glViewport(0, 0, _width, _height);
//???
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), (GLvoid*) (sizeof(float) * 9));
GLKMatrix4 modelViewMatrixForParparit = GLKMatrix4MakeTranslation(0.0f, 0.3f, -3.5f );
modelViewMatrixForParparit = GLKMatrix4Scale(modelViewMatrixForParparit, 1.0, 1.0, 1.0);
self.effect.transform.modelviewMatrix = modelViewMatrixForParparit;
self.effect.constantColor = GLKVector4Make( 0.8f, 0.3f, 0.3f, 1.0f );
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, sizeof(parparit51OBJVertices) / sizeof(Vertex));
const GLfloat width = [glView drawableWidth];
const GLfloat height = [glView drawableHeight];
NSAssert(0 < width && 0 < height, #"Invalid drawble size");
int blackPixelsCounter = 0;
int coloredPixelsCounter = 0;
GLubyte savePixelColor[4] = {0, };
bool bFoundDifferentColors = NO;
GLubyte pixelColor[4]; // Red, Green, Blue, Alpha color
glReadPixels(50,
50,
1,
1,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixelColor);
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
savePixelColor[0] = pixelColor[0];
savePixelColor[1] = pixelColor[1];
savePixelColor[2] = pixelColor[2];
for (GLint xx=0; xx<_width; xx++) {
for (GLint yy=0; yy<_height; yy++) {
glReadPixels(xx,
yy,
1,
1,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixelColor);
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
if ( (savePixelColor[0] != pixelColor[0]) || (savePixelColor[1] != pixelColor[1]) || (savePixelColor[2] != pixelColor[2]) )
{
bFoundDifferentColors = YES;
}
if ( (pixelColor[0] !=0) || (pixelColor[1] !=0) || (pixelColor[2] !=0) ) {
//NSLog(#"pixelColor[0]=%i, pixelColor[1]=%i, pixelColor[2]=%i", pixelColor[0], pixelColor[1], pixelColor[2] );
coloredPixelsCounter++;
}
else
{
blackPixelsCounter++;
}
}
}
NSLog( #"colored pixels=%i black pixels=%i", coloredPixelsCounter, blackPixelsCounter );
if ( bFoundDifferentColors )
{
NSLog( #"Found at least 2 different pixels colors in picking buffer !" );
}
else
{
NSLog( #"All pixels have the same color: %i, %i, %i", savePixelColor[0], savePixelColor[1], savePixelColor[2]);
}
NSLog( #"******* 9" );
//--- at the end !!! -------
// Restore OpenGL state that pickTerrainEffect changed
glBindFramebuffer(GL_FRAMEBUFFER, 0); // default frame buffer
//glViewport(0, 0, _width, _height); // full area of glView
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
NSLog( #"******* 10" );
return 0;
}
//tap-11
-(void) buildFBO
{
NSLog(#"before: buildFBO._pickFBO=%i", _pickFBO );
if ( 0 == _pickFBO )
{
NSLog(#"buildFBO._pickFBO=%i", _pickFBO );
GLuint colorRenderbuffer;
//GLuint framebuffer;
glGenFramebuffers(1, &_pickFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, _width, _height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER, colorRenderbuffer);
GLuint depthRenderbuffer;
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _width, _height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE)
{
NSLog(#"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
//+++tbd+++UtilityPickTerrainEffectDestroyFBO(fboName);
return;
}
//#ifdef DEBUG
// { // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
// }
//#endif
}
}
I think you need to close the texture out before reading from it. My color picker implementation is working fine. But I remember that for about 2 days I was also getting only 0,0,0,0.
Here's a clue for you. Is the pixel you're getting (0,0,0,0) or (0,0,0,1)? Cause you only mention that it's (0,0,0). Since you have a clearColor to (0,0,0,1), if you're getting an alpha of zero it's not reading the buffer at all.
I recommend doing it in 2 steps. One method to draw the color picker. Another to read from it. here's my exact code for reading my color buffer:
-(void)process3DTouch
{
if (colorPickerNeedsRedrawn) {
[self drawColorPicker];
}
glBindFramebuffer(GL_FRAMEBUFFER, pickerFramebuffer); // speed note - will bind twice if we just had to draw it
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureInfo[TEX_PICKER].texture, 0);
Byte pixelColor[4] = {0,0,0,0};
glReadPixels(colorPickerTouchPosition.x * backingScale, (backingHeight - (colorPickerTouchPosition.y * backingScale)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor);
ColorSpec pickerColor = ColorSpecMake((float)pixelColor[0]/255, (float)pixelColor[1]/255, (float)pixelColor[2]/255, (float)pixelColor[3]/255);
//NSLog(#"what is pixelColor at %f,%f = %f %f %f %f", colorPickerTouchPosition.x, colorPickerTouchPosition.y, pickerColor.r,pickerColor.g,pickerColor.b,pickerColor.a);
if (pickerColor.a == 0.0) {
whatTouched=TOUCHED_NOTHING;
whatSecondaryTouched=TOUCHED_NOTHING;
NSLog(#"touched nothing");
processColorPicker=false;
return;
}
// now look up the item index from the color
int itemIndex = [self itemIndexFromColor:pickerColor];
[self handleTouchedItem: itemIndex];
processColorPicker=false;
}
Another good debugging method for color pickers is to draw the color picker and then draw the color picker texture to the screen so you can see if you have something else going on in the drawing of the picker.