Related
So I've been moving from a legacy profile to Core Profile for the last couple of days. I'd already moved much of my functionality to use VBOs and shaders, so I thought it wouldn't take that much work.
However, I can't get my new core profile contexts to draw anything at all using glDrawElements. My application manipulates textures in an app wide background openglContext, the GUI shows various stages of that using OpenGL views that share contexts with the background context.
Each texture object builds it's own VBOs for texture coords and colours as required, leaving me only to provide new vertex VBOs for displaying in views. The number of vertices and their drawing is standardised so I can share Index buffers.
This is my Pixel format shared between all contexts:
+ (NSOpenGLPixelFormat *) defaultPixelFormat
{
NSOpenGLPixelFormatAttribute attrs[] =
{
kCGLPFAOpenGLProfile, kCGLOGLPVersion_3_2_Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFABackingStore,
NSOpenGLPFAAllowOfflineRenderers,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFADepthSize, 24,
0
};
NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
return pixFmt;
}
This is a short example of how I setup my VAOs without using a texture, I just want to draw something!
- (void) genTestVao
{
// Generate buffers first to simulate app environment
GLfloat verts[] = {
0.0, 0.0, 0.0, 0.0,
100.0, 0.0, 0.0, 0.0,
0.0, 100.0, 0.0, 0.0,
100.0, 100.0, 0.0, 0.0
};
GLfloat colors[] = {
1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 0.0
};
GLushort indices[] = {0, 1, 2, 3};
if (_testVBuffer) {
glDeleteBuffers(1, &_testVBuffer);
}
if (_testCBuffer) {
glDeleteBuffers(1, &_testCBuffer);
}
if (_testIBuffer) {
glDeleteBuffers(1, &_testIBuffer);
}
glGenBuffers(1, &_testVBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _testVBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_DYNAMIC_DRAW);
glGenBuffers(1, &_testCBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _testCBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// vert and colors buffers done
glGenBuffers(1, &_testIBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _testIBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Index buffer done
// Generate VAO with pre stored buffers
if (_testVAO) {
glDeleteVertexArrays(1, &_testVAO);
}
glGenVertexArrays(1, &_testVAO);
glBindVertexArray(_testVAO);
// Vertex
glBindBuffer(GL_ARRAY_BUFFER, _testVBuffer);
glEnableVertexAttribArray(kSCGLVertexAttribPosition);
glVertexAttribPointer(kSCGLVertexAttribPosition, 4, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 4, 0);
// Colors
glBindBuffer(GL_ARRAY_BUFFER, _testCBuffer);
glEnableVertexAttribArray(kSCGLColorAttribPosition);
glVertexAttribPointer(kSCGLColorAttribPosition, 4, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 4, 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Setting up a view matrix:
_mvpMatrix = GLKMatrix4Multiply(
GLKMatrix4MakeOrtho(0.0, self.bounds.size.width, 0.0, self.bounds.size.height, -1.0, 1.0),
GLKMatrix4Identity);
The drawing code:
glUseProgram(self.testShader.shaderProgram);
glUniformMatrix4fv(self.testShader.mvpMatrixLocation, 1, GL_FALSE, self.mvpMatrix.m);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _testIBuffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_RECTANGLE, 0);
glUseProgram(0);
The vertex Shader:
#version 150
in vec4 position;
in vec4 color;
uniform mat4 mvpMatrix;
out vec4 vertex_color;
void main()
{
// perform standard transform on vertex
gl_Position = position * mvpMatrix;
vertex_color = color;
}
The Fragment Shader:
#version 150
in vec4 vertex_color;
out vec4 colourout;
void main()
{
colourout = vertex_color;
}
And finaly the code that links shader and vertices and binds the attribute locations:
- (BOOL) createProgramObjectWithVertexShader:(GLuint) vertShader withFragShader:(GLuint) fragShader
{
_shaderProgram = glCreateProgram();
glAttachShader(_shaderProgram, vertShader);
glBindAttribLocation(_shaderProgram, kSCGLVertexAttribPosition, "position");
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
NSLog(#"Error generated getting position!");
if (error == GL_INVALID_VALUE) {
NSLog(#"Invalid value");
} else if (error == GL_INVALID_OPERATION) {
NSLog(#"Invalid operation");
} else {
NSLog(#"unexpected error");
}
}
glBindAttribLocation(_shaderProgram, kSCGLColorAttribPosition, "color");
error = glGetError();
if (error != GL_NO_ERROR) {
NSLog(#"Error generated getting color!");
if (error == GL_INVALID_VALUE) {
NSLog(#"Invalid value");
} else if (error == GL_INVALID_OPERATION) {
NSLog(#"Invalid operation");
} else {
NSLog(#"unexpected error");
}
}
//glBindAttribLocation(_shaderProgram, kSCGLNormalAttribPosition, "normal");
//glBindAttribLocation(_shaderProgram, kSCGLTexCoordPosition, "texcoord");
error = glGetError();
if (error != GL_NO_ERROR) {
NSLog(#"Error generated getting texcoord!");
if (error == GL_INVALID_VALUE) {
NSLog(#"Invalid value");
} else if (error == GL_INVALID_OPERATION) {
NSLog(#"Invalid operation");
} else {
NSLog(#"unexpected error");
}
}
glAttachShader(_shaderProgram, fragShader);
glLinkProgram(_shaderProgram);
glDeleteShader(vertShader);
glDeleteShader(fragShader);
GLint result = GL_FALSE;
GLint infoLogLength = 0;
glGetProgramiv(_shaderProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0) {
char errMsg[infoLogLength];
glGetProgramInfoLog(_shaderProgram, infoLogLength, &infoLogLength, errMsg);
NSString *msg = [NSString stringWithUTF8String:errMsg];
NSLog(#"Self = %#", self);
NSLog(#"Validate program failed with %#", msg);
if (![msg hasPrefix:#"WARNING:"]) {
NSLog(#"Fatal");
glDeleteProgram(_shaderProgram);
return NO;
}
}
if (![self getUniformLocations]) {
NSLog(#"Failed getting uniform variables for %#", self.shaderName);
glDeleteProgram(_shaderProgram);
return NO;
}
return YES;
}
I'm sure it's something simple, but I just can't see what it is and it's driving me crazy. The opengl view is setup correctly, if I clear it with colours, it shows correctly, it just won't draw my elements....
Part of the reason I'm moving to core profile is to share code with an iOS app, except for some simple changes, most of my opengl code is es compatible.
EDIT 1:
I created a rough and ready XCode project that shows the basics on GitHub. The app delegate holds a base shared openglContext and loads the test shader. The openGLView is based on a shared context from the App delegate:
EDIT 2:
I updated the project with a couple of corrections, now something draws, but it's not what I expect. It's a single colour where I've used multiple, and it's in the top right when I expect it to be in the bottom left.
An apple guy posted the answer to my problems on the apple devforums.
The reason I couldn't get anything to draw was the order of the matrix and position multiplication matters in the vertex shader. So the line:
gl_Position = position * mvpMatrix;
Should be:
gl_Position = mvpMatrix * position;
EDIT: Removed second part of answer as per Reto's comment.
I need to draw and texture a sphere. The project I am working on will involve a considerable amount of graphics which led me down the route of using VBO's.
I'm currently experiencing trouble trying to texture the sphere and all attempts have led me to a solid shaded sphere each time - with no visible texture. I am most likely doing something silly - but after many attempts, I am no further into understanding whether the problem is because of the texture loading, bad U/V Texture coordinates or using the wrong shaders..
Below is a copy of all of the source code.
//
//
// Copyright (c) 2013 Andy Ward. All rights reserved.
//
#import "SphereRenderer.h"
#import "shaderUtil.h"
#import "fileUtil.h"
#import "debug.h"
//#import <GLKit/GLKit.h>
// Shaders
enum {
PROGRAM_LIGHTING,
PROGRAM_PASSTHRU,
NUM_PROGRAMS
};
enum {
UNIFORM_MVP,
UNIFORM_MODELVIEW,
UNIFORM_MODELVIEWIT,
UNIFORM_LIGHTDIR,
UNIFORM_AMBIENT,
UNIFORM_DIFFUSE,
UNIFORM_SPECULAR,
UNIFORM_SHININESS,
UNIFORM_CONSTANT_COLOR,
NUM_UNIFORMS
};
enum {
ATTRIB_VERTEX,
ATTRIB_COLOR,
ATTRIB_NORMAL,
NUM_ATTRIBS
};
typedef struct {
char *vert, *frag;
GLint uniform[NUM_UNIFORMS];
GLuint id;
} programInfo_t;
programInfo_t program[NUM_PROGRAMS] = {
{ "lighting.vsh", "color.fsh" }, // PROGRAM_LIGHTING
{ "color.vsh", "color.fsh" }, // PROGRAM_PASSTHRU
};
typedef struct
{
float x;
float y;
float z;
float nx;
float ny;
float nz;
float u;
float v;
float r;
float g;
float b;
float a;
GLbyte padding[16];
} Vertex;
static float lightDir[3] = { 0.8, 4.0, 1.0 };
static float ambient[4] = { 0.35, 0.35, 0.35, 0.35 };
static float diffuse[4] = { 1.0-0.35, 1.0-0.35, 1.0-0.35, 1.0 };
static float specular[4] = { 0.8, 0.8, 0.8, 1.0 };
static float shininess = 8;
#implementation SphereRenderer
- (id)init
{
if (self = [super init])
{
angleDelta = -0.05f;
scaleFactor = 7; //max = 1025
r = 350; //scaleFactor * 48.0f;
//maxValue = 1025 * 48.0f;
xVelocity = 1.5f;
yVelocity = 0.0f;
xPos = r*2.0f;
yPos = r*3.0f;
// normalize light dir
lightDirNormalized = GLKVector3Normalize(GLKVector3MakeWithArray(lightDir));
projectionMatrix = GLKMatrix4Identity;
[self LoadTexture];
[self generateSphereData];
[self setupShaders];
}
return self;
}
- (void)makeOrthographicForWidth:(CGFloat)width height:(CGFloat)height
{
projectionMatrix = GLKMatrix4MakeOrtho(0, width, 0, height, -50000.0f, 2000.0f);
}
-(void)generateSphereData
{
#define PI 3.141592654
#define TWOPI 6.283185308
int x;
int index = 0;
float v1x, v1y, v1z;
float v2x, v2y, v2z;
float d;
int theta, phi;
float theta0, theta1;
float phi0, phi1;
Vertex quad[4];
Vertex *sphereData = malloc( 128 * 256* 6 * sizeof( Vertex ) );
float delta = M_PI / 128;
// 32 vertical segments
for(theta = 0; theta < 128; theta++)
{
theta0 = theta*delta;
theta1 = (theta+1)*delta;
// 64 horizontal segments
for(phi = 0; phi < 256; phi++)
{
phi0 = phi*delta;
phi1 = (phi+1)*delta;
// Generate 4 points per quad
quad[0].x = r * sin(theta0) * cos(phi0);
quad[0].y = r * cos(theta0);
quad[0].z = r * sin(theta0) * sin(phi0);
quad[0].u = (float)theta / (float)128;
quad[0].v = (float)phi / (float)256;
quad[1].x = r * sin(theta0) * cos(phi1);
quad[1].y = r * cos(theta0);
quad[1].z = r * sin(theta0) * sin(phi1);
quad[1].u = (float)theta / (float)128;
quad[1].v = (float)(phi + 1) / (float)256;
quad[2].x = r * sin(theta1) * cos(phi1);
quad[2].y = r * cos(theta1);
quad[2].z = r * sin(theta1) * sin(phi1);
quad[2].u = (float)(theta + 1)/ (float)128;
quad[2].v = (float)(phi + 1) / (float)256;
quad[3].x = r * sin(theta1) * cos(phi0);
quad[3].y = r * cos(theta1);
quad[3].z = r * sin(theta1) * sin(phi0);
quad[3].u = (float)(theta + 1) / (float)128;
quad[3].v = (float)phi / (float)256;
// Generate the normal
if(theta >= 4)
{
v1x = quad[1].x - quad[0].x;
v1y = quad[1].y - quad[0].y;
v1z = quad[1].z - quad[0].z;
v2x = quad[3].x - quad[0].x;
v2y = quad[3].y - quad[0].y;
v2z = quad[3].z - quad[0].z;
}
else
{
v1x = quad[0].x - quad[3].x;
v1y = quad[0].y - quad[3].y;
v1z = quad[0].z - quad[3].z;
v2x = quad[2].x - quad[3].x;
v2y = quad[2].y - quad[3].y;
v2z = quad[2].z - quad[3].z;
}
quad[0].nx = ( v1y * v2z ) - ( v2y * v1z );
quad[0].ny = ( v1z * v2x ) - ( v2z * v1x );
quad[0].nz = ( v1x * v2y ) - ( v2x * v1y );
d = 1.0f/sqrt(quad[0].nx*quad[0].nx +
quad[0].ny*quad[0].ny +
quad[0].nz*quad[0].nz);
quad[0].nx *= d;
quad[0].ny *= d;
quad[0].nz *= d;
// Generate the color - This was for testing until I have the textures loading...
if((theta ^ phi) & 1)
{
quad[0].r = 0.0f;
quad[0].g = 0.0f;
quad[0].b = 0.0f;
quad[0].a = 0.0f;
}
else
{
quad[0].r = 0.0f;
quad[0].g = 0.0f;
quad[0].b = 0.0f;
quad[0].a = 0.0f;
}
// Replicate vertex info.
for(x = 1; x < 4; x++)
{
quad[x].nx = quad[0].nx;
quad[x].ny = quad[0].ny;
quad[x].nz = quad[0].nz;
quad[x].r = quad[0].r;
quad[x].g = quad[0].g;
quad[x].b = quad[0].b;
quad[x].a = quad[0].a;
}
// Store the vertices in two triangles. We are drawing everything as triangles.
sphereData[index++] = quad[0];
sphereData[index++] = quad[1];
sphereData[index++] = quad[2];
sphereData[index++] = quad[0];
sphereData[index++] = quad[3];
sphereData[index++] = quad[2];
}
}
// Create the VAO
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
// Create a VBO buffer
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, 128 * 256 * 6 * sizeof(Vertex), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, 128 * 256 * 6 * sizeof(Vertex), sphereData);
// set the colors - left as it's great for debugging
glEnableVertexAttribArray(ATTRIB_COLOR);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof(Vertex), (GLubyte *)(uintptr_t)offsetof(Vertex,r));
// set the normals
glEnableVertexAttribArray(ATTRIB_NORMAL);
glVertexAttribPointer(ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLubyte *)(uintptr_t)offsetof(Vertex,nx));
// set the texture
glEnableVertexAttribArray(1);
glError();
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLubyte *)(uintptr_t)offsetof(Vertex,u));
glError();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
// set the positions
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLubyte *)(uintptr_t)offsetof(Vertex,x));
//We need to free as we used malloc
free(sphereData);
}
-(void)LoadTexture
{
NSURL *url = nil;
CGImageSourceRef src;
CGImageRef image;
CGContextRef context = nil;
CGColorSpaceRef colorSpace;
GLubyte *data;
GLsizei width, height;
// NSImage* image = [NSImage imageNamed:#"World-satellite-map.png"];
NSBundle *bundle = [NSBundle bundleWithIdentifier: #"Award.WeatherEye3D"];
NSString *bundleRoot = [bundle pathForImageResource:#"World-satellite-map.png"];
url = [NSURL fileURLWithPath: bundleRoot];
src = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
if (!src) {
NSLog(#"No image");
// free(data);
return;
}
image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
CFRelease(src);
width = CGImageGetWidth(image);
height = CGImageGetHeight(image);
data = (GLubyte*) calloc(width * height * 4, sizeof(GLubyte));
colorSpace = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
CGColorSpaceRelease(colorSpace);
// Core Graphics referential is upside-down compared to OpenGL referential
// Flip the Core Graphics context here
// An alternative is to use flipped OpenGL texture coordinates when drawing textures
CGContextTranslateCTM(context, 0.0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// Set the blend mode to copy before drawing since the previous contents of memory aren't used. This avoids unnecessary blending.
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
CGContextRelease(context);
CGImageRelease(image);
glGenTextures(1, &texture);
glGenBuffers(1, &pboId);
// Bind the texture
glBindTexture(GL_TEXTURE_2D, texture);
// Bind the PBO
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboId);
// Upload the texture data to the PBO
glBufferData(GL_PIXEL_UNPACK_BUFFER, width * height * 4 * sizeof(GLubyte), data, GL_STATIC_DRAW);
// Setup texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// OpenGL likes the GL_BGRA + GL_UNSIGNED_INT_8_8_8_8_REV combination
// Use offset instead of pointer to indictate that we want to use data copied from a PBO
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
// We can delete the application copy of the texture data now
free(data);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
- (void)setupShaders
{
//This code has been lifted from an example.
for (int i = 0; i < NUM_PROGRAMS; i++)
{
char *vsrc = readFile(pathForResource(program[i].vert));
char *fsrc = readFile(pathForResource(program[i].frag));
GLsizei attribCt = 0;
GLchar *attribUsed[NUM_ATTRIBS];
GLint attrib[NUM_ATTRIBS];
GLchar *attribName[NUM_ATTRIBS] = {
"inVertex", "inColor", "inNormal",
};
const GLchar *uniformName[NUM_UNIFORMS] = {
"MVP", "ModelView", "ModelViewIT", "lightDir", "ambient", "diffuse", "specular", "shininess", "constantColor",
};
// auto-assign known attribs
for (int j = 0; j < NUM_ATTRIBS; j++)
{
if (strstr(vsrc, attribName[j]))
{
attrib[attribCt] = j;
attribUsed[attribCt++] = attribName[j];
}
}
glueCreateProgram(vsrc, fsrc,
attribCt, (const GLchar **)&attribUsed[0], attrib,
NUM_UNIFORMS, &uniformName[0], program[i].uniform,
&program[i].id);
free(vsrc);
free(fsrc);
// set constant uniforms
glUseProgram(program[i].id);
if (i == PROGRAM_LIGHTING)
{
// Set up lighting stuff used by the shaders
glUniform3fv(program[i].uniform[UNIFORM_LIGHTDIR], 1, lightDirNormalized.v);
glUniform4fv(program[i].uniform[UNIFORM_AMBIENT], 1, ambient);
glUniform4fv(program[i].uniform[UNIFORM_DIFFUSE], 1, diffuse);
glUniform4fv(program[i].uniform[UNIFORM_SPECULAR], 1, specular);
glUniform1f(program[i].uniform[UNIFORM_SHININESS], shininess);
}
else if (i == PROGRAM_PASSTHRU)
{
glUniform4f(program[i].uniform[UNIFORM_CONSTANT_COLOR], 0.0f,0.0f,0.0f,0.4f);
}
}
glError();
}
- (void)update
{
yPos = 400;
xPos = 375;
}
- (void)render
{
GLKMatrix4 modelViewMatrix, MVPMatrix, modelViewMatrixIT;
GLKMatrix3 normalMatrix;
glBindVertexArray(vaoId);
// glBindTexture(GL_TEXTURE, texture);
// Draw "shadow"
/* glUseProgram(program[PROGRAM_PASSTHRU].id);
glEnable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA);
*/
/*// Make the "shadow" move around a bit. This is not a real shadow projection.
GLKVector3 pos = GLKVector3Normalize(GLKVector3Make(xPos, yPos, -100.0f));
modelViewMatrix = GLKMatrix4MakeTranslation(xPos + (pos.v[0]-lightDirNormalized.v[0])*20.0,
yPos + (pos.v[1]-lightDirNormalized.v[1])*10.0,
-800.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -16.0f, 0.0f, 0.0f, 1.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, angle, 0.0f, 1.0f, 0.0f);
modelViewMatrix = GLKMatrix4Scale(modelViewMatrix, 1.05f, 1.05f, 1.05f);
MVPMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
glUniformMatrix4fv(program[PROGRAM_PASSTHRU].uniform[UNIFORM_MVP], 1, GL_FALSE, MVPMatrix.m);
//Draw the shadow arrays
glDrawArrays(GL_TRIANGLES, 0, 32*64*6);
*/
// Draw Sphere
glUseProgram(program[PROGRAM_LIGHTING].id);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glDisable(GL_BLEND);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
// ModelView
modelViewMatrix = GLKMatrix4MakeTranslation(xPos, yPos, -200.0f); // was -100
//modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -0.01f, 0.0f, 0.0f, 0.01f);
// modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, angle, 0.0f, 1.0f, 0.0f);
glUniformMatrix4fv(program[PROGRAM_LIGHTING].uniform[UNIFORM_MODELVIEW], 1, GL_FALSE, modelViewMatrix.m);
// MVP
MVPMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
glUniformMatrix4fv(program[PROGRAM_LIGHTING].uniform[UNIFORM_MVP], 1, GL_FALSE, MVPMatrix.m);
// ModelViewIT (normal matrix)
bool success;
modelViewMatrixIT = GLKMatrix4InvertAndTranspose(modelViewMatrix, &success);
if (success) {
normalMatrix = GLKMatrix4GetMatrix3(modelViewMatrixIT);
glUniformMatrix3fv(program[PROGRAM_LIGHTING].uniform[UNIFORM_MODELVIEWIT], 1, GL_FALSE, normalMatrix.m);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 128*256*6 ); // Value needs changing for number of triangles...
glUseProgram(0);
glError();
}
- (void)dealloc
{
if (vboId) {
glDeleteBuffers(1, &vboId);
vboId = 0;
}
if (vaoId) {
glDeleteVertexArrays(1, &vaoId);
vaoId = 0;
}
if (vertexShader) {
glDeleteShader(vertexShader);
vertexShader = 0;
}
if (fragmentShader) {
glDeleteShader(fragmentShader);
fragmentShader = 0;
}
if (shaderProgram) {
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
[super dealloc];
}
#end
Lighting.vsh : -
#version 150
in vec4 inVertex, inColor;
in vec3 inNormal;
out vec4 color;
uniform mat4 MVP, ModelView;
uniform mat3 ModelViewIT;
uniform vec3 lightDir;
uniform vec4 ambient, diffuse, specular;
uniform float shininess;
void main()
{
// transform position to clip space
gl_Position = MVP * inVertex;
// transform position to eye space
vec3 eyePosition = vec3(ModelView * inVertex);
// transform normal to eye space (normalization skipped here: inNormal already normalized, matrix not scaled)
vec3 eyeNormal = ModelViewIT * inNormal;
// directional light ambient and diffuse contribution (lightDir alreay normalized)
float NdotL = max(dot(eyeNormal, lightDir), 0.0);
vec4 lightColor = ambient + diffuse * NdotL;
if (NdotL > 0.0)
{
// half angle
vec3 H = normalize(lightDir - normalize(eyePosition));
// specular contribution
float NdotH = max(dot(eyeNormal, H), 0.0);
lightColor += specular * pow(NdotH, shininess);
}
// apply directional light color and saturate result
// to match fixed function behavior
color = min(inColor * lightColor, 1.0);
}
color.vsh : -
#version 150
in vec4 inVertex;
out vec4 color;
uniform mat4 MVP;
uniform vec4 constantColor;
void main()
{
gl_Position = MVP * inVertex;
color = constantColor;
}
Color.fsh: -
#version 150
in vec4 color;
out vec4 fragColor;
void main()
{
fragColor = color;
}
For texture loading I always double check the modes I'm using.
For your shaders I would check the #version of vertex and fragment shaders and make sure it plays nicely with whatever version of OpenGL you have installed or whatever your video card supports. I used to do a lot with JOGL and whenever I used #version 330 instead of #version 400 it was because my video card wasn't one of the newest models at the time and didn't support any shader beyond 330. There is actually quite some difference between versions 150 and 400 so if you are doing anything more advanced in your GL code than what your shaders can support, your textures won't load. (i.e. there was a major change in OpenGL around there where it was no longer fixed function pipeline, it was then all programmable pipeline and therefore you had much more control at the cost of having to do more work....like write your own VBO, heh)
There are also certain functions in GLSL that are different from version to version and when you are going that far back to 150, many of the newer ones won't be recognized.
Here is a good reference for shader language and what versions of OpenGL they are compatible with http://en.wikipedia.org/wiki/OpenGL_Shading_Language. I know it's just wiki, but all the version mappings on there look correct.
Also, I always had to check the direction of my normals. If they are upside down or in the opposite direction they're supposed to be (like pointing inward instead of outward) then your lighting and textures also won't work.
Here's an example of a shader I wrote a while back before I started using the newer shader versions:
v.glsl
#version 130
in vec4 vPosition;
in vec4 vColor;
in vec3 vNormal;
in vec2 vTexCoord;
out vec4 color;
out vec3 E,L,N;
out vec2 texCoord;
uniform vec4 LightPosition;
uniform vec4 Projection, Model, View;
void main() {
vec3 pos = (Model * vPosition).xyz;
E = normalize((View * vec4(0,0,0,1)).xyz-pos);
//camera eye
L = normalize(LightPosition.xyz - pos);
N = normalize(Model * vec4(vNormal, 0.0)).xyz; //set normal vector
gl_Position = Projection * View * Model * vPosition; //view mode: Projection
texCoord = vTextCoord; //output vector of texture coordinates
color = vColor; //output vector that tells you the color of each vertex
}
f.glsl
#version 130
in vec4 color;
in vec2 texCoord;
in vec3 N,L,E;
out vec4 fColor;
uniform sampler2D texture;
uniform vec4 GlobalAmbient, AmbientProduct, DiffuseProduct, SpecularProduct;
uniform vec3 LightDirection;
uniform float Shininess, CutoffAngle, LightIntensity;
void main() {
vec3 D, H;
//process the spotlight
D = normalize(LightDirection);
H = normalize(L+E); //normalize the sum of the Light and Camera (Eye) vectors
vec4 ambient = vec4(0,0,0,0);
vec4 diffuse = vec4(0,0,0,1);
vec4 specular = vec4(0,0,0,1);
vec4 color = vec4(0,0,0,0);
//spot coefficient
float Kc = LightIntensity * max(dot(D,-L)-CutoffAngle,0.0);
//ambient coefficient
ambient = (Kc*AmbientProduct) + ambient + GlobalAmbient;
//diffuse coefficient
float Kd = max(dot(L,N), 0.0);
//diffuse component
diffuse = Kc * Kd * DiffuseProduct + diffuse;
//specular coefficient
float Ks = pow(max(dot(E,H), 0.0), Shininess);
//specular component
if(dot(L,N) >= 0.0) {
specular = Kc * Ks * SpecularProduct + specular;
}
fColor = (color + ambient + diffuse + specular) * texture2D(texture, texCoord);
fColor.a = 1.0; //fully opaque
}
I'll take a look at this some more when I get home because I love graphics. Now again, this shader code talks to Java code (using JOGL libs) so it will be done differently in Objective C, but the ideas are all the same.
Also check the order of your gl function calls - that can make a difference in many cases.
In ObjC I'd imagine you'd hand over your pixel data like this:
- (GLuint)setupTexture:(NSString *)fileName {
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image %#", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
I am trying to learn OpenGL ES 2.0 to do some iPhone game development. I have read through multiple tutorials and some of the OpenGL ES 2.0 spec. All of the examples I have seen have created a single mesh, loaded it into a vertex buffer and then rendered it (with the expected translation, rotation, gradient, etc.)
My question is this: how do you render multiple objects in your scene that have different meshes and are moving independently? If I have a car and a motorcycle for example, can I create 2 vertex buffers and keep the mesh data for both around for each render call, and then just send in different matrices for the shader for each object? Or do I need to somehow translate the meshes and then combine them into a single mesh so that they can be rendered in one pass? I'm looking for more of the high-level strategy / program structure rather than code examples. I think I just have the wrong mental modal of how this works.
Thanks!
The best way I found to do this is using VAOs in addition to VBOs.
I'll first answer you question using VBOs only.
First of all, assume you have the two meshes of your two objects stored in the following arrays:
GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;
where:
GLfloat gCubeVertexData1[36] = {...};
GLfloat gCubeVertexData2[36] = {...};
And you also have to vertix buffers:
GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;
Now, to draw those two cubes (without VAOs), you have to do something like that:
in draw function (from OpenGLES template):
//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);
This will answer you question. But now to use VAOs, your draw function code is much simpler (which is good because it is the repeated function):
First you will define to VAOs:
GLuint _vertexArray1;
GLuint _vertexArray2;
and then you will do all the steps previously done in draw method, you will do it in setupGL function but after binding to the VAO. Then in your draw function you just bind to the VAO you want.
VAO here is like a profile that contains a lot of properties (imagine a smart device profile). Instead of changing color, desktop, fonts.. etc every time you wish to change them, you do that once and save it under a profile name. Then you just switch the profile.
So you do that once, inside setupGL, then you switch between them in draw.
Of course you may say that you could have put the code (without VAO) in a function and call it. That's true, but VAOs are more efficient according to Apple:
http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html#//apple_ref/doc/uid/TP40008793-CH107-SW1
Now to the code:
In 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);
Then finally in your draw method:
glBindVertexArrayOES(_vertexArray1);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArrayOES(_vertexArray2);
glDrawArrays(GL_TRIANGLES, 0, 36);
You maintain separate vertex/index buffers for different objects, yes. For example, you might have a RenderedObject class, and each instance would have it's own vertex buffer. One RenderedObject might take it's vertices from a house mesh, one might come from a character mesh, etc.
During rendering you set the appropriate transform/rotation/shading for the vertex buffer you're working with, perhaps something like:
void RenderedObject::render()
{
...
//set textures/shaders/transformations
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
...
}
As mentioned in there other answer, the bufferID is just a GLuint not the entire contents of the buffer. If you need more details on creating vertex buffers and filling them with data, I'm happy to add those as well.
I realize this is an older post, but I was trying to find instructions on how to render multiple objects within OpenGL. I found a great tutorial, which describes how to render multiple objects and could be easily extended to render objects of different types (i.e. one cube, one pyramid).
The tutorial I'm posting also describes how to render objects using GLKit. I found it helpful and thought I'd repost it here. I hope it helps you too!
http://games.ianterrell.com/opengl-basics-with-glkit-in-ios5-encapsulated-drawing-and-animation/
If the meshes are different, you keep them in different vertex buffers. If they are similar (eg. animation, color) you pass arguments to the shader. You only have to keep the handles to the VBOs, not the vertex data itself if you don't plan on animating the object on the application side. Device side animation is possible.
I am hopefully-contributing to this older post, because I undertook to solve this problem a different way. Like the question asker, I have seen lots of "one-object" examples. I undertook to place all vertices into a single VBO, and then save the offset to that object's position (per object), rather than a buffer handle. It worked. The offset can be given as a parameter to glDrawElements as below. It seems obvious in retrospect, but I was not convinced until I saw it work. Please note that I have been working with "vertex pointer" rather than the more current "vertex attribute pointer". I am working towards the latter so I can leverage shaders.
All the objects "bind" to the same vertex buffer, prior to calling "draw elements".
gl.glVertexPointer( 3, GLES20.GL_FLOAT, 0, vertexBufferOffset );
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, indicesCount,
GLES20.GL_UNSIGNED_BYTE, indexBufferOffset
);
I did not find anywhere spelled out what was the purpose of this offset, so I took a chance. Also, this gotcha: you have to specify the offset in bytes, not vertices or floats. That is, multiply by four to get the correct position.
It is possible when using shaders, to use the same program for all objects without having to compile, link and create one for each. To do this, simply store the GLuint value to the program and then for each object "glUseProgram(programId);". As a result personal experience, i use a singleton to manage GLProgram structures.. (included below :))
#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
Note that once data-spaces (attributes/uniforms) are passed in, you DONT have to pass them in each render cycle but only when invalidated. This results a serious GPU performance gain.
Per the VBO side of things, the answer above spells out how best to deal with this. Per the orientation side of the equation, you'll need a mechanism to nest tdobjects inside each other (similar to UIView and children under iOS) and then evaluation relative rotations to parents etc.
Good luck !
I'm trying some experimental imagestuff on ipad and I'm trying to store every pixel's colordata into one array to increase performance for reading every pixel colordata,
right now I have a timer that calls my DrawRect as much as possible, in my DrawRect function I have this:
-(void)drawRect:(CGRect)rect
{
UIGraphicsBeginImageContext(self.frame.size);
[currentImage.image drawInRect:CGRectMake(0, 0, 768, 1004)];
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 0.3);
r_x = r_x + 1;
if (r_x == 768) {
r_x = 1;
r_y = r_y + 1;
}
if (r_y == 1004) {
NSLog(#"color = %#", mijnArray_kleur);
}
CGPoint point2_1 = CGPointMake(r_x, r_y);
GetColor *mycolor = [GetColor alloc];
UIColor *st = [mycolor getPixelColorAtLocation:point2_1];
[mijnArray_kleur addObject:st];
[mycolor release];
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [st CGColor]);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(r_x,r_y,1,1));
}
and getPixelColorAtLocation is a custom class that returns the UIDeviceRGBColorSpace values of a pixel
with this it takes me about 4 hours (yes, hours :p) to complete one image, is there anything faster/improvements?
Thanks!
Thys
[Copied from comment for clarity] Not that I know objective C, at all, but it seems to me like your function iterates over 768 * 1004 values and thus draws that many rectangles. Guessing about 60 frames/second, this would take 3h 40min. Am I wrong here?
I just ported a .obj loader to objective-C and so far, it works, I can get my vertices and normals and stuff.
Every normal is good, pointing in the right direction, all my faces are in CCW winding, but I have some issues with the depth test.
float rotX = 0;
float rotY = 0;
objModel* o = [[objModel alloc] initWithPath:#"/model.obj"]
glClearColor(0,0,0,0);
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0, -1, 0);
glRotatef(90,0,0,1);
glRotatef(90,0,1,0);
glRotatef(rotX,0,0,-1);
glRotatef(rotY,0,1,0);
[o drawObjWithArrays]
glFlush();
I have 2 different ways of drawing my object, one uses glBegin() / glEnd(), the other uses vertex and normal arrays with a call to glDrawArrays(). Both of them result in the same problem : faces that should be hidden by faces in front of them are displayed, because the depth test isn't working. The faces are drawn in the order they come in the .obj file.
You'll find an image here : http://img524.imageshack.us/img524/994/image2jgq.png
I quite new to OpenGL and objective-C, so I guess my problem comes from a setting that I forgot. Here they are :
-(id) initWithFrame:(NSRect) frame {
NSLog(#"INIT GL VIEW\n");
GLuint attributes[] = {
NSOpenGLPFANoRecovery,
NSOpenGLPFAWindow,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFAAccumSize, 0,
0
};
NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*) attributes];
if (!fmt)
NSLog(#"No OpenGL pixel format");
GLfloat mat_ambient[] = {0.0, 0.0, 1.0, 1.0};
GLfloat mat_flash[] = {0.0, 0.0, 1.0, 1.0};
GLfloat mat_flash_shiny[] = {50.0};
GLfloat light_position[] = {100.0,-200.0,-200.0,0.0};
GLfloat ambi[] = {0.1, 0.1, 0.1, 0.1};
GLfloat lightZeroColor[] = {0.9, 0.9, 0.9, 0.1};
/* set the material */
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambi);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_flash_shiny);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
return self = [super initWithFrame:frame pixelFormat:[fmt autorelease]];
}
Anyone can help me ? I've tried everyone glDepthFunc, glCullFace, glFrontFace combination possible, nothing works...
Thanks ^^
what do you get when you add this to your draw method?
int depth;
glGetIntegerv(GL_DEPTH_BITS, &depth);
NSLog(#"%i bits depth", depth)
A few more things to try:
make sure initWithFrame is being called
if you're using an NSOpenGLView subclass from IB, set the depth buffer in IB, and move all your openGL initialization into - (void)prepareOpenGL
In case someone wonders how to set this without IB:
NSOpenGLPixelFormatAttribute attrs[] = {
// NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 32,
0
};
NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
NSOpenGLView *view = [[NSOpenGLView alloc] initWithFrame:frame pixelFormat:format];
Make sure you call glDepthFunc() to set the depth buffer comparison function. Most applications use GL_LEQUAL or GL_LESS for the depth function. Also make sure you call glClearDepth() to set the value that the depth buffer gets cleared to; you should probably use a parameter of 1.0 for this to clear to maximum depth.
I'm not sure how the appropriate AGL calls should look like but you should make sure you actually allocate bit planes for the depth buffer.