opengl-es-2.0

Renderice mĂșltiples objetos con OpenGL ES 2.0



opengl-es-2.0 (6)

Al usar sombreadores, es posible usar el mismo programa para todos los objetos sin tener que compilar, vincular y crear uno para cada uno. Para hacer esto, simplemente almacene el valor de GLuint en el programa y luego para cada objeto "glUseProgram (programId);". Como resultado de la experiencia personal, uso un singleton para gestionar las estructuras GLProgram .. (incluido a continuación :))

@interface TDShaderSet : NSObject { NSMutableDictionary *_attributes; NSMutableDictionary *_uniforms; GLuint _program; } @property (nonatomic, readonly, getter=getUniforms) NSMutableDictionary *uniforms; @property (nonatomic, readonly, getter=getAttributes) NSMutableDictionary *attributes; @property (nonatomic, readonly, getter=getProgram) GLuint program; - (GLint) uniformLocation:(NSString*)name; - (GLint) attribLocation:(NSString*)name; @end @interface TDProgamManager : NSObject + (TDProgamManager *) sharedInstance; + (TDProgamManager *) sharedInstanceWithContext:(EAGLContext*)context; @property (nonatomic, readonly, getter=getAllPrograms) NSArray *allPrograms; - (BOOL) loadShader:(NSString*)shaderName referenceName:(NSString*)refName; - (TDShaderSet*) getProgramForRef:(NSString*)refName; @end @interface TDProgamManager () { NSMutableDictionary *_glPrograms; EAGLContext *_context; } @end @implementation TDShaderSet - (GLuint) getProgram { return _program; } - (NSMutableDictionary*) getUniforms { return _uniforms; } - (NSMutableDictionary*) getAttributes { return _attributes; } - (GLint) uniformLocation:(NSString*)name { NSNumber *number = [_uniforms objectForKey:name]; if (!number) { GLint location = glGetUniformLocation(_program, name.UTF8String); number = [NSNumber numberWithInt:location]; [_uniforms setObject:number forKey:name]; } return number.intValue; } - (GLint) attribLocation:(NSString*)name { NSNumber *number = [_attributes objectForKey:name]; if (!number) { GLint location = glGetAttribLocation(_program, name.UTF8String); number = [NSNumber numberWithInt:location]; [_attributes setObject:number forKey:name]; } return number.intValue; } - (id) initWithProgramId:(GLuint)program { self = [super init]; if (self) { _attributes = [[NSMutableDictionary alloc] init]; _uniforms = [[NSMutableDictionary alloc] init]; _program = program; } return self; } @end @implementation TDProgamManager { @private } static TDProgamManager *_sharedSingleton = nil; - (NSArray *) getAllPrograms { return _glPrograms.allValues; } - (TDShaderSet*) getProgramForRef:(NSString *)refName { return (TDShaderSet*)[_glPrograms objectForKey:refName]; } - (BOOL) loadShader:(NSString*)shaderName referenceName:(NSString*)refName { NSAssert(_context, @"No Context available"); if ([_glPrograms objectForKey:refName]) return YES; [EAGLContext setCurrentContext:_context]; GLuint vertShader, fragShader; NSString *vertShaderPathname, *fragShaderPathname; // Create shader program. GLuint _program = glCreateProgram(); // Create and compile vertex shader. vertShaderPathname = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"vsh"]; if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) { NSLog(@"Failed to compile vertex shader"); return NO; } // Create and compile fragment shader. fragShaderPathname = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"fsh"]; if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) { NSLog(@"Failed to compile fragment shader"); return NO; } // Attach vertex shader to program. glAttachShader(_program, vertShader); // Attach fragment shader to program. glAttachShader(_program, fragShader); // Bind attribute locations. // This needs to be done prior to linking. glBindAttribLocation(_program, GLKVertexAttribPosition, "a_position"); glBindAttribLocation(_program, GLKVertexAttribNormal, "a_normal"); glBindAttribLocation(_program, GLKVertexAttribTexCoord0, "a_texCoord"); // Link program. if (![self linkProgram:_program]) { NSLog(@"Failed to link program: %d", _program); if (vertShader) { glDeleteShader(vertShader); vertShader = 0; } if (fragShader) { glDeleteShader(fragShader); fragShader = 0; } if (_program) { glDeleteProgram(_program); _program = 0; } return NO; } // Release vertex and fragment shaders. if (vertShader) { glDetachShader(_program, vertShader); glDeleteShader(vertShader); } if (fragShader) { glDetachShader(_program, fragShader); glDeleteShader(fragShader); } TDShaderSet *_newSet = [[TDShaderSet alloc] initWithProgramId:_program]; [_glPrograms setValue:_newSet forKey:refName]; return YES; } - (BOOL) compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file { GLint status; const GLchar *source; source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]; if (!source) { NSLog(@"Failed to load vertex shader"); return NO; } *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; } #pragma mark - Singleton stuff... Don''t mess with this other than proxyInit! - (void) proxyInit { _glPrograms = [[NSMutableDictionary alloc] init]; } - (id) init { Class myClass = [self class]; @synchronized(myClass) { if (!_sharedSingleton) { if (self = [super init]) { _sharedSingleton = self; [self proxyInit]; } } } return _sharedSingleton; } + (TDProgamManager *) sharedInstance { @synchronized(self) { if (!_sharedSingleton) { _sharedSingleton = [[self alloc] init]; } } return _sharedSingleton; } + (TDProgamManager *) sharedInstanceWithContext:(EAGLContext*)context { @synchronized(self) { if (!_sharedSingleton) { _sharedSingleton = [[self alloc] init]; } _sharedSingleton->_context = context; } return _sharedSingleton; } + (id) allocWithZone:(NSZone *)zone { @synchronized(self) { if (!_sharedSingleton) { return [super allocWithZone:zone]; } } return _sharedSingleton; } + (id) copyWithZone:(NSZone *)zone { return self; } @end

Tenga en cuenta que una vez que se transfieren los espacios de datos (atributos / uniformes), NO DEBE pasarlos en cada ciclo de representación, sino solo cuando se invaliden. Esto resulta en una ganancia de rendimiento de GPU seria.

Por el lado VBO de las cosas, la respuesta anterior explica la mejor manera de lidiar con esto. Por el lado de la orientación de la ecuación, necesitarás un mecanismo para anidar tdobjects dentro de cada uno (similar a UIView y los niños en iOS) y luego evaluar rotaciones relativas a los padres, etc.

Buena suerte !

Estoy tratando de aprender OpenGL ES 2.0 para hacer un poco de desarrollo de juegos de iPhone. He leído varios tutoriales y algunas de las especificaciones de OpenGL ES 2.0. Todos los ejemplos que he visto han creado una única malla, la han cargado en un búfer de vértices y luego la han renderizado (con la traducción esperada, la rotación, el degradado, etc.)

Mi pregunta es esta: ¿cómo renderizas múltiples objetos en tu escena que tienen mallas diferentes y se mueven independientemente? Si tengo un automóvil y una motocicleta, por ejemplo, ¿puedo crear 2 búferes de vértice y guardar los datos de malla para ambos por cada llamada de renderizado, y luego simplemente enviar diferentes matrices para el sombreador para cada objeto? ¿O necesito traducir las mallas de alguna manera y luego combinarlas en una sola malla para que se puedan procesar en una sola pasada? Estoy buscando más de la estructura de estrategia / programa de alto nivel en lugar de ejemplos de código. Creo que tengo el modo mental incorrecto de cómo funciona esto.

¡Gracias!


Espero poder contribuir a esta publicación más antigua, porque me comprometí a resolver este problema de otra manera. Como el que pregunta, he visto muchos ejemplos de "un solo objeto". Me empeñé en colocar todos los vértices en un solo VBO, y luego guardar el desplazamiento en la posición de ese objeto (por objeto), en lugar de un controlador de búfer. Funcionó. El desplazamiento se puede dar como un parámetro para glDrawElements como a continuación. Parece obvio en retrospectiva, pero no estaba convencido hasta que lo vi funcionar. Tenga en cuenta que he estado trabajando con "puntero de vértice" en lugar del "puntero de atributo de vértice" más actual. Estoy trabajando en este último para poder aprovechar sombreadores. Todos los objetos "se unen" al mismo búfer de vértice, antes de llamar a "dibujar elementos".

gl.glVertexPointer( 3, GLES20.GL_FLOAT, 0, vertexBufferOffset ); GLES20.glDrawElements( GLES20.GL_TRIANGLES, indicesCount, GLES20.GL_UNSIGNED_BYTE, indexBufferOffset );

No encontré en ningún lugar explicado cuál era el propósito de este desplazamiento, así que me arriesgué. Además, este problema: debe especificar el desplazamiento en bytes, no en vértices o flotantes. Es decir, multiplique por cuatro para obtener la posición correcta.


La mejor forma que encontré para hacer esto es usar VAOs además de VBOs.

Primero te responderé tu pregunta usando solo VBO.

Antes que nada, supongamos que tiene las dos mallas de sus dos objetos almacenadas en las siguientes matrices:

GLuint _vertexBufferCube1; GLuint _vertexBufferCube2;

dónde:

GLfloat gCubeVertexData1[36] = {...}; GLfloat gCubeVertexData2[36] = {...};

Y también tienes que vertix buffers:

GLuint _vertexBufferCube1; GLuint _vertexBufferCube2;

Ahora, para dibujar esos dos cubos (sin VAO), tienes que hacer algo como eso: en la función dibujar (desde la plantilla de OpenGLES):

//Draw first object, bind VBO, adjust your attributes then call DrawArrays glGenBuffers(1, &_vertexBufferCube1); glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1); glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0)); glEnableVertexAttribArray(GLKVertexAttribNormal); glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12)); glDrawArrays(GL_TRIANGLES, 0, 36); //Repeat for second object: glGenBuffers(1, &_vertexBufferCube2); glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2); glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0)); glEnableVertexAttribArray(GLKVertexAttribNormal); glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12)); glUseProgram(_program); glDrawArrays(GL_TRIANGLES, 0, 36);

Esto te responderá la pregunta. Pero ahora, para usar VAO, su código de función de dibujo es mucho más simple (lo cual es bueno porque es la función repetida):

Primero definirá VAOs:

GLuint _vertexArray1; GLuint _vertexArray2;

y luego hará todos los pasos anteriores en el método draw, lo hará en la función setupGL pero luego de enlazar con el VAO. Luego, en su función de dibujar, simplemente se une al VAO que desea.

VAO aquí es como un perfil que contiene muchas propiedades (imagine un perfil de dispositivo inteligente). En lugar de cambiar el color, el escritorio, las fuentes, etc. cada vez que desee cambiarlos, haga eso una vez y guárdelo con un nombre de perfil. Entonces solo cambias el perfil.

Entonces hazlo una vez, dentro de setupGL, luego cambias entre ellos en draw.

Por supuesto, puede decir que podría haber puesto el código (sin VAO) en una función y llamarlo. Eso es cierto, pero los VAO son más eficientes de acuerdo con Apple:

http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html#//apple_ref/doc/uid/TP40008793-CH107-SW1

Ahora al código:

En setupGL:

glGenVertexArraysOES(1, &_vertexArray1); //Bind to first VAO glBindVertexArrayOES(_vertexArray1); glGenBuffers(1, &_vertexBufferCube1); //All steps from this one are done to first VAO only glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1); glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0)); glEnableVertexAttribArray(GLKVertexAttribNormal); glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12)); glGenVertexArraysOES(1, &_vertexArray2); // now bind to the second glBindVertexArrayOES(_vertexArray2); glGenBuffers(1, &_vertexBufferCube2); //repeat with the second mesh glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2); glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0)); glEnableVertexAttribArray(GLKVertexAttribNormal); glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12)); glBindVertexArrayOES(0);

Entonces, finalmente, en tu método de sorteo:

glBindVertexArrayOES(_vertexArray1); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArrayOES(_vertexArray2); glDrawArrays(GL_TRIANGLES, 0, 36);


Mantiene buffers de vértices / índices separados para diferentes objetos, sí. Por ejemplo, podría tener una clase RenderedObject y cada instancia tendría su propio búfer de vértices. Un RenderedObject podría tomar sus vértices de una malla de la casa, uno podría provenir de una malla de caracteres, etc.

Durante el renderizado, configura la transformación / rotación / sombreado apropiado para el búfer de vértices con el que está trabajando, tal vez algo como:

void RenderedObject::render() { ... //set textures/shaders/transformations glBindBuffer(GL_ARRAY_BUFFER, bufferID); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount); ... }

Como se menciona en la otra respuesta, el bufferID es solo un GLuint, no todo el contenido del buffer. Si necesita más detalles sobre cómo crear buffers de vértices y rellenarlos con datos, me complace agregarlos también.


Me doy cuenta de que esta es una publicación más antigua, pero estaba tratando de encontrar instrucciones sobre cómo renderizar múltiples objetos dentro de OpenGL . Encontré un gran tutorial, que describe cómo renderizar múltiples objetos y podría extenderse fácilmente para representar objetos de diferentes tipos (es decir, un cubo, una pirámide).

El tutorial que estoy publicando también describe cómo renderizar objetos usando GLKit . Lo encontré útil y pensé que volvería a publicarlo aquí. ¡Espero que te ayude a ti también!

http://games.ianterrell.com/opengl-basics-with-glkit-in-ios5-encapsulated-drawing-and-animation/


Si las mallas son diferentes, las guardas en diferentes buffers de vértices. Si son similares (por ejemplo, animación, color), pasa argumentos al sombreador. Solo debe mantener los identificadores en las VBO, no en los datos de los vértices si no planea animar el objeto en el lado de la aplicación. La animación del lado del dispositivo es posible.