Sign in to follow this  

OpenGL glDrawElements: Vertices and Normals

This topic is 4173 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. I'm fairly new to OpenGL, so up until now I've been doing all my rendering in immediate mode, which I understand to be slow. I've got the code to load and render 3D models from files, but right now I only have immediate-mode render code, and I'm trying to write a version that uses vertex arrays. It looks like the gl*Pointer functions and glDrawElements are the way to go. However, I've run into a problem. My 3D model data is stored as usual with a list of vertices, and then a list of faces (triangles) that each refer to 3 vertices via an index number. There are also 3 normal vectors stored per face. But it appears that glDrawElements uses only one index array to reference both the array of vertices (set by glVertexPointer) and the array of normals (set by glNormalPointer). That means that there can only be one normal per vertex, but there could be more than one described in my 3D model file. How can I use vertex arrays with multiple normals per vertex? Or will I be forced to average all the normals on each vertex so there's only one per vertex? Thanks! EDIT: Sorry about that block of text. Split it into paragraphs for readability. [Edited by - Aikavanak on August 13, 2006 8:19:13 PM]

Share this post


Link to post
Share on other sites
You are correct that OpenGL uses a single index array to index all active vertex arrays (vertex, normal, color, etc.).

You may have to adapt your model data to reflect this. How to do so depends on whether you want smooth or faceted shading. For the latter, you 'detach' the faces so that each has its own copies of the vertex data. For the former, you average the normals associated with each vertex to yield a single normal (as you mentioned).

Share this post


Link to post
Share on other sites
firstly, paragraphs can help make post clearer [smile]

As I see it, you can go about it in one of two ways;

1 - average the normals as you suggested, however this can lead to sharp corners becoming rounded

2 - duplicate the vertex information for each normal as required. This will increase the vertex data size however chances are in a model of any real size this kind of sharing is rare as such the size increase shouldn't be a huge problem..

Share this post


Link to post
Share on other sites
Thanks for the quick replies.

I think I'll go with duplicating the vertex information, because it seems like averaging the normals could distort my 3D modeler's artwork a bit too much for my tastes.

But I have another question about vertex arrays. In my code, I load the model data into my own custom arrays (I use a custom Vector class to help store the data). I obviously can't send my own Vector classes to gl*Pointer functions directly, so I could copy all the data from my Vectors to a new array of floats (float[]) that I can send directly to gl*Pointer. Is this how its generally done? If so, it seems like it could be really bad for performance, since I would basically be copying all my model data every frame. And, it would take more memory.

Should I load the model data directly into float[] arrays that I can pass to gl*Pointer functions? Will this give me a noticeable boost in framerate?

