In my project I want to create 5 textured layers.
Each layer is made out of 4 textured rectangles each. These four parts of a layer are arranged so that it looks like one big texture.
The layers are partly transparent and are arranged in front of each other to create a three dimensional look.
When I run the project with only one layer enabled, everything looks fine, but as soon as I add a second (or more) layers everything gets chaotic.
Some parts go missing, other parts have a completely wrong z-Coordinate (the value itself looks fine, but the background layer is suddenly the foremost layer). Some parts even shift their x-Coordinate (this one looks good as well on setup, if I use NSLog to output all the square coordinates).
This is my setupVBOs function where I write the object coordinates into the VBOs (I have only one Index VBO because every square is the same, but an array of 5x4 VBOs to hold the coordinates for every single part of the layers.)
- (void)setupVBOs {
glGenBuffers(1, &_indexBufferLayer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferLayer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndicesLayer), IndicesLayer, GL_STATIC_DRAW);
for (int layerNo = 1; layerNo < LAYER_COUNT + 1; ++layerNo)
{
for (int layerPart = 1; layerPart < LAYER_PARTS + 1; ++layerPart)
{
glGenBuffers(1, &_vertexBufferLayer[layerNo][layerPart]);
GLfloat x = -3.0 + (2.0 * (layerPart - 1));
GLfloat z = 0.0 + (50.0 * (layerNo - 1));
NSLog(#"Layer %d, Part %d: x=%f, z=%f", layerNo, layerPart, x, z);
// Alter the Texture slightly to
// remove errors from compression (x-Coord.)
Vertex Vertices[] = {
{{x + 1.0, -1.0, z}, {0.9865, 1.0}},
{{x + 1.0, +1.0, z}, {0.9865, 0}},
{{x - 1.0, +1.0, z}, {0.01, 0}},
{{x - 1.0, -1.0, z}, {0.01, 1.0}}
};
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferLayer[layerNo][layerPart]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
x = z = 0;
}
}
}
This is my render Function where I draw everything and add the textures.
- (void)render: (CADisplayLink*)displayLink {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-1 andRight:1 andBottom:-h/4 andTop:h/4 andNear:2 andFar:500];
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
// Translate the Modelviewmatrix
[modelView populateFromTranslation:CC3VectorMake(_cameraX, _cameraY, -5.0)];
// Rotate the Modelviewmatrix
[modelView rotateBy:CC3VectorMake(_currentRotation, 0, 90)];
[modelView translateByZ:_cameraZoom];
//
// Draw all layers
//
for (int layerNo = 1; layerNo < LAYER_COUNT + 1; layerNo++)
{
GLfloat layerFactor = (LAYER_COUNT + 1 - layerNo) * 22.0;
GLfloat scaleFactor = 100.0 + layerFactor;
[modelView scaleByX:scaleFactor];
[modelView scaleByY:scaleFactor];
for (int layerPart = 1; layerPart < LAYER_PARTS + 1; layerPart++)
{
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
GLuint uniformTexture = glGetUniformLocation(programHandle, "Texture");
// Bind Buffer and Texture
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferLayer[layerNo][layerPart]);
// Activate Texturing Pipeline and Bind Texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _layers[layerNo][layerPart][0]);
glUniform1i(uniformTexture, 0);
// Vertex Shader calls
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 0);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_texCoordSlot);
glDrawElements(GL_TRIANGLES, sizeof(IndicesLayer)/sizeof(IndicesLayer[0]), GL_UNSIGNED_BYTE, 0);
glDisableVertexAttribArray(_texCoordSlot);
glDisableVertexAttribArray(_positionSlot);
}
[modelView scaleByX:1/scaleFactor];
[modelView scaleByY:1/scaleFactor];
}
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
Use:
glenable(GL_TEXTURE_2D)
And also:
glActiveTexture — select active texture unit
Related
I have drawn a 10x10 grid of noise textures. I would like to be able to control the depth of each texture. As a test I tried translating half of the textured objects with the z coordinate at -.5 but it seems to be having no effect. What am I missing?
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, RectWidth(winRect), RectHeight(winRect));
glMatrixMode(GL.PROJECTION);
glLoadIdentity;
glMatrixMode(GL.MODELVIEW);
glLoadIdentity;
glEnable(GL.TEXTURE_2D);
wall_texid = glGenTextures(1);
halfTextureSize = 128; % in px for meshgrid
%plaidData = makePlaidData(halfTextureSize);
%normalizedPlaidData = (plaidData - min(plaidData)) / ( max(plaidData) - min(plaidData) );
[x,y] = meshgrid(-halfTextureSize+1:halfTextureSize,-halfTextureSize+1:halfTextureSize);
noysSlope = 1.0; %1.5;
noys = 255.*oneoverf(noysSlope, size(x,1), size(x,2)); % oneoverf -> [0:1]
noys=repmat(noys,[ 1 1 3 ]);
noys=permute(uint8(noys),[ 3 2 1 ]);
xoffset = -10:1:10;
yoffset = -10:1:10;
rmin_bg = 45.6874;% pixels
rmax_bg = 350.7631;% pixels
rstrip = 11.6268;% this cuts a strip into the fixation disk that has a height the size of the paddle height
% this code pokes out the transparent aperture
opaque = ones(size(x'));
% for i = 1:length(xoffset)
% opaque = min(opaque, ((sqrt((x'+xoffset(i)).^2+(y'+yoffset(i)).^2) > rmax_bg) |( sqrt((x'+xoffset(i)).^2+(y'+yoffset(i)).^2) < rmin_bg)));
% end
% noys(4,:,:) = shiftdim(255 .* opaque, -1);
glBindTexture(GL_TEXTURE_2D, wall_texid);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, halfTextureSize*2, halfTextureSize*2, 0, GL_RGB, GL_UNSIGNED_BYTE, noys);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
%glClear(GL.COLOR_BUFFER_BIT);
corners = [0 0;
1 0;
1 1;
0 1
];
halfHeight = winRect(4)/2;
halfWidth = winRect(3)/2;
canvas = .09; % pick the larger of the two dimensions, since they are not equal
depth = 0.0;
v=[-canvas -canvas depth;...
canvas -canvas depth;...
canvas canvas depth;...
-canvas canvas depth]';
surroundTexture = glGenLists(1);
glNewList(surroundTexture, GL.COMPILE);
glBegin(GL.POLYGON);
for i = 1:4
glTexCoord2dv(corners(i,:));
glVertex3dv(v(:,i));
end
glEnd;
glEndList;
translations = struct('x',{},'y',{},'z',{});
index = 1;
offset = 0.1;
for y = -10:2:9
for x = -10:2:9
translation.x = x / 10.0 + offset;
translation.y = y / 10.0 + offset;
translations(index) = translation;
index = index+1;
end
end
%%%Draw texture grid%%%%
for i = 1:100
glPushMatrix;
if i < 51
glTranslated(translations(i).x, translations(i).y, -.5);
else
glTranslated(translations(i).x, translations(i).y, 0.0);
end
glCallList(surroundTexture);
glPopMatrix;
end
NB: I am using PsychToolBox which gives access to opengl in Matlab.
While using Cairo 1.14.6 for display purposes, I found that overwriting the very same path with another color does not necessarily overwrite all pixels, and leaves undesirable artifacts behind.
As evidence of my claim I offer this output from a short self-contained example, the source for which follows further below.
An explanation of the six parts of the image, from left to right:
Original shape stroked in blue.
Original shape overwritten in RGBA white.
Original shape overwritten in RGB white.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode and CAIRO_ANTIALIAS_NONE.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode and CAIRO_ANTIALIAS_BEST.
The image was generated from the following code:
#include "cairo/cairo.h"
#define M_PI 3.14159265358979323846
void draw_shape(cairo_t* cr, int x, int y) {
cairo_arc(cr, 50 + x, 50 + y, 48, -M_PI, -M_PI / 2);
cairo_stroke(cr);
cairo_move_to(cr, x + 2, y + 2);
cairo_line_to(cr, x + 48, y + 48);
cairo_stroke(cr);
}
int main(int argc, char** argv) {
int x = 0;
int y = 0;
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 50);
cairo_t* cr = cairo_create(surface);
/* Draw a white background and a few shapes to overwrite */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_paint(cr);
cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
x = 50;
/* Leftmost shape is left unchanged for reference */
/* Stroke in RGBA opaque white */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
/* Stroke in RGB white */
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
draw_shape(cr, x + 0, y); x += 50;
/* Stroke in opaque white without blending */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with no antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with best antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
draw_shape(cr, x, y); x += 50;
/* Write the results to a file */
cairo_surface_write_to_png(surface, "output.png");
return 0;
}
It doesn't make intuitive sense to me that overwriting the very same shape would not overwrite all of its pixels, especially if I force it into non-blending CAIRO_OPERATOR_SOURCE mode. The results are the same on the framebuffer that constitutes my actual surface, so this is not an issue with the backend.
Cairo is usually so good at what it does that I'm very surprised at this. Is there no way to overwrite an anti-aliased shape exactly in Cairo?
What I was trying to do is apparently not possible. I posted my question to the Cairo mailing list and was offered two options:
Keep a copy of the original pixels before drawing over them: "Anti-aliasing involves blending. If you don’t want anti-aliasing, turn it off." (Link)
Draw at a much higher resolution: "The only real solution is to draw a much higher resolution with coverage rounded to exactly zero or one for each pixel." (Link)
More specifically:
All that is stored in the pixel from the first drawing is what
percentage of the pixel was covered by the shape. It does not remember
exactly what parts of the pixel are covered.
Since antialiasing necessarily involves blending, and since Cairo does not remember what part of a subpixel led to the blending, it has no way of knowing how to undo that blending.
I have a sphere (earth) in OpenGL ES 2.0 for iOS. I also have markers that I want to place at lat/lons on the earth - but I want the markers to always face the user ( billboards ) but also move with the Earth when it rotates by touch. So I've tried to research billboarding and put together the following code. This code is from the billboarding function that is called after I create the earth ( which is translated backwards 6 units on the Z axis). I can't seem to get the planes of the billboards to always face the camera but also move with the Earth as it rotates. How can I do that?
// Get the current modelview matrix
GLKMatrix4 originalMat = self.effect.transform.modelviewMatrix;
GLKMatrix4 currMat = self.effect.transform.modelviewMatrix;
// Define the buffer designators
GLuint billboardVertexArray;
GLuint billboardVertexBuffer;
GLuint billboardIndexBuffer;
glGenVertexArraysOES(1, &billboardVertexArray);
glBindVertexArrayOES(billboardVertexArray);
// Now draw the billboard
glGenBuffers(1, &billboardVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, billboardVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Billboard_vertex), Billboard_vertex, GL_STATIC_DRAW);
glGenBuffers(1, &billboardIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, billboardIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Billboard_index), Billboard_index, GL_STATIC_DRAW);
// u0,v0,normalx0,normaly0,normalz0,x0,y0,z0
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(5));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(2));
// Enable the Earth texture
self.effect.texture2d0.name = _billBoardTextureInfo.name;
self.effect.texture2d0.target = _billBoardTextureInfo.target;
// Bind the earth vertex array
glBindVertexArrayOES(billboardVertexArray);
// Now put a billboard at a specific Lat Lon - so first
// calculate XYZ from lat lon
XYZ xyz;
xyz.x = 0; xyz.y = 0; xyz.z = 0;
[self LLAtoXYZwithLat:0 andLon:0 andXYZ:&xyz];
//NSLog(#"XYZ after: %f %f %f",xyz.x,xyz.y,xyz.z);
// Move the billboard back so we can see it
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -6.0f);
// In update, we convert the quaternion into a rotation matrix, and apply it to the model view matrix as usual.
GLKMatrix4 rotation = GLKMatrix4MakeWithQuaternion(_quat);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, rotation);
// First create the variation translations to anchor the billboard
GLKMatrix4 translationXYZ = GLKMatrix4MakeTranslation(xyz.x, xyz.y, xyz.z);
GLKMatrix4 translationForLatLonWithTranslation = GLKMatrix4Multiply(modelViewMatrix,translationXYZ);
// Scale this object as well
GLKMatrix4 scaledWithTranslationAndRotation = GLKMatrix4Scale(translationForLatLonWithTranslation, scale, scale, scale);
// Remove the Translation portion of the matrix
// | xx xy xz xw |
// | yx yy yz yw |
// | zx zy zz zw |
// | wx wy wz ww |
//
// | R T |
// | (0,0,0) 1 |
//
// d = sqrt( xx² + yx² + zx² )
//
// | d 0 0 T.x |
// | 0 d 0 T.y |
// | 0 0 d T.z |
// | 0 0 0 1 |
//
// union _GLKMatrix4
// {
// struct
// {
// float m00, m01, m02, m03;
// float m10, m11, m12, m13;
// float m20, m21, m22, m23;
// float m30, m31, m32, m33;
// };
// float m[16];
// }
// typedef union _GLKMatrix4 GLKMatrix4;
// Construct the rows in the new matrix
float d = sqrt( pow(currMat.m00,2) + pow(currMat.m10,2) + pow(currMat.m20,2) );
GLKVector4 columnToInsert0 = GLKVector4Make(d, 0, 0, currMat.m03+xyz.x);
GLKVector4 columnToInsert1 = GLKVector4Make(0, d, 0, currMat.m13+xyz.y);
//GLKVector4 columnToInsert2 = GLKVector4Make(0, 0, d, currMat.m23-6+xyz.z);
GLKVector4 columnToInsert3 = GLKVector4Make(0, 0, 0, 1);
// Build the new Matrix
GLKMatrix4 noTranslationInfo = GLKMatrix4SetRow(currMat, 0, columnToInsert0);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 1, columnToInsert1);
//noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 2, columnToInsert2);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 3, columnToInsert3);
[self printMatrix:noTranslationInfo];
// Assign the new matrix to draw with - no translation
self.effect.transform.modelviewMatrix = noTranslationInfo;
// Render the object with GLKit
[self.effect prepareToDraw];
// Draw elements from the index array and uv / vertex / normal info
glDrawElements(GL_TRIANGLES,Billboard_polygoncount*3,GL_UNSIGNED_SHORT,0);
// Restore the original matrix
self.effect.transform.modelviewMatrix = originalMat;
This seems to work pretty good:
// Get the current modelview matrix
GLKMatrix4 originalMat = self.effect.transform.modelviewMatrix;
GLKMatrix4 currMat = self.effect.transform.modelviewMatrix;
// Print the original matrix for comparison
//NSLog(#"Original Matrix:");
//[self printMatrix:currMat];
// Define the buffer designators
GLuint billboardVertexArray;
GLuint billboardVertexBuffer;
GLuint billboardIndexBuffer;
glGenVertexArraysOES(1, &billboardVertexArray);
glBindVertexArrayOES(billboardVertexArray);
// Now draw the billboard
glGenBuffers(1, &billboardVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, billboardVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Billboard_vertex), Billboard_vertex, GL_STATIC_DRAW);
glGenBuffers(1, &billboardIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, billboardIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Billboard_index), Billboard_index, GL_STATIC_DRAW);
// u0,v0,normalx0,normaly0,normalz0,x0,y0,z0
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(5));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), BUFFER_OFFSET(2));
// Enable the Earth texture
self.effect.texture2d0.name = _billBoardTextureInfo.name;
self.effect.texture2d0.target = _billBoardTextureInfo.target;
// Bind the earth vertex array
glBindVertexArrayOES(billboardVertexArray);
// Now put a billboard at a specific Lat Lon - so first
// calculate XYZ from lat lon
XYZ xyz;
xyz.x = 0; xyz.y = 0; xyz.z = 0;
[self LLAtoXYZwithLat:35 andLon:-97 andXYZ:&xyz];
//NSLog(#"XYZ after: %f %f %f",xyz.x,xyz.y,xyz.z);
// Scale this object as well
//GLKMatrix4 scaledWithTranslationAndRotation = GLKMatrix4Scale(translationForLatLonWithTranslation, scale, scale, scale);
// Remove the Translation portion of the matrix
// | xx xy xz xw |
// | yx yy yz yw |
// | zx zy zz zw |
// | wx wy wz ww |
//
// | R T |
// | (0,0,0) 1 |
//
// d = sqrt( xx² + yx² + zx² )
//
// | d 0 0 T.x |
// | 0 d 0 T.y |
// | 0 0 d T.z |
// | 0 0 0 1 |
//
// union _GLKMatrix4
// {
// struct
// {
// float m00, m01, m02, m03;
// float m10, m11, m12, m13;
// float m20, m21, m22, m23;
// float m30, m31, m32, m33;
// };
// float m[16];
// }
// typedef union _GLKMatrix4 GLKMatrix4;
// Construct the rows in the new matrix
float d = sqrt( pow(currMat.m00,2) + pow(currMat.m10,2) + pow(currMat.m20,2) );
GLKVector4 columnToInsert0 = GLKVector4Make(d, 0, 0, xyz.x);
GLKVector4 columnToInsert1 = GLKVector4Make(0, d, 0, xyz.y);
GLKVector4 columnToInsert2 = GLKVector4Make(0, 0, d, xyz.z);
GLKVector4 columnToInsert3 = GLKVector4Make(0, 0, 0, 1);
// Build the new Matrix
GLKMatrix4 noTranslationInfo = GLKMatrix4SetRow(currMat, 0, columnToInsert0);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 1, columnToInsert1);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 2, columnToInsert2);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 3, columnToInsert3);
// Print out the 'no translation' matrix
//NSLog(#"No Translation Matrix:");
//[self printMatrix:noTranslationInfo];
// Get a rotation matrix from the quaternion we last rotated the globe with
GLKMatrix4 rotationMatrixFromQuaternion = GLKMatrix4MakeWithQuaternion( _quat );
//NSLog(#"Rotation Matrix from Quaternion: ");
//[self printMatrix:rotationMatrixFromQuaternion];
// Now use the matrix produced from our Quaternion to rotate the global coordinates
// of the billboard object
GLKMatrix4 rotatedNoTranslationInfo = GLKMatrix4Multiply(rotationMatrixFromQuaternion, noTranslationInfo);
//NSLog(#"rotatedNoTranslationInfo:");
//[self printMatrix:rotatedNoTranslationInfo];
// Throw the world translation coordinates in the matrix
noTranslationInfo.m30 = ( rotatedNoTranslationInfo.m30 );
noTranslationInfo.m31 = ( rotatedNoTranslationInfo.m31 );
noTranslationInfo.m32 = ( rotatedNoTranslationInfo.m32 + GLOBAL_EARTH_Z_LOCATION );
//NSLog(#"Final Matrix:");
//[self printMatrix:noTranslationInfo];
// Assign the new matrix to draw with - no translation
self.effect.transform.modelviewMatrix = noTranslationInfo;
// Render the object with GLKit
[self.effect prepareToDraw];
// Draw elements from the index array and uv / vertex / normal info
glDrawElements(GL_TRIANGLES,Billboard_polygoncount*3,GL_UNSIGNED_SHORT,0);
// Restore the original matrix
self.effect.transform.modelviewMatrix = originalMat;
When I've done this, I sent four vertices for the billboard, all from the center of the rectangle, to the vertex shader (really two sets of three, for the two triangles) and had the vertex shader displace them to always face the viewer. I describe this process in my answer here, but the relevant vertex shader code is as follows:
attribute vec4 position;
attribute vec4 inputImpostorSpaceCoordinate;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump vec3 normalizedViewCoordinate;
uniform mat4 modelViewProjMatrix;
uniform mediump mat4 orthographicMatrix;
uniform mediump float sphereRadius;
void main()
{
vec4 transformedPosition;
transformedPosition = modelViewProjMatrix * position;
impostorSpaceCoordinate = inputImpostorSpaceCoordinate.xy;
transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate.xy * vec2(sphereRadius);
transformedPosition = transformedPosition * orthographicMatrix;
normalizedViewCoordinate = (transformedPosition.xyz + 1.0) / 2.0;
gl_Position = transformedPosition;
}
The inputImpostorSpaceCoordinate attribute are four coordinates that range from (-1, -1) to (1, 1) and are supplied in parallel with the four vertices that make up the rectangular billboard you're trying to present. They act to displace the vertices relative to the screen and then also can provide texture coordinates for the kind of sub-map you'd use above. I used those values to do raytracing calculations for my sphere generation, but you can ignore that part of it.
(iPhone) I'm trying to draw a cube in ES2 with a different color on each face. Right now the colors aren't coming out right and I can't figure out why. Here's the relevant code:
- (void) DrawES2 {
glViewport ( 0, 0, backingWidth, backingHeight );
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUseProgram ( programObject );
int colorIndex = 0;
BOOL newFace = NO;
for(int i = 0; i < 36; i += 3)
{
GLfloat faceColor[] = { faceColors[colorIndex], faceColors[colorIndex+1], faceColors[colorIndex+2], faceColors[colorIndex+3] };
// Load the vertex data
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
// Load the color data
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, faceColor);
glEnableVertexAttribArray( 1 );
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &indices[i]);
newFace = ( i%2 == 0 ) ? NO : YES;
if( newFace )
colorIndex+=4;
}
}
GLfloat vVertices[] = { -0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f };
// Used to draw cube more efficiently
GLubyte indices[36] = {
4, 7, 3, //top face
4, 3, 0,
5, 6, 7, //front face
5, 7, 4,
3, 2, 1, //back face
0, 3, 1,
6, 2, 3, //right face
6, 3, 7,
5, 0, 1, //left face
5, 4, 0,
5, 2, 6, //bottom face
5, 1, 2 };
const GLfloat faceColors[] = {
0, 1, 0, 1,
1, 0.5f, 0, 1,
1, 0, 0, 1,
1, 1, 0, 1,
0, 0, 1, 1,
1, 0, 1, 1
};
GLbyte vShaderStr[] =
"uniform mat4 t_matrix; \n"
"uniform mat4 r_matrix; \n"
"uniform mat4 u_proj_matrix; \n"
"attribute vec4 vPosition; \n"
"attribute vec4 a_color; \n"
"varying vec4 v_color; \n"
"void main() \n"
"{ \n"
" mat4 model_matrix = t_matrix * r_matrix; \n"
" mat4 mvp_matrix = u_proj_matrix * model_matrix; \n"
" gl_Position = mvp_matrix * vPosition; \n"
" v_color = a_color; \n"
"} \n";
GLbyte fShaderStr[] =
"precision mediump float; \n"
"varying vec4 v_color; \n"
"void main() \n"
"{ \n"
" gl_FragColor = v_color; \n"
"}
You have probably already found this, but it seems like your color data is being declared and initialized as floats while you're setting up the vertex attribute array to be using unsigned bytes. This may be why the colors don't seem to be what you expect.
Just to mention it - if you wanna use bytes for colors, use:
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, faceColor);
(GL_TRUE instead of GL_FALSE) see Techniques for working with vertex data
You'll find that your logic is cleaner if you define vertex and element data via nested arrays:
GLubyte vVertices[][3] = {
{ -0.5f, -0.5f, 0.5f },
{ 0.5f, -0.5f, 0.5f },
...
};
GLubyte indices[][3] = {
{ 4, 7, 3 }, //top face
{ 4, 3, 0 },
...
};
GLfloat faceColors[][4] = {
{ 0, 1, 0, 1 },
{ 1, 0.5, 0, 1 },
...
};
It will also allow you to simply divide by two to get the color index, rather than using the awkward i%2 trick, which, BTW, is where you'll probably find the bug. You are incrementing the color index after the first face (i is still zero at that point, so i%2 == 0), so the second triangle of the top face gets a different color to the first one; the same problem will occur for all faces.
You have to enable the DEPTH_TEST on your engine otherwise the sequence of drawing of the vertices will override the logical Z DEPTH your eyes expect to see. Very very common initial error.
Try to enable the just after you clear the color and depth buffer.
glEnable(GL_DEPTH_TEST);
Cheers
So I bought O'reilly's Iphone 3D programming and found what I believe to be a bug in there code. However I can't figure out what the problem is, and unless I do I can't move forward with my own code.
I will paste what I consider to be the appropriate code into this post but luckily all the code is available online at:
http://examples.oreilly.com/9780596804831/HelloCone/
The problem I am having is with their OpenGL ES 2.0 renderer, it does not show up in their ES 1.1 renderer.
So what I have been noticing is that the cone does not render exactly in the correct position. To test this I changed the ModelViewMatrix to render exactly on the FrustumNear plane. So the cone should appear cut completely in two. When I do this with the ES 1.1 render this is the case, when I do the same in OpenGL ES 2.0 however it is not. The cone is for the most part there, but slightly shaved off. Meaning it is not landing exactly on the fustrum's near face.
Here is the initialization code where the projection matrix is created and set up:
void RenderingEngine2::Initialize(int width, int height)
{
const float coneRadius = 0.5f;
const float coneHeight = 1.0f;
const int coneSlices = 40;
{
// Allocate space for the cone vertices.
m_cone.resize((coneSlices + 1) * 2);
// Initialize the vertices of the triangle strip.
vector<Vertex>::iterator vertex = m_cone.begin();
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_cone.end(); theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness, brightness, 1);
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
}
{
// Allocate space for the disk vertices.
m_disk.resize(coneSlices + 2);
// Initialize the center vertex of the triangle fan.
vector<Vertex>::iterator vertex = m_disk.begin();
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = 0;
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = 0;
vertex++;
// Initialize the rim vertices of the triangle fan.
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_disk.end(); theta += dtheta) {
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex++;
}
}
// Create the depth buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
// Set up some GL state.
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
// Build the GLSL program.
m_simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(m_simpleProgram);
// Set the projection matrix.
GLint projectionUniform = glGetUniformLocation(m_simpleProgram, "Projection");
mat4 projectionMatrix = mat4::Frustum(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glUniformMatrix4fv(projectionUniform, 1, 0, projectionMatrix.Pointer());
}
And here is the Render code. As you can see I have changed the ModelVieMatrix to place the cone on the bottom left corner of the near Frustum face.
void RenderingEngine2::Render() const
{
GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");
GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
mat4 rotation(m_animation.Current.ToMatrix());
mat4 translation = mat4::Translate(-1.6, -2.4, -5);
// Set the model-view matrix.
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
mat4 modelviewMatrix = rotation * translation;
glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
// Draw the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_cone[0].Position.x;
const GLvoid* pColors = &m_cone[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_cone.size());
}
// Draw the disk that caps off the base of the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_disk[0].Position.x;
const GLvoid* pColors = &m_disk[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_FAN, 0, m_disk.size());
}
glDisableVertexAttribArray(positionSlot);
glDisableVertexAttribArray(colorSlot);
}
Looks like I found the answer to my own question.
The projection matrix in the O'Reilly code is being calculated incorrectly.
In their code they have:
T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;
However this is not the projection matrix. m.w.w should be 0 not 1.
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 0;
return m;