views:

521

answers:

6

Hi,

I'm building a first person shooter using OpenGL, and I'm trying to get a gun model to float in front of the camera. I've ripped a model from Fallout 3 using a resource decompiler (converted to .obj and loaded in).

However, this is what it looks like on the screen:

Screenshot

Half the gun's triangles are clipped to what appears to be the frustum.

I put it in front of my camera like this:

glPushMatrix();
    glLoadIdentity();

    glTranslatef(m_GunPos.x, m_GunPos.y, m_GunPos.z);
    glRotatef(m_GunRot.x, 1, 0, 0);
    glRotatef(m_GunRot.y, 0, 1, 0);
    glRotatef(m_GunRot.z, 0, 0, 1);
    glScalef(m_GunScale.x, m_GunScale.y, m_GunScale.z);

    m_Gun->Render(NULL);
glPopMatrix();

So I save the original GL_MODELVIEW matrix, load the identity matrix, translate my gun to be slightly to the right of my camera and render it. This is my render routine for a SceneNode:

glPushMatrix();
    if (m_Model) { m_Model->Render(&final); }

    if (m_Children.size() > 0)
    {
        for (std::vector<SceneNode*>::iterator i = m_Children.begin(); i != m_Children.end(); ++i)
        {
            (*i)->Render(&final);
        }
    }
glPopMatrix();

So it renders its own model and any child SceneNode's. Finally, the actual mesh rendering looks like this:

if (m_Material)
{
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, m_Material->m_TexDiffuse);
}

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

glVertexPointer(3, GL_FLOAT, sizeof(Vec3), &m_Vertex[0]);
glNormalPointer(GL_FLOAT, sizeof(Vec3), &m_Normal[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &m_UV[0]);

glDrawArrays(GL_TRIANGLES, 0, m_Vertex.size());

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

Is there any way to turn off clipping for just the gun? How do other games do this?

Thanks in advance.

+2  A: 

The problem is caused by the near-clipping plane.

Reduce the znear value you've used to calculate your projection matrix. Only make it as low as needed though. The lower it is, the more problems with z-fighting you will get.

Nils Pipenbrinck
Hmm. No. I have to set the znear to really low (0.0001) to not clip the gun and then I get huge z-fighting issues in a more complex scene.
knight666
+1  A: 

Did you try pushing your gun further into the scene (ie: away from the camera)? When you do so, are the polygon still clipped? If no, then, as suggested your model is being clipped by the near clipping place (set in your projection matrix). If you still have artifacts, the problem could be anything else from render states, model loading, etc, etc.

Have you seen the entire model in some viewer? Maybe that's how the model looks and the poly count was optimized because you need see those polys in the game

Francis Boivin
When I push the gun further into the scene (Z = -0.1 instead of -0.0001) the gun is no longer clipped. The problem only occurs when the gun is too close to the camera.
knight666
Well, there you have it, problem solved! It's clipped by the near plane. 0.0001 is insanely close to the camera, what are your units? For a fps, 1 = 1m is common. However, if you're ripping assets left and right with different units, you'll never have anything coherent. Also, to have it appear closer to the camera, maybe you want a different field of view. Again, 70-90 degrees is typical in a fps.
Francis Boivin
A: 

Why not just push the gun farther into the scene and scale it up so that it doesn't clip against the near plane?

munificent
+2  A: 

From the perspective (no pun intended) of OpenGL, the frustrum is just another matrix. You should be able to push the projection matrix, call gluPerspective (or glFrustrum, if you're adventurous) to set znear to a very small value, draw the gun, then pop the projection matrix and draw the rest of the scene (beware, however, that the projection matrix stack is often pretty shallow -- it can be as little as two levels).

One caveat: I've never really thought through how this should affect z-fighting. It might not do any real good -- it could work out the same as if you had the smaller znear value while drawing the whole scene.

Jerry Coffin
I figured it out on my own, but this turned out to be the solution. I set gluPerspective to different values (field of view of 30, znear of 0.0001), rendered my model and restored the old perspective. Works pretty good!
knight666
A: 

You have to specify a different near-far clip plane when you render close objects, such as a weapon hold by a character. This would cause the window-space depth values of the fragments to be invalid with respect to your other scene elements, so you have also have to modify the target depth range by calling glDepthRange. This will give you increase precision in camera near objects.

As you have mentioned, only modifying the near-far planes of your general scene frustum is not a solution, nor moving the gun forwards/backwards, since they introduce z-fighting issues.

tersyon
A: 

"Layering" with different projection matrix can be dangerous if you're not careful since the depth values between the different layers aren't in the same "space". What I'm calling a layer here is a group of objects rendered with a given projection matrix. There are usually 2 solutions to this:

  1. Render your different layers (with all the objects on them) starting from the furthest away and clearing the depth buffer between each renders.
  2. Partition the depth buffer between your layers using glDepthRange.

Note that using any of these techniques will allow you so fix potential issues with objects from different layers fighting together but there some drawbacks:

  1. It prevent objects from intersecting one another (near layers will always appear on top of further layers - they'll never intersect). Sometimes it's something that you wish to achieve anyway with fps where the gun is always on top of everything
  2. Your depth buffer will be unusable for stuff like postfx (depth of field, etc) or deferred rendering.
Francis Boivin