iOS - So zeichnen Sie ein YUV-Bild mit openGL
Derzeit versuche ich, ein Bild mit openGL zu zeichnen (das Bild wird sehr oft aktualisiert und muss daher neu gezeichnet werden). Bisher habe ich mein Bild von YUV in RGB konvertiert und dieses neue Bild dann zum Zeichnen mit openGL verwendet. Alles hat gut funktioniert, aber der Konvertierungsprozess war nicht besonders schnell.
Ich versuche jetzt, den Code so zu ändern, dass die Konvertierung in den openGL-Shadern erledigt wird. Nachdem ich mich umgesehen habe, habe ich ein paar Codefragmente gefunden (insbesondere die Shader und den Großteil meiner renderImage-Funktion), die mir geholfen haben, eine Grundlinie zu erhalten, aber ich kann das Bild scheinbar nicht richtig zeichnen - alles, was ich jemals hatte siehe ist ein schwarzes Bild.
Es ist sehr wahrscheinlich, dass mir etwas sehr Einfaches und Wichtiges fehlt - meine Erfahrung mit openGL ist eher begrenzt. Wenn jemand einen Blick darauf werfen und feststellen kann, dass etwas nicht stimmt, lassen Sie es mich bitte wissen.
Ich sollte darauf hinweisen, ich versuche iOS 4.x zu unterstützen, daher sollte CVOpenGLESTextureCacheCreateTextureFromImage nicht verwendbar sein (und ich bin nicht wirklich sicher, wie ich es einrichten soll, auch wenn ich wollte).
Jede Hilfe wäre dankbar. Code unten -
Mein Vertex-Shader:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
Fragment Shader:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 textureCoordinate;
uniform sampler2D videoFrame;
uniform sampler2D videoFrameUV;
const mat3 yuv2rgb = mat3(
1, 0, 1.2802,
1, -0.214821, -0.380589,
1, 2.127982, 0
);
void main() {
vec3 yuv = vec3(
1.1643 * (texture2D(videoFrame, textureCoordinate).r - 0.0625),
texture2D(videoFrameUV, textureCoordinate).r - 0.5,
texture2D(videoFrameUV, textureCoordinate).a - 0.5
);
vec3 rgb = yuv * yuv2rgb;
gl_FragColor = vec4(rgb, 1.0);
}
Die renderImage Funktion:
-(void)renderImage:(ImageBuffer *)image
{
if (image)
{
int bufferHeight = image->GetHeight();
int bufferWidth = image->GetWidth();
if(!imageTexture){
// Dealing with the Y portion of the YCbCr
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &imageTexture);
//Bind Y texture
glBindTexture(GL_TEXTURE_2D, imageTexture);
glUniform1i(uniforms[UNIFORM_VIDEOFRAME], 0);
// For fitting
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// This is necessary for non-power-of-two textures
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, bufferWidth, bufferHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image->GetPlanePointer(0));
// Dealing with the CbCr potion of the YCbCr
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &imageTextureUV);
//Bind CbCr texture
glBindTexture(GL_TEXTURE_2D, imageTextureUV);
glUniform1i(uniforms[UNIFORM_VIDEOFRAMEUV], 1);
// For fitting
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// This is necessary for non-power-of-two textures
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_ALPHA, bufferWidth/2, bufferHeight/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image->GetPlanePointer(1));
}
[self drawFrame];
}
}
Und zum Schluss die drawFrame-Funktion:
- (void)drawFrame
{
[self setFramebuffer];
// Replace the implementation of this method to do your own custom drawing.
static const GLfloat squareVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat textureVertices[] = {
// 1.0f, 1.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
// 0.0f, 0.8f,
// 1.0f, 0.8f,
// 0.0f, 0.2f,
// 1.0f, 0.2f
};
static float transY = 0.0f;
if ([context API] == kEAGLRenderingAPIOpenGLES2) {
// Use shader program.
glUseProgram(program);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, imageTexture);
// Update attribute values.
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, imageTextureUV);
// Update attribute values.
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
// Validate program before drawing. This is a good check, but only really necessary in a debug build.
// DEBUG macro must be defined in your debug configurations if that's not already the case.
#if defined(DEBUG)
if (![self validateProgram:program]) {
NSLog(@"Failed to validate program: %d", program);
return;
}
#endif
} else {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
transY += 0.075f;
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
[self presentFramebuffer];
}