(I would try it both ways myself, but it would require a lot of re-working of code, and I don't feel like doing it right now. Just wondering if any of you have experience with this already.)

Share this post


Link to post
Share on other sites
There are two pretty simple solutions:

First you could make your vector class only store the 2 / 3 / whatever floats and tell the compiler not to add any unused bytes for packing it into a power of two boundry, so your vector class is memory equivalent to the floats it stores. This also means you have to turn down the abillity to use virtual functions and other syntactic sugar, which increases your classes size, but that shouldn't be any problem for any vector class implementation.

The second one, which takes up more memory and is a little less efficient than the first one, but definitely more than copying all the data back and forth, would be to let your vector class hold a pointer to the first of its floats which is actually stored in the vertexarray and the dimension of the vector itself ( can be templated, so that it doesn't need any additional space ).

You should stick with the first solution as long as you don't need any extra variables in your vector class or something that takes up memory, because the second solution adds an additional 4 bytes per vertex which normally is 12 bytes of size ( increase of 33% ) or per normal which is 8 bytes ( 50% ! ) plus the runtime overhead of resolving the pointers ( not that much ^^ ) and the need of initialising all pointers and keeping track of their validity.



t0}{!c

Share this post


Link to post
Share on other sites
while vector classes are good at holding date when it comes to locations, directions and other singlar data I don't see the point in having them when it comes to model information, you won't be perform per-vertex changes on them in any meaningfull way once your load step is completed, as such while you might want to initally load your data into your customs classes once you've done any jiggery pokery you need to it you might as well dump it into a normal array.

Well, a normal array might well be overkill, instead consider a plain struct consisting of the data you'll want per vertex.


struct terraindata {
float x,y,z;
GLubyte r,g,b,a; // use four bytes so we dont botch the alignment up
float u,v; // texture U,V coords
float nx,ny,nz; // normal vector for vertex
};



Thats one I've used before now.

You can even make a std::vector of them and render from that, just pass the gl*Pointer() functions the address of the first element as the data location (gl*Pointer(...,static_cast<void*>(&myVec[0]));).

Oh, and interleaving the data as above is a good idea on all hardware and maintaining 4btye alignment on the vertex elements is critical if you don't want to it die on ATI hardware.

Share this post


Link to post
Share on other sites
Actually my first solution behaves exactly the like phantoms. consider

struct TerrainData
{
vector< float, 3 > Vertex;
color< desc_rgba > Color;
vector< float, 2 > TexCoord;
vector< float, 3 > Normal;
};

// Now later you can perform stuff like

bla->getTerrainData()[234].Normal.normalize();



While this code would behave the same as your code it still allows you to perform all your vector algorithms on the data. You surely wont need them that often but once you will, you'll be glad to not beeing forced to rewrite code or extract the data and then put it in again.

Share this post


Link to post
Share on other sites
Thanks again for the informative replies.

I suppose I should have mentioned that I'm actually using Java and JOGL for my programs, I didn't post this in the Java forum because it's not really a Java-specific topic. But as you all probably know, Java doesn't give the coder as much control over pointers as C++ does, so I'm not sure how to implement your examples with Java.

Technically, JOGL requires NewIO Buffer objects for the gl*Pointer() calls. Any Java masters out there who might have some insight into adapting toxic's first solution to Java and FloatBuffers? (a ByteBuffer could probably also be used.)

From my look through the FloatBuffer API docs, I can't see an easy way to keep my data in a custom class and get it to a gl*Pointer function via Buffers without copying my custom data to a Buffer every frame.

Oh well, maybe I'll just have to do without my vector operations. Like toxic said, they won't be needed often, but it'd be nice to have the ability if the need comes up.


BTW, I've also decided to write two versions of my loader, one that averages the normals and one that duplicates the vertices to preserve normals when needed, just to cover all possibilities.

Share this post


Link to post
Share on other sites

This topic is 4173 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By xhcao
      Does sync be needed to read texture content after access texture image in compute shader?
      My simple code is as below,
      glUseProgram(program.get());
      glBindImageTexture(0, texture[0], 0, GL_FALSE, 3, GL_READ_ONLY, GL_R32UI);
      glBindImageTexture(1, texture[1], 0, GL_FALSE, 4, GL_WRITE_ONLY, GL_R32UI);
      glDispatchCompute(1, 1, 1);
      // Does sync be needed here?
      glUseProgram(0);
      glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
      glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                     GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture[1], 0);
      glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
       
      Compute shader is very simple, imageLoad content from texture[0], and imageStore content to texture[1]. Does need to sync after dispatchCompute?
    • By Jonathan2006
      My question: is it possible to transform multiple angular velocities so that they can be reinserted as one? My research is below:
      // This works quat quaternion1 = GEQuaternionFromAngleRadians(angleRadiansVector1); quat quaternion2 = GEMultiplyQuaternions(quaternion1, GEQuaternionFromAngleRadians(angleRadiansVector2)); quat quaternion3 = GEMultiplyQuaternions(quaternion2, GEQuaternionFromAngleRadians(angleRadiansVector3)); glMultMatrixf(GEMat4FromQuaternion(quaternion3).array); // The first two work fine but not the third. Why? quat quaternion1 = GEQuaternionFromAngleRadians(angleRadiansVector1); vec3 vector1 = GETransformQuaternionAndVector(quaternion1, angularVelocity1); quat quaternion2 = GEQuaternionFromAngleRadians(angleRadiansVector2); vec3 vector2 = GETransformQuaternionAndVector(quaternion2, angularVelocity2); // This doesn't work //quat quaternion3 = GEQuaternionFromAngleRadians(angleRadiansVector3); //vec3 vector3 = GETransformQuaternionAndVector(quaternion3, angularVelocity3); vec3 angleVelocity = GEAddVectors(vector1, vector2); // Does not work: vec3 angleVelocity = GEAddVectors(vector1, GEAddVectors(vector2, vector3)); static vec3 angleRadiansVector; vec3 angularAcceleration = GESetVector(0.0, 0.0, 0.0); // Sending it through one angular velocity later in my motion engine angleVelocity = GEAddVectors(angleVelocity, GEMultiplyVectorAndScalar(angularAcceleration, timeStep)); angleRadiansVector = GEAddVectors(angleRadiansVector, GEMultiplyVectorAndScalar(angleVelocity, timeStep)); glMultMatrixf(GEMat4FromEulerAngle(angleRadiansVector).array); Also how do I combine multiple angularAcceleration variables? Is there an easier way to transform the angular values?
    • By dpadam450
      I have this code below in both my vertex and fragment shader, however when I request glGetUniformLocation("Lights[0].diffuse") or "Lights[0].attenuation", it returns -1. It will only give me a valid uniform location if I actually use the diffuse/attenuation variables in the VERTEX shader. Because I use position in the vertex shader, it always returns a valid uniform location. I've read that I can share uniforms across both vertex and fragment, but I'm confused what this is even compiling to if this is the case.
       
      #define NUM_LIGHTS 2
      struct Light
      {
          vec3 position;
          vec3 diffuse;
          float attenuation;
      };
      uniform Light Lights[NUM_LIGHTS];
       
       
    • By pr033r
      Hello,
      I have a Bachelor project on topic "Implenet 3D Boid's algorithm in OpenGL". All OpenGL issues works fine for me, all rendering etc. But when I started implement the boid's algorithm it was getting worse and worse. I read article (http://natureofcode.com/book/chapter-6-autonomous-agents/) inspirate from another code (here: https://github.com/jyanar/Boids/tree/master/src) but it still doesn't work like in tutorials and videos. For example the main problem: when I apply Cohesion (one of three main laws of boids) it makes some "cycling knot". Second, when some flock touch to another it scary change the coordination or respawn in origin (x: 0, y:0. z:0). Just some streng things. 
      I followed many tutorials, change a try everything but it isn't so smooth, without lags like in another videos. I really need your help. 
      My code (optimalizing branch): https://github.com/pr033r/BachelorProject/tree/Optimalizing
      Exe file (if you want to look) and models folder (for those who will download the sources):
      http://leteckaposta.cz/367190436
      Thanks for any help...

    • By Andrija
      I am currently trying to implement shadow mapping into my project , but although i can render my depth map to the screen and it looks okay , when i sample it with shadowCoords there is no shadow.
      Here is my light space matrix calculation
      mat4x4 lightViewMatrix; vec3 sun_pos = {SUN_OFFSET * the_sun->direction[0], SUN_OFFSET * the_sun->direction[1], SUN_OFFSET * the_sun->direction[2]}; mat4x4_look_at(lightViewMatrix,sun_pos,player->pos,up); mat4x4_mul(lightSpaceMatrix,lightProjMatrix,lightViewMatrix); I will tweak the values for the size and frustum of the shadow map, but for now i just want to draw shadows around the player position
      the_sun->direction is a normalized vector so i multiply it by a constant to get the position.
      player->pos is the camera position in world space
      the light projection matrix is calculated like this:
      mat4x4_ortho(lightProjMatrix,-SHADOW_FAR,SHADOW_FAR,-SHADOW_FAR,SHADOW_FAR,NEAR,SHADOW_FAR); Shadow vertex shader:
      uniform mat4 light_space_matrix; void main() { gl_Position = light_space_matrix * transfMatrix * vec4(position, 1.0f); } Shadow fragment shader:
      out float fragDepth; void main() { fragDepth = gl_FragCoord.z; } I am using deferred rendering so i have all my world positions in the g_positions buffer
      My shadow calculation in the deferred fragment shader:
      float get_shadow_fac(vec4 light_space_pos) { vec3 shadow_coords = light_space_pos.xyz / light_space_pos.w; shadow_coords = shadow_coords * 0.5 + 0.5; float closest_depth = texture(shadow_map, shadow_coords.xy).r; float current_depth = shadow_coords.z; float shadow_fac = 1.0; if(closest_depth < current_depth) shadow_fac = 0.5; return shadow_fac; } I call the function like this:
      get_shadow_fac(light_space_matrix * vec4(position,1.0)); Where position is the value i got from sampling the g_position buffer
      Here is my depth texture (i know it will produce low quality shadows but i just want to get it working for now):
      sorry because of the compression , the black smudges are trees ... https://i.stack.imgur.com/T43aK.jpg
      EDIT: Depth texture attachment:
      glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24,fbo->width,fbo->height,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fbo->depthTexture, 0);
  • Popular Now