Related
I have been working on getting screenshot from OpenGL and then also rendering the same on screen. For this purpose i have created two framebuffers. 1 For rendering(ratinaFramebuffer) and 1 for using the apple texture cache drawing(textureFramebuffer). Created both using below code(except variable names).
glGenFramebuffers(1, &ratinaFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, ratinaFramebuffer);
glGenRenderbuffers(1, &ratinaColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, ratinaColorRenderbuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glGenRenderbuffers(1, &ratinaDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, ratinaDepthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ratinaDepthRenderbuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(#"failed to make complete framebuffer object %x", status);
}
Here is what my render code looks like
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Clear content
glBindFramebuffer(GL_FRAMEBUFFER, ratinaFramebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, textureFramebuffer);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Start drawing
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
static int nColor = 104;
glClearColor(0, (++nColor & 0xff)/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:10];
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
[modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)];
_currentRotation += displayLink.duration * 90;
[modelView rotateBy:CC3VectorMake(_currentRotation, _currentRotation, 0)];
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
// 1
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
// 2
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _floorTexture);
glUniform1i(_textureUniform, 0);
// 3
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2);
glActiveTexture(GL_TEXTURE0); // unneccc in practice
glBindTexture(GL_TEXTURE_2D, _fishTexture);
glUniform1i(_textureUniform, 0); // unnecc in practice
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices2)/sizeof(Indices2[0]), GL_UNSIGNED_BYTE, 0);
// Get the image
CGImageRef cgImage = NULL;
UIImage *screenImageimage = nil;
OSStatus res = CreateCGImageFromCVPixelBuffer(renderTarget,&cgImage);
if (res == noErr)
{
screenImageimage = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];
}
At this point, image is perfectly drawn into the texture. But when i try to render it on screen, the same texture, it is not getting drawn :(. It shows the blue color that i have given in clear color but nothing else.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ratinaFramebuffer);
glBindTexture(GL_TEXTURE_2D, 0);
glClearColor(0, 255/255.0, 255/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, renderTexture);
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, ratinaColorRenderbuffer);
[_context presentRenderbuffer:GL_RENDERBUFFER_OES];
When you draw to the separate frame buffer you may attach a texture to it where in the end you may affectively draw to the texture itself. Once this drawing is complete you may then use the texture as you would any other which includes drawing it to your main onscreen buffer.
There are a few points you should check when doing so:
Do not forget to rebind the frame buffer when switching
You must set the viewport according to the bound frame buffer when switching
Possibly you may need to clear the buffer
It is possible the texture is drawn only on a part of the buffer for instance drawing a 600x600 screen on an 1024x1024 texture. In this case you need to generate the vertex coordinates in range [0, 600/1024] to draw the correct part of it. This can be checked by comparing the viewport values from the texture bound frame buffer and the texture size.
It is hard to say what you are missing now but the best shot at the moment would be to check the viewport. Beside that you do not need to complicate with the main buffer if you are simply drawing a fullscreen texture. You may disable the matrix and simply use the [-1,1] coordinate system which is the default.
Problem is resolved, as i was new to openGL and i was not drawing the screen framebuffer, which i did by using below code in the render on screen call. Just posting if anyone need to look at that at beginer level
GLfloat coordinates[] = { 0, 1,
1, 1,
0, 0,
1, 0 };
GLfloat width = self.frame.size.width,
height = self.frame.size.height;
GLfloat vertices[] = { -width / 2 + point.x, -height / 2 + point.y, 0.0,
width / 2 + point.x, -height / 2 + point.y, 0.0,
-width / 2 + point.x, height / 2 + point.y, 0.0,
width / 2 + point.x, height / 2 + point.y, 0.0 };
glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture));
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);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Although, this shows the texture but in a small box that is drawn in on the other frame buffer. It does not draw to complete screen although the vertexs and co-ords are of complete screen. Looking into that, will edit this if i found.
This is driving me nuts! I've not done much with OpenGL before, so any pointers appreciated.
Can anyone tell me any reason why some of my objects appear in my shadow map, and others don't?
I have 2 object I am rendering; southernHemisphere and Frame. Both of these are rendered to the scene and to the shadow map texture. The scene is working perfectly showing all objects, but only southernHemisphere is appearing in the shadow map.
My shadow map texture is bound like this:
- (void)setupShadowmapBuffers {
glGenTextures(1, &_shadowTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _shadowTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenFramebuffers(1, &_shadowFBO);
glGenRenderbuffers(1, &_shadowRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _shadowRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 1024, 1024);
glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO);
glBindTexture(GL_TEXTURE_2D, _shadowTexture);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
_shadowRenderBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _shadowTexture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
printf("Error: FrameBufferObject is not complete!\n");
}
}
In my render function, I render to the shadow map texture:
// Render the shadow map
glUseProgram(_shadowProgramHandle);
glUniformMatrix4fv(_shadowProjectionUniform, 1, 0, LSprojection.glMatrix);
glUniformMatrix4fv(_shadowLightMPVUniform, 1, 0, lightsourcemat.glMatrix);
glBindFramebuffer(GL_FRAMEBUFFER, _shadowFBO);
glBindRenderbuffer(GL_RENDERBUFFER, _shadowRenderBuffer);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, 1024, 1024);
glUniformMatrix4fv(_shadowModelViewUniform, 1, 0, southernHemispheremat.glMatrix);
[self drawSouthernHemisphere];
glUniformMatrix4fv(_shadowModelViewUniform, 1, 0, framemat.glMatrix);
[self drawFrame];
Then I render to the scene:
// Reset buffers and finally render the scene
glUseProgram(_mainProgramHandle);
glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderBuffer);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, vFrame.size.width, vFrame.size.height);
glUniformMatrix4fv(_modelViewUniform, 1, 0, shadowmat.glMatrix);
[self drawShadow];
glUniformMatrix4fv(_modelViewUniform, 1, 0, southernHemispheremat.glMatrix);
[self drawSouthernHemisphere];
glUniformMatrix4fv(_modelViewUniform, 1, 0, framemat.glMatrix);
[self drawFrame];
My drawing functions look like this:
- (void)drawSouthernHemisphere {
// Southern Hemisphere
glEnable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, _globeTexture);
glBindVertexArrayOES(_southernHemisphere);
// Draw outside (textured)
glUniform1f(_useTextureSlot, 1);
glCullFace(GL_BACK);
glDrawElements(GL_TRIANGLES, sizeof(Southern)/sizeof(Southern[0]), GL_UNSIGNED_SHORT, 0);
// Draw inside (coloured)
glCullFace(GL_FRONT);
glUniform1f(_useTextureSlot, 0);
glUniform4f(_runTimeColorSlot, 0, 0, 0, 0);
glDrawElements(GL_TRIANGLES, sizeof(Southern)/sizeof(Southern[0]), GL_UNSIGNED_SHORT, 0);
glBindVertexArrayOES(0);
}
- (void)drawFrame {
// Frame
glEnable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, _frameTexture);
glBindVertexArrayOES(_frame);
// Draw outside (textured)
glUniform1f(_useTextureSlot, 1);
glCullFace(GL_BACK);
glDrawElements(GL_LINES, sizeof(Frame)/sizeof(Frame[0]), GL_UNSIGNED_SHORT, 0);
glBindVertexArrayOES(0);
}
Thanks.
EDIT:
I have isolated the issue being down to my vertex arrays - in particular the normals.
I swapped the values in the frame array with the ones in my southernHemisphere array, and bingo - the object appears in both the scene, and the shadow map!
After some adjustment of values, it appears that it will only work if the direction of the normals are the same as the direction of each vector itself.
What baffles me is that in my shadow map shader, I don't even access the normals so why do they have any effect on the rendering at all??
Here is my vertex shader for my shadow map:
attribute vec4 Position;
uniform mat4 Projection;
uniform mat4 Modelview;
uniform mat4 Light;
varying vec4 TexCoord;
void main()
{
TexCoord = Projection * Light * Modelview * Position;
gl_Position = Projection * Light * Modelview * Position;
}
and fragment:
precision mediump float;
varying vec4 TexCoord;
void main()
{
/* Generate shadow map - write fragment depth. */
float value = 10.0 - TexCoord.z;
float v = floor(value);
float f = value - v;
float vn = v * 0.1;
gl_FragColor = vec4(vn, f, 0.0, 1.0);
}
EDIT2:
Here are my set up function for VAOs
// Southern Hemisphere
glGenVertexArraysOES(1, &_southernHemisphere);
glBindVertexArrayOES(_southernHemisphere);
GLuint vb2;
glGenBuffers(1, &vb2);
glBindBuffer(GL_ARRAY_BUFFER, vb2);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(_positionSlot);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Position));
glEnableVertexAttribArray(_colorSlot);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Color));
glEnableVertexAttribArray(_normalSlot);
glVertexAttribPointer(_normalSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
glEnableVertexAttribArray(_texCoordSlot);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoord));
GLuint southernBuffer;
glGenBuffers(1, &southernBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,southernBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Southern) * sizeof(Southern[0]), Southern, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArrayOES(0);
// Frame
glGenVertexArraysOES(1, &_frame);
glBindVertexArrayOES(_frame);
GLuint vb4;
glGenBuffers(1, &vb4);
glBindBuffer(GL_ARRAY_BUFFER, vb4);
glBufferData(GL_ARRAY_BUFFER, sizeof(Frame_Vertices), Frame_Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(_positionSlot);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Position));
glEnableVertexAttribArray(_colorSlot);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Color));
glEnableVertexAttribArray(_normalSlot);
glVertexAttribPointer(_normalSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
glEnableVertexAttribArray(_texCoordSlot);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoord));
GLuint legsBuffer;
glGenBuffers(1, &frameBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,frameBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Frame) * sizeof(Frame[0]), Frame, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArrayOES(0);
My Arrays all follow the same form of:
const Vertex Vertices[] = {
{{0.0000,-1.5000,0.0000},{0.3843137254902,0.14117647058824,0.082352941176471,1},{0.0000,-1.0000,0.0000},{0.0000,1.0000}},
{{0.0000,-1.4872,0.1958},{0.3843137254902,0.14117647058824,0.082352941176471,1},{0.0000,-0.9914,0.1305},{0.0000,0.9583}},
....
}
which consists of Position, Colour, Normal, Texture Coordinates as you can see from the set up code.
After my shaders are linked, I finish set up with these:
- (void)useMainProgram:(GLfloat[])lightsource {
const GLfloat l_ambient = 0.7;
glUseProgram(_mainProgramHandle);
_positionSlot = glGetAttribLocation(_mainProgramHandle, "Position");
_colorSlot = glGetAttribLocation(_mainProgramHandle, "Color");
_normalSlot = glGetAttribLocation(_mainProgramHandle, "Normal");
_lightingSlot = glGetUniformLocation(_mainProgramHandle, "l1");
_ambientLightingSlot = glGetUniformLocation(_mainProgramHandle, "l_ambient");
_texCoordSlot = glGetAttribLocation(_mainProgramHandle, "TexCoordIn");
_useTextureSlot = glGetUniformLocation(_mainProgramHandle, "use_texture");
_runTimeColorSlot = glGetUniformLocation(_mainProgramHandle, "RunTimeColor");
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_colorSlot);
glEnableVertexAttribArray(_normalSlot);
glEnableVertexAttribArray(_texCoordSlot);
_projectionUniform = glGetUniformLocation(_mainProgramHandle, "Projection");
_modelViewUniform = glGetUniformLocation(_mainProgramHandle, "Modelview");
for (int i=0;i<_num_bottles; i++){
_bottlesUniform[i] = glGetUniformLocation(_mainProgramHandle, "Modelview");
}
_textureUniform = glGetUniformLocation(_mainProgramHandle, "Texture");
// Lighting doesn't change, so we set it here.
glUniform3fv(_lightingSlot, 1, lightsource);
glUniform1fv(_ambientLightingSlot, 1, &l_ambient);
}
- (void)useShadowProgram:(GLfloat[])lightsource {
glUseProgram(_shadowProgramHandle);
_shadowPositionSlot = glGetAttribLocation(_shadowProgramHandle, "Position");
_shadowLightMPVUniform = glGetUniformLocation(_shadowProgramHandle, "Light");
_shadowProjectionUniform = glGetUniformLocation(_shadowProgramHandle, "Projection");
_shadowModelViewUniform = glGetUniformLocation(_shadowProgramHandle, "Modelview");
glEnableVertexAttribArray(_shadowPositionSlot);
}
I'm trying to reproduce in a GLKViewController the effect in this stencil buffer tutorial here in which a stencil is used for clipping.
In my drawinrect method I have:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
[self.effect prepareToDraw];
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, Position));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, TexCoord));
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); // draw 1s on test fail (always)
// draw stencil pattern
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT); // needs mask=0xFF
glDrawElements(GL_TRIANGLES, 1*6, GL_UNSIGNED_SHORT, 0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glStencilMask(0x00);
// draw only where stencil's value is 1
glStencilFunc(GL_EQUAL, 1, 0xFF);
glDrawElements(GL_TRIANGLES, nLiveSquares*6, GL_UNSIGNED_SHORT, 0);
glDisable(GL_STENCIL_TEST);
}
The vertexbuffer contains info for squares, which hold textures.
The effect I'm trying to get is to have the first square be used to define the stencil shape and then when I draw the scene, anything which lies outside of the first square is clipped. Hence, I have used drawElements twice - firstly to draw the clipping square and then to draw all the squares.
I also have another method used for the OpenGL set-up, called once at the start of the program. As suggested by an answer to this stackoverflow question, this includes:
GLuint depthStencilRenderbuffer;
glGenRenderbuffers(1, &depthStencilRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthStencilRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilRenderbuffer);
Running this results in nothing except a screen containing the grey clear color. It appears that everything has been stenciled out (or is not drawn to the screen).
Solution:
i) I changed the initialization to:
GLuint depthStencilRenderbuffer;
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthStencilRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
GL_DEPTH24_STENCIL8_OES,
self.view.bounds.size.width,
self.view.bounds.size.height);
I'm not sure what the difference is, but this seems to work.
ii) In the set up I use:
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
Thanks to #rickster for his suggestion.
In my app I have a view that renders STL files. STL files are essentially a list of triangle vertices with normals, they have no indices.
Every tutorial that I have found explains how to use multiple VBOs but with Indices and when I try using drawArrays rather than drawElements it doesn't work. I'm new to OpenGL and would really appreciate it if someone could provide code examples for using multiple VBOs for setup and drawing them without indices.
Here is how I am trying to do this now to no avail. SetupGL:
[EAGLContext setCurrentContext:self.context];
[self loadShaders];
self.effect = [[GLKBaseEffect alloc] init];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(.05f, .55f, 1.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glClearColor(0.88f, 0.88f, 0.88f, 1.0f);
//---- First Vertex Array Object --------
glGenVertexArraysOES(1, &_vertexArray);
glBindVertexArrayOES(_vertexArray);
glBindVertexArrayOES(_vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, vertCount*sizeof(verticesBuff) * 3 * 2, verticesBuff, 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));
//----- Second Vertex Array Object ----------
glGenBuffers(1, &_gridVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _gridVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, gridVertCount*sizeof(gridVerticesBuff) * 3 * 2, NULL, 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));
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArrayOES(0);
And this is my draw code:
glBindVertexArrayOES(_vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glUseProgram(_program);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glDrawArrays(GL_TRIANGLES, 0, vertCount);
///////// second VBO and shader program:
glBindBuffer(GL_ARRAY_BUFFER, _gridVertexBuffer);
glUseProgram(_program);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
glDrawArrays(GL_TRIANGLES, 0, 108);
Also, here is code that I was using before that doesn't render both the arrays but it does render one (verticesBuff). SetupGL:
[EAGLContext setCurrentContext:self.context];
[self loadShaders];
self.effect = [[GLKBaseEffect alloc] init];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(.05f, .55f, 1.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glClearColor(0.88f, 0.88f, 0.88f, 1.0f);
glGenVertexArraysOES(1, &_vertexArray);
glBindVertexArrayOES(_vertexArray);
glGenBuffers(1, &_gridVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _gridVertexBuffer);
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));
glBufferData(GL_ARRAY_BUFFER, 108 * sizeof(gridVerticesBuff), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(gridVerticesBuff) * 108, gridVerticesBuff);
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
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));
glBufferData(GL_ARRAY_BUFFER, vertCount*sizeof(verticesBuff) * 3 * 2, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verticesBuff) * vertCount * 3, verticesBuff);
glBindVertexArrayOES(0);
And here is the draw code:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (loaded) {
// Render the object with GLKit
[self.effect prepareToDraw];
glBindVertexArrayOES(_gridVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, _gridVertexBuffer);
glDrawArrays(GL_TRIANGLES, 0, 108);
glBindVertexArrayOES(_vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glDrawArrays(GL_TRIANGLES, 0, vertCount);
}
It is tough to know exactly what is wrong without seeing your code, but if you follow these general steps you should be able to figure it out. There is little difference between indexed and flat data. Using VBOs will be easier if you understand what they are doing. Essentially VBOs allow you to preupload common vertex data onto the graphics card so it doesn't have to be reploaded every draw call. With that in mind you need to think about how to pack all the vertex data used in a normal draw call into a single memory buffer.
Create Buffer:
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, totalBufferSize, NULL, GL_STATIC_DRAW); /* totalBufferSize is sizeof vertices buffer + sizeof normals buffer */
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexBufferSize, vertexBuffer); /* offset 0 */
glBufferSubData(GL_ARRAY_BUFFER, vertexBufferSize, normalBufferSize, normalBuffer); /* offset vertexbufferSize */
Draw Buffer:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(VERTEX_ATTRIB ..., ((char*)NULL) + 0); /* start at beginning (0 offset) of the vertex buffer */
glVertexAttribPointer(NORMAL_ATTRIB ..., ((char*)NULL) + vertexBufferSize); /* start at normal subdata (offset vertexBufferSize) of the vertex buffer */
glDrawArrays(..., vertexCount);
The (char*)NULL thing may look a little weird. Instead of passing in a pointer to a buffer in RAM, you are passing in an offset into the currently bound VBO. The arguments of the function still require a pointer so the address of the pointer is used as an offset. Understand NULL is 0 and treat the code as if (char*)NULL + is not there.
I'm trying to take my code to the next level. Following some best practices from Apple, I'm trying to implement Vertex Array Objects around my Vertex Buffer Objects (VBO). I setup my VBOs and VAOs like this:
- (void)setupVBOs {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArrayOES(0);
{
glGenVertexArraysOES(1, &directArrayObject);
glBindVertexArrayOES(directArrayObject);
// GLuint texCoordBuffer;
glGenBuffers(1, &texCoordBuffer);
glBindBuffer(GL_ARRAY_BUFFER, texCoordBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(DirectVertices), DirectVertices, GL_STATIC_DRAW);
glVertexAttribPointer(directPositionSlot, 2, GL_FLOAT, GL_FALSE, sizeof(DirectVertex), (GLvoid*)offsetof(DirectVertex, position));
glEnableVertexAttribArray(directPositionSlot);
glVertexAttribPointer(texCoordSlot, 2, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(DirectVertex), (GLvoid*)offsetof(DirectVertex, texCoord));
glEnableVertexAttribArray(texCoordSlot);
glGenVertexArraysOES(1, &arrayObject);
glBindVertexArrayOES(arrayObject);
// GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glEnableVertexAttribArray(positionSlot);
glVertexAttribPointer(colorSlot, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Color));
glEnableVertexAttribArray(colorSlot);
// GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArrayOES(0);
}
which I took from http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=287977 and then use it like this:
- (void) render:(CADisplayLink*)displayLink {
glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, backingWidth, backingHeight);
[directProgram use];
glBindVertexArrayOES(directArrayObject);
glDisable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, videoFrameTexture);
// // Update uniform values
glUniform1i(videoFrameUniform, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
[program use];
glBindVertexArrayOES(arrayObject);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:10];
glUniformMatrix4fv(projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
[modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)];
currentRotation += displayLink.duration * 90;
[modelView rotateBy:CC3VectorMake(currentRotation, currentRotation, 0)];
glUniformMatrix4fv(modelViewUniform, 1, 0, modelView.glMatrix);
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
glBindVertexArrayOES(0);
BOOL success = [context presentRenderbuffer:GL_RENDERBUFFER];
if(!success)
NSLog(#"present failed");
}
The call to glDrawArrays works, and it fills my texture, however, the call to glDrawElements fails with an EXC_BAD_ACCESS. My shader programs (i use two) are wrapped in a GLProgram object that I took from http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
Remove glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); from the end of your setup function.
Per the specification for OES_vertex_array_object, a vertex array object encapsulates all state except the array buffer binding1, so there is no element buffer bound at the time that you’re drawing, whereas you presumably wanted indexBuffer to be bound. By leaving indexBuffer bound at the time that you bind away from your vertex array object, you ensure that it’ll be rebound when you return to that vertex array object.
1 If you’re wondering why the array buffer binding isn’t tracked in vertex array objects, this is presumably because the currently-bound array buffer isn’t used directly when reading vertex data from arrays—rather, each vertex attribute has its own buffer binding, which is filled out by its respective gl*Pointer function by looking at the array buffer binding when the function is called.