Sign in to follow this  
n0obAtroN

OpenGL OpenGL Frustum Culling Problem

Recommended Posts

Hello everybody. I have a rather complex scene I want to cull via the frustum. However, for some strange reason my implementation is not working (nothing is drawn). I have looked at *many* of other people implementations, and it seems as if they are identical to mine. Here are my various data structures:
struct Plane
{
    f32 a, b, c, d;
};

struct Matrix4x4
{
    f32 m[16];
};

struct vector3df
{
    f32 x, y, z;
};
Here are various mathematical functions:
FASTCALL struct Matrix4x4 multiplyMatrix4x4(struct Matrix4x4 a, struct Matrix4x4 b)
{
    struct Matrix4x4 c;

	c.m[0]  = a.m[0] * b.m[0] + a.m[1] * b.m[4] + a.m[2] * b.m[8] + a.m[3] * b.m[12];
	c.m[1]  = a.m[0] * b.m[1] + a.m[1] * b.m[5] + a.m[2] * b.m[9] + a.m[3] * b.m[13];
	c.m[2]  = a.m[0] * b.m[2] + a.m[1] * b.m[6] + a.m[2] * b.m[10] + a.m[3] * b.m[14];
	c.m[3]  = a.m[0] * b.m[3] + a.m[1] * b.m[7] + a.m[2] * b.m[11] + a.m[3] * b.m[15];

	c.m[4]  = a.m[4] * b.m[0] + a.m[5] * b.m[4] + a.m[6] * b.m[8] + a.m[7] * b.m[12];
	c.m[5]  = a.m[4] * b.m[1] + a.m[5] * b.m[5] + a.m[6] * b.m[9] + a.m[7] * b.m[13];
	c.m[6]  = a.m[4] * b.m[2] + a.m[5] * b.m[6] + a.m[6] * b.m[10] + a.m[7] * b.m[14];
	c.m[7]  = a.m[4] * b.m[3] + a.m[5] * b.m[7] + a.m[6] * b.m[11] + a.m[7] * b.m[15];

	c.m[8]  = a.m[8] * b.m[0] + a.m[9] * b.m[4] + a.m[10] * b.m[8] + a.m[11] * b.m[12];
	c.m[9]  = a.m[8] * b.m[1] + a.m[9] * b.m[5] + a.m[10] * b.m[9] + a.m[11] * b.m[13];
	c.m[10] = a.m[8] * b.m[2] + a.m[9] * b.m[6] + a.m[10] * b.m[10] + a.m[11] * b.m[14];
	c.m[11] = a.m[8] * b.m[3] + a.m[9] * b.m[7] + a.m[10] * b.m[11] + a.m[11] * b.m[15];

	c.m[12] = a.m[12] * b.m[0] + a.m[13] * b.m[4] + a.m[14] * b.m[8] + a.m[15] * b.m[12];
	c.m[13] = a.m[12] * b.m[1] + a.m[13] * b.m[5] + a.m[14] * b.m[9] + a.m[15] * b.m[13];
	c.m[14] = a.m[12] * b.m[2] + a.m[13] * b.m[6] + a.m[14] * b.m[10] + a.m[15] * b.m[14];
	c.m[15] = a.m[12] * b.m[3] + a.m[13] * b.m[7] + a.m[14] * b.m[11] + a.m[15] * b.m[15];


    return c;
}

FASTCALL void normalizePlane(struct Plane * plane)
{
    f32 mag = sqrtf(plane->a * plane->a + plane->b * plane->b + plane->c * plane->c);

    if(mag)
    {
        plane->a /= mag;
        plane->b /= mag;
        plane->c /= mag;
        plane->d /= mag;
    }
}
Here is the global frustum planes declaration:
struct Plane frustumPlane[6];
Here is the frustum extraction function:
FASTCALL void updateFrustum()
{
    struct Matrix4x4 proj, modl, clip;
    struct Plane * p;

    glPushMatrix();
    glLoadIdentity();

    glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
    glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

    glPopMatrix();

    clip = multiplyMatrix4x4(proj, modl);

    printf("{%f, %f, %f, %f\n %f, %f, %f, %f\n %f, %f, %f, %f\n %f, %f, %f, %f}\n\n",
           clip.m[0], clip.m[1], clip.m[2], clip.m[3],
           clip.m[4], clip.m[5], clip.m[6], clip.m[7],
           clip.m[8], clip.m[9], clip.m[10], clip.m[11],
           clip.m[12], clip.m[13], clip.m[14], clip.m[15]);

    p = &frustumPlane[RIGHT];
    p->a = clip.m[3]  - clip.m[0];
    p->b = clip.m[7]  - clip.m[4];
    p->c = clip.m[11] - clip.m[8];
    p->d = clip.m[15] - clip.m[12];

    p = &frustumPlane[LEFT];
    p->a = clip.m[3]  + clip.m[0];
    p->b = clip.m[7]  + clip.m[4];
    p->c = clip.m[11] + clip.m[8];
    p->d = clip.m[15] + clip.m[12];

    p = &frustumPlane[BOTTOM];
    p->a = clip.m[3]  + clip.m[1];
    p->b = clip.m[7]  + clip.m[5];
    p->c = clip.m[11] + clip.m[9];
    p->d = clip.m[15] + clip.m[13];

    p = &frustumPlane[TOP];
    p->a = clip.m[3]  - clip.m[1];
    p->b = clip.m[7]  - clip.m[5];
    p->c = clip.m[11] - clip.m[9];
    p->d = clip.m[15] - clip.m[13];

    p = &frustumPlane[BACK];
    p->a = clip.m[3]  - clip.m[2];
    p->b = clip.m[7]  - clip.m[6];
    p->c = clip.m[11] - clip.m[10];
    p->d = clip.m[15] - clip.m[14];

    p = &frustumPlane[FRONT];
    p->a = clip.m[3]  + clip.m[2];
    p->b = clip.m[7]  + clip.m[6];
    p->c = clip.m[11] + clip.m[10];
    p->d = clip.m[15] + clip.m[14];

    u8 i;
    for (i = 0; i < 6; i++)
        normalizePlane(&frustumPlane[i]);
}
Here is the function to test if a vertex is in the frustum:
FASTCALL bool isVector3dfInFrustum(struct vector3df d)
{
    if(frustumPlane[RIGHT].a * d.x + frustumPlane[RIGHT].b * d.y + frustumPlane[RIGHT].c * d.z + frustumPlane[RIGHT].d > 0)
        return false;

    if(frustumPlane[LEFT].a * d.x + frustumPlane[LEFT].b * d.y + frustumPlane[LEFT].c * d.z + frustumPlane[LEFT].d > 0)
        return false;

    if(frustumPlane[BACK].a * d.x + frustumPlane[BACK].b * d.y + frustumPlane[BACK].c * d.z + frustumPlane[BACK].d > 0)
        return false;

    if(frustumPlane[FRONT].a * d.x + frustumPlane[FRONT].b * d.y + frustumPlane[FRONT].c * d.z + frustumPlane[FRONT].d > 0)
        return false;

    if(frustumPlane[TOP].a * d.x + frustumPlane[TOP].b * d.y + frustumPlane[TOP].c * d.z + frustumPlane[TOP].d > 0)
        return false;

    if(frustumPlane[BOTTOM].a * d.x + frustumPlane[BOTTOM].b * d.y + frustumPlane[BOTTOM].c * d.z + frustumPlane[BOTTOM].d > 0)
        return false;

    return true;
}
And a simple test case scenario:
    glBegin(GL_POINTS);
    glPointSize(5.0f);

    s32 x, y, z;
    u32 count = 0;
    for(x = -100; x < 100; x++)
        for(y = -100; y < 100; y++)
            for(z = -100; z < 100; z++)
                if(isVector3dfInFrustum(makeVector3df(x, y, z)))
                {
                    glVertex3i(x, y, z);
                    count++;
                }

    glEnd();

    printf("%lu\n", count);
This test case scenario renders 0 of the points, regardless of the camera orientation. Any ideas on what I might be doing wrong? [Edited by - n0obAtroN on June 10, 2009 2:39:47 AM]

Share this post


Link to post
Share on other sites
Couple of things:

1. In updateFrustum(), you're not setting the matrix mode before pushing the matrix stack, so it's not clear which stack is being manipulated. Also, since you're loading the identity matrix before querying for the projection and modelview matrices, I would think that one of the two would always come back as identity (which would definitely be wrong in the case of the projection matrix, and is likely to be wrong in the case of the modelview matrix).

2. I'd recommend creating a separate function for testing a point against a plane rather than writing the test out manually each time; this will help avoid typos and copy-and-paste errors.

Frustum culling code (especially based on this particular algorithm), as well as math library code in general, is difficult to debug just by looking at it; a single typo (such as a wrong index in a matrix multiplication function) can throw the whole thing off.

Testing the functions and components individually can help. With frustum culling, I recommend confirming visually that the frustum planes are correct before moving on to culling. It can be a bit of a pain to set up, but a fairly straightforward way to generate debug graphics for the frustum is to intersect the planes in sets of three to yield the corners of the frustum, and then render the 6 quadrilaterals making up the frustum. (You'll most likely need to attach the frustum to an object other than the camera in order to see the debug graphics clearly.)

Share this post


Link to post
Share on other sites
Hi all!
I have a similar problem:
I want to calculate the points of the View frustum (after the transformations of course).

I just don't know why it isn't working.

Calculating code:
float MVM[16];
void Calc_Frustum()
{
float pt[16] = {0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,1};
float out[16];
float inv_MVM[16];

glGetFloatv(GL_MODELVIEW_MATRIX, MVM);
MatrixTranspose(MVM,inv_MVM);

for( int i = 0; i < 5; i++ )
{
pt[0] = Frustum_Points[i][0];
pt[1] = Frustum_Points[i][1];
pt[2] = Frustum_Points[i][2];

MatrixMult(inv_MVM,pt,out);

Frustum_Tarnsformed_Points[i][0] = out[0];
Frustum_Tarnsformed_Points[i][1] = out[1];
Frustum_Tarnsformed_Points[i][2] = out[2];
}
}

void MatrixMult(float *in,float *in2, float *out)
{
out[ 0] = in[0]*in2[0]+in[4]*in2[1]+in[8]*in2[2]+in[12]*in2[3];
out[ 1] = in[1]*in2[0]+in[5]*in2[1]+in[9]*in2[2]+in[13]*in2[3];
out[ 2] = in[2]*in2[0]+in[6]*in2[1]+in[10]*in2[2]+in[14]*in2[3];
out[ 3] = in[3]*in2[0]+in[7]*in2[1]+in[11]*in2[2]+in[15]*in2[3];
out[ 4] = in[0]*in2[4]+in[4]*in2[5]+in[8]*in2[6]+in[12]*in2[7];
out[ 5] = in[1]*in2[4]+in[5]*in2[5]+in[9]*in2[6]+in[13]*in2[7];
out[ 6] = in[2]*in2[4]+in[6]*in2[5]+in[10]*in2[6]+in[14]*in2[7];
out[ 7] = in[3]*in2[4]+in[7]*in2[5]+in[11]*in2[6]+in[15]*in2[7];
out[ 8] = in[0]*in2[8]+in[4]*in2[9]+in[8]*in2[10]+in[12]*in2[11];
out[ 9] = in[1]*in2[8]+in[5]*in2[9]+in[9]*in2[10]+in[13]*in2[11];
out[10] = in[2]*in2[8]+in[6]*in2[9]+in[10]*in2[10]+in[14]*in2[11];
out[11] = in[3]*in2[8]+in[7]*in2[9]+in[11]*in2[10]+in[15]*in2[11];
out[12] = in[0]*in2[12]+in[4]*in2[13]+in[8]*in2[14]+in[12]*in2[15];
out[13] = in[1]*in2[12]+in[5]*in2[13]+in[9]*in2[14]+in[13]*in2[15];
out[14] = in[2]*in2[12]+in[6]*in2[13]+in[10]*in2[14]+in[14]*in2[15];
out[15] = in[3]*in2[12]+in[7]*in2[13]+in[11]*in2[14]+in[15]*in2[15];
} This works well in any other places.

void MatrixTranspose(float *in, float *out)
{
out[ 0] = in[0];
out[ 1] = in[4];
out[ 2] = in[8];
out[ 3] = in[12];

out[ 4] = in[1];
out[ 5] = in[5];
out[ 6] = in[9];
out[ 7] = in[13];

out[ 8] = in[2];
out[ 9] = in[6];
out[10] = in[10];
out[11] = in[14];

out[12] = in[3];
out[13] = in[7];
out[14] = in[11];
out[15] = in[15];
}

The Calc_Frustum() is called at the proper place in the main code, look at the glLoadMatrixf(MVM) for the debugging.
...
SetupView();
Calc_Frustum();
glLoadMatrixf(MVM); // its unnecessary
//drawing stuff
...

Drawing the calculated frustum points:
...
glLoadMatrixf(MVM);
Draw_Frustum();
...

There are no transformations in Draw_Frustum(), just drawing the lines.
If the calculation is correct, the frustum should appear as a screen sized rectangle, regardless of the camera orientation.
The rotations apply, but the translations don't, so I see the rectangle, but its position is changing.
Maybe matrix representation of the points are wrong? Or I shouldn't invert the modelview matrix? Or something totally wrong here...

Thanks!

Share this post


Link to post
Share on other sites
You should try to draw the frustum. This isn't easy, try to draw the normals of the
side planes, then draw the front and back planes as Quads with different colors and backface culling enabled. So you can determine if the orientations are good.

Or you could try (if you haven't yet) to comment all the statements in the isVector3dfInFrustum() function, so the points always pass, then uncomment the statements one by one, and just one at a time. Because if one of them is incorrect, the points will always fail.

The glLoadIdentity() in the updateFrustum() isn't clear for me.

Share this post


Link to post
Share on other sites
I have updated the code to try to eliminate typos. Here it is:


#define calculateFrustumPlane(a, b, c) for(i = 0; i < 4; i++) p->d[i] = clip.m[a + i * 4] b clip.m[c + i * 4]

FASTCALL void updateFrustum()
{
struct Matrix4x4 proj, modl, clip;
struct Plane * p;
u8 i; // used in calculateFrustumPlane() macro

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

glPopMatrix();

clip = multiplyMatrix4x4(proj, modl);

p = &frustumPlane[RIGHT];
calculateFrustumPlane(3, -, 0);

p = &frustumPlane[LEFT];
calculateFrustumPlane(3, +, 0);

p = &frustumPlane[BOTTOM];
calculateFrustumPlane(3, +, 1);

p = &frustumPlane[TOP];
calculateFrustumPlane(3, -, 1);

p = &frustumPlane[BACK];
calculateFrustumPlane(3, -, 2);

p = &frustumPlane[FRONT];
calculateFrustumPlane(3, +, 2);

for (i = 0; i < 6; i++)
normalizePlane(&frustumPlane[i]);
}

FASTCALL bool isVector3dfInFrustum(struct vector3df d)
{
u8 i;
for(i = 0; i < 6; i++)
if(frustumPlane[BOTTOM].d[0] * d.x + frustumPlane[BOTTOM].d[1] * d.y + frustumPlane[BOTTOM].d[2] * d.z + frustumPlane[BOTTOM].d[3] > 0)
return false;

return true;
}

Share this post


Link to post
Share on other sites
As noted above, you're testing against the same plane each time through the loop. Also, you should really make the point-plane test a function; using a loop to eliminate some of the redundant code is a good start, but it would make much more sense to make this a separate function that can be tested in isolation and used elsewhere in your code if needed.

I would also advise against making calculateFrustumPlane a macro. It looks like you're programming in pure C (where use of macros is a little more defensible than in C++), but there are still better, safer alternatives to using a macro (for example, you could use a function rather than a macro, and use a function pointer to abstract away the summation operation).

Share this post


Link to post
Share on other sites
Thank you for your help. However, I prefer C over C++ for many reasons, and will stick to using macros as it saves a function call (much the way an inline function does).

I have come here for help in debugging frustum culling code, not for the criticism of my coding technique.

The code that has been posted on this forum is a quick test case scenario, that has been broken down into smaller, stringier chunks to make it easier for online debugging here on the forums.

Hence, I openly welcome help on matters pertaining to why the frustum is not being extracted properly.

Thank you.

Share this post


Link to post
Share on other sites
Quote:
Thank you for your help. However, I prefer C over C++ for many reasons, and will stick to using macros as it saves a function call (much the way an inline function does).

I have come here for help in debugging frustum culling code, not for the criticism of my coding technique.
For the most part, my comments on your coding style were entirely relevant. The types of things you were doing in your originally posted code (typing things out by hand, overlooking opportunities to refactor) are *exactly* the kinds of things that make complex code like this difficult to debug. So I stand by my recommendations: cleaning up the code and addressing technical and stylistic issues can often be an important (sometimes even necessary) first step in fixing this sort of problem.

As for the macro, in most cases, frustum plane extraction code will be called once per frame, maybe a couple of times if you're doing picking or something of that sort. Do you really think the performance difference (if any) between a macro and a function call is going to be measurable in this case? Or is this a premature optimization? Have you profiled the application? If so, does the frustum plane extraction code even show up in the profile?

The reason I mention the macro thing is that in some cases using macros can introduce subtle bugs that can be avoided by using real functions. As such, using a function rather than a macro here could eliminate a possible source of error, thereby making it easier to debug the algorithm as a whole. (Also, note that I said nothing about using C++ rather than C, so I'm not sure what your comment about preferring C is in relation to.)

Again, stylistic and technical analysis of code such as this is often entirely relevant, even when it's not the stated subject of the post. Sorry the suggestions bug you, but they really are intended to help.

Share this post


Link to post
Share on other sites
The application I am writing is the basis of a multi-computer multi-threaded rendering farm. The culling code is re-used for every refraction rendering pass, every reflection rendering pass, and every normal rendering pass.

To set up a test case scenario to show how much juice the frustum culling code is taking I will (arbitrarily) create a 3D scene of a single house. This house has water running in the sink, 5 candles burning, and lets say 1 mirror, and 1 window. We will render 1 minute of video at 25 FPS.

The flames of each candle is composed of about ~5000 particles. Each particle has a unique light source attached to it, and affects the lighting of objects around it. It also affects the shadow projections of the objects around it. Thats 5x5000 light sources, and 5x5000 textured quads. For each of the particles the scene must be translated to its location, and the scene re-rendered for the shadow pass of the particles light source. Each time the scene is rendered, the culling code is called. Thats approximately 5x5000 calls to eh frustum culling code.

The water in the sink has to refract the scene, and reflect the scene. It must re-render all the geometry for each pass. This multiplies the frustum calls by 3 (original pass, reflection pass, and refraction pass). That is approximately 3x5x5000 calls to the frustum code.

The mirror only reflects the scene, and hence multiplies the frustum calls by 2. That is approximately 2x2x5x5000 calls to the frustum code.

The window both refracts the scene and reflects the scene, and multiplies the number of calls by 3. That is approximately 3x2x2x5x5000 calls to the frustum code.

That is a total of 300,000 calls to the frustum code in a single frame. Running at 25 FPS for 60 seconds, it is a grand total of 450,000,000 calls to the frustum code.


Rendering a scene of a house, with a window, a mirror, and 5 candles for 1 minute has called the frustum code 450,000,000 times. This is not a realistic scenario, as that would include a lot more in the scene and a lot more calls to the frustum code.

As you can see the frustum code may not be the biggest juice sucker, but still uses a massive amount of juice. This is why I am counting the number of function calls I use, limit the number of variables (this includes variables used by loops), and using FASTCALL calling conventions. This results in much stringier code, yes, but also much faster in the long run.

I would love to write it in a less-stringy way, but its not an option.

With that said, any ideas why the frustum is not being extracted correctly?

Share this post


Link to post
Share on other sites
Quote:
The application I am writing is the basis of a multi-computer multi-threaded rendering farm. The culling code is re-used for every refraction rendering pass, every reflection rendering pass, and every normal rendering pass.

To set up a test case scenario to show how much juice the frustum culling code is taking I will (arbitrarily) create a 3D scene of a single house. This house has water running in the sink, 5 candles burning, and lets say 1 mirror, and 1 window. We will render 1 minute of video at 25 FPS.

The flames of each candle is composed of about ~5000 particles. Each particle has a unique light source attached to it, and affects the lighting of objects around it. It also affects the shadow projections of the objects around it. Thats 5x5000 light sources, and 5x5000 textured quads. For each of the particles the scene must be translated to its location, and the scene re-rendered for the shadow pass of the particles light source. Each time the scene is rendered, the culling code is called. Thats approximately 5x5000 calls to eh frustum culling code.

The water in the sink has to refract the scene, and reflect the scene. It must re-render all the geometry for each pass. This multiplies the frustum calls by 3 (original pass, reflection pass, and refraction pass). That is approximately 3x5x5000 calls to the frustum code.

The mirror only reflects the scene, and hence multiplies the frustum calls by 2. That is approximately 2x2x5x5000 calls to the frustum code.

The window both refracts the scene and reflects the scene, and multiplies the number of calls by 3. That is approximately 3x2x2x5x5000 calls to the frustum code.

That is a total of 300,000 calls to the frustum code in a single frame. Running at 25 FPS for 60 seconds, it is a grand total of 450,000,000 calls to the frustum code.
So, you're saying the frustum code is called a lot? (j/k :)

I didn't spot anything, but here are some things you might double-check:

1. Did you already fix the loop that was only testing against the bottom frustum plane? (Just checking...)

2. There are two forms of the frustum extraction algorithm you're using: one for D3D-style projection matrices, and one for OpenGL-style projection matrices (they differ in that D3D uses a distance of 0 for the near plane of the canonical view volume, while OpenGL uses a distance of -1). You might check to make sure you're using the right form of the algorithm, just in case.

I'm still not clear on how you're handling the matrices exactly (for example, right now you have a matrix push-pop in your code that doesn't actually do anything), so I can't really comment on that part of the code.

Lastly, have you tried attaching the frustum to a 'test' object of some sort and rendering it so that you can see if it's correct? That might help you nail down the source of the problem (if the frustum is obviously wrong, for example, that will point towards the frustum extraction or matrix transform code rather than the culling code).

Share this post


Link to post
Share on other sites
As I have now idea whats wrong, I would suggest you start by culling against one of the planes at a time. Also, try to cull against a different camera than your actual openGL view camera, then you can render the extracted planes as huge squares and literally see whats going one.

As long as it doesn't work, you should not care about speed. As soon as you get it working, you can refactor the code to your preferred lightning fast equivalent.

Share this post


Link to post
Share on other sites
Quote:
Original post by szecs
You should try to draw the frustum. This isn't easy, try to draw the normals of the
side planes, then draw the front and back planes as Quads with different colors and backface culling enabled. So you can determine if the orientations are good.

Or you could try (if you haven't yet) to comment all the statements in the isVector3dfInFrustum() function, so the points always pass, then uncomment the statements one by one, and just one at a time. Because if one of them is incorrect, the points will always fail.



These ideas are totally wrong?
Or haven't you read this comment?
Or maybe my English :D
It's maybe 5 minutes of work and will work.
If it's bullshit than Sorry!



Share this post


Link to post
Share on other sites
#1:
Expanding your calculateFrustumPlane() macro reveals your method to be exactly like mine (with one exception covered in #2). This is a problem, since my matrices are row-major and yours are column-major. Transpose your matrix before using this routine, or rewrite the routine to work with column-major matrices.


#2:
Plane distances are always stored negative. This reduces a lot of otherwise redundant negations in plane equations.
This is the difference between my routine and yours.

A snippet of mine:
		// Left clipping plane.
pLeftPlane.n.x = _mMatrix._14 + _mMatrix._11;
pLeftPlane.n.y = _mMatrix._24 + _mMatrix._21;
pLeftPlane.n.z = _mMatrix._34 + _mMatrix._31;
pLeftPlane.dist = -(_mMatrix._44 + _mMatrix._41);

// Right clipping plane.
pRightPlane.n.x = _mMatrix._14 - _mMatrix._11;
pRightPlane.n.y = _mMatrix._24 - _mMatrix._21;
pRightPlane.n.z = _mMatrix._34 - _mMatrix._31;
pRightPlane.dist = -(_mMatrix._44 - _mMatrix._41);


And the associated way to compare points:
	// Determine on which side of the plane is given point is.
CPlane3::PLANE_INTERSECT LSM_CALL CIntersection::ClassifyPoint( const CPlane3 &_pPlane, const CVector3 &_vPoint ) {
FXREAL fDist = _pPlane.n.Dot( _vPoint ) - _pPlane.dist;
if ( fDist > FX_PLANE_THICKNESS ) { return CPlane3::PI_FRONT; }
if ( fDist < -FX_PLANE_THICKNESS ) { return CPlane3::PI_BACK; }
return CPlane3::PI_COPLANAR;
}


#3:
Extending off the above, you can see that it is insufficient to simply return true or false; there are 3 possible cases, and for numerical robustness each must be correctly handled.

#4:
You have already stressed how performance-demanding your routine needs to be. Fixing your plane (and vector) normalization yields the following:
    f32 mag = plane->a * plane->a + plane->b * plane->b + plane->c * plane->c;
if(mag)
{
mag = 1.0f / sqrt( mag );
plane->a *= mag;
plane->b *= mag;
plane->c *= mag;
plane->d *= mag;
}

Never divide multiple numbers by the same denominator. Get the reciprocal via one divide and then multiply your values by that number.

Storing plane distances as negatives is another optimization, and one assumed by many textbooks (which means the formulas they give will not work if your plane is not negated).


#5:
As already mentioned, your new version of isVector3dfInFrustum() uses only the BOTTOM plane.
Surely this is not the code you are using now, but I am covering every ground here.


L. Spiro

Share this post


Link to post
Share on other sites
Thank you YogurtEmperor, your post was very helpful. I had forgoten that division is slower than multiplication. I have re-written my frustum code as follows:

#define calculateFrustumPlane(a, b, c) p->d[0] = clip.m[a + 0 * 4] b clip.m[c + 0 * 4]; p->d[1] = clip.m[a + 1 * 4] b clip.m[c + 1 * 4]; p->d[2] = clip.m[a + 2 * 4] b clip.m[c + 2 * 4]; p->d[3] = -(clip.m[a + 3 * 4] b clip.m[c + 3 * 4]); normalizePlane(p);

FASTCALL void updateFrustum()
{
static struct Matrix4x4 proj, modl, clip;
static struct Plane * p;

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

glPopMatrix();

proj = getTransposedMatrix4x4(proj);
modl = getTransposedMatrix4x4(modl);

clip = multiplyMatrix4x4(proj, modl);

p = &frustumPlane[RIGHT];
calculateFrustumPlane(3, -, 0);

p = &frustumPlane[LEFT];
calculateFrustumPlane(3, +, 0);

p = &frustumPlane[BOTTOM];
calculateFrustumPlane(3, +, 1);

p = &frustumPlane[TOP];
calculateFrustumPlane(3, -, 1);

p = &frustumPlane[BACK];
calculateFrustumPlane(3, -, 2);

p = &frustumPlane[FRONT];
calculateFrustumPlane(3, +, 2);
}

// because I am only doing point based culling (no bbox, or bsphere) if a point is on a frustum plane it would not be seen anyways, so just return false.
FASTCALL bool isVector3dfInFrustum(struct vector3df d)
{
static u8 i;
for(i = 0; i < 6; i++)
if(frustumPlane[i].d[0] * d.x + frustumPlane[i].d[1] * d.y + frustumPlane[i].d[2] * d.z - frustumPlane[i].d[3] <= 0)
return false;

return true;
}

FASTCALL struct Matrix4x4 getTransposedMatrix4x4(struct Matrix4x4 a)
{
struct Matrix4x4 b;

b.m[0] = a.m[0];
b.m[1] = a.m[4];
b.m[2] = a.m[8];
b.m[3] = a.m[12];

b.m[4] = a.m[1];
b.m[5] = a.m[5];
b.m[6] = a.m[9];
b.m[7] = a.m[13];

b.m[8] = a.m[2];
b.m[9] = a.m[6];
b.m[10] = a.m[10];
b.m[11] = a.m[14];

b.m[12] = a.m[3];
b.m[13] = a.m[7];
b.m[14] = a.m[11];
b.m[15] = a.m[15];

return b;
}



It is now clipping some things, but not others. Moving the camera down the X-axis culls points in the distance down the Z-axis, rather than on the X-axis.

Share this post


Link to post
Share on other sites
You should swap the order of the operands in multiplyMatrix4x4() and transpose the clip matrix again, or modify the multiplyMatrix4x4()
function by indexing it like I did it in my first reply as YogurtEmperor told about matrix indexing.
So you don't have to transpose before or after the multiplication.

And I think you should invert the modelview matrix before the operations:

glMatrixMode(GL_MODELVIEW);

f32 inv_modl[16];

glPushMatrix(); //these are totally useless here

glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

glPopMatrix(); //these are totally useless here

invert(&modl.m[0], inv_modl);
//inv_modl = getInvertMatrix4x4(modl);

clip = multiplyMatrix4x4(proj, inv_modl);//the modified func


.....

I had the same problem as I mentioned before, and that was the solution.

Invertion code

/* | a1 a2 |
| b1 b2 | calculate the determinent of a 2x2 matrix*/
double det2x2(const double a1, const double a2,
const double b1, const double b2)
{
return a1*b2 - b1*a2;
}

/* | a1 a2 a3 |
| b1 b2 b3 |
| c1 c2 c3 | calculate the determinent of a 3x3 matrix*/
double det3x3(const double a1, const double a2, const double a3,
const double b1, const double b2, const double b3,
const double c1, const double c2, const double c3)
{
return a1*det2x2(b2,b3,c2,c3) - b1*det2x2(a2,a3,c2,c3) +
c1*det2x2(a2,a3,b2,b3);
}

void invert(float *output, float *i)
{
double a11 = det3x3(i[5],i[6],i[7],i[9],i[10],i[11],i[13],i[14],i[15]);
double a21 = -det3x3(i[1],i[2],i[3],i[9],i[10],i[11],i[13],i[14],i[15]);
double a31 = det3x3(i[1],i[2],i[3],i[5],i[6],i[7],i[13],i[14],i[15]);
double a41 = -det3x3(i[1],i[2],i[3],i[5],i[6],i[7],i[9],i[10],i[11]);

double a12 = -det3x3(i[4],i[6],i[7],i[8],i[10],i[11],i[12],i[14],i[15]);
double a22 = det3x3(i[0],i[2],i[3],i[8],i[10],i[11],i[12],i[14],i[15]);
double a32 = -det3x3(i[0],i[2],i[3],i[4],i[6],i[7],i[12],i[14],i[15]);
double a42 = det3x3(i[0],i[2],i[3],i[4],i[6],i[7],i[8],i[10],i[11]);

double a13 = det3x3(i[4],i[5],i[7],i[8],i[9],i[11],i[12],i[13],i[15]);
double a23 = -det3x3(i[0],i[1],i[3],i[8],i[9],i[11],i[12],i[13],i[15]);
double a33 = det3x3(i[0],i[1],i[3],i[4],i[5],i[7],i[12],i[13],i[15]);
double a43 = -det3x3(i[0],i[1],i[3],i[4],i[5],i[7],i[8],i[9],i[11]);

double a14 = -det3x3(i[4],i[5],i[6],i[8],i[9],i[10],i[12],i[13],i[14]);
double a24 = det3x3(i[0],i[1],i[2],i[8],i[9],i[10],i[12],i[13],i[14]);
double a34 = -det3x3(i[0],i[1],i[2],i[4],i[5],i[6],i[12],i[13],i[14]);
double a44 = det3x3(i[0],i[1],i[2],i[4],i[5],i[6],i[8],i[9],i[10]);

double det = (i[0]*a11) + (i[4]*a21) + (i[8]*a31) + (i[12]*a41);
double oodet = 1/det;

output[ 0] = a11*oodet;
output[ 1] = a21*oodet;
output[ 2] = a31*oodet;
output[ 3] = a41*oodet;

output[ 4] = a12*oodet;
output[ 5] = a22*oodet;
output[ 6] = a32*oodet;
output[ 7] = a42*oodet;

output[ 8] = a13*oodet;
output[ 9] = a23*oodet;
output[10] = a33*oodet;
output[11] = a43*oodet;

output[12] = a14*oodet;
output[13] = a24*oodet;
output[14] = a34*oodet;
output[15] = a44*oodet;
}



and another thing: you should pass the matrices to the functions with pointers, not the whole matrices because the CPU has to copy those matrices. It's easy to modify, a few minutes work.

I hope that helps you.

Share this post


Link to post
Share on other sites
Thank you for your help. I have re-written my code as recommended, but now nothing is drawn.

Here is the updated version:

#define calculateFrustumPlane(a, b, c) p->d[0] = clip.m[a + 0 * 4] b clip.m[c + 0 * 4]; p->d[1] = clip.m[a + 1 * 4] b clip.m[c + 1 * 4]; p->d[2] = clip.m[a + 2 * 4] b clip.m[c + 2 * 4]; p->d[3] = -(clip.m[a + 3 * 4] b clip.m[c + 3 * 4]); normalizePlane(p);

FASTCALL void updateFrustum()
{
static struct Matrix4x4 proj, modl, clip;
static struct Plane * p;

glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

modl = getInvertedMatrix4x4(modl);

clip = multiplyMatrix4x4(proj, modl);

p = &frustumPlane[RIGHT];
calculateFrustumPlane(3, -, 0);

p = &frustumPlane[LEFT];
calculateFrustumPlane(3, +, 0);

p = &frustumPlane[BOTTOM];
calculateFrustumPlane(3, +, 1);

p = &frustumPlane[TOP];
calculateFrustumPlane(3, -, 1);

p = &frustumPlane[BACK];
calculateFrustumPlane(3, -, 2);

p = &frustumPlane[FRONT];
calculateFrustumPlane(3, +, 2);
}

// make opengl do he matrix multiplication
FASTCALL struct Matrix4x4 multiplyMatrix4x4(struct Matrix4x4 a, struct Matrix4x4 b)
{
struct Matrix4x4 c;

glPushMatrix();

glLoadMatrixf(&b.m[0]);
glMultMatrixf(&a.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &c.m[0]);

glPopMatrix();

return c;
}

/** thanks to szecs frome GameDev.net **/
FASTCALL f32 det2x2(f32 a1, f32 a2, f32 b1, f32 b2)
{
return a1 * b2 - b1 * a2;
}

/** thanks to szecs frome GameDev.net **/
FASTCALL f32 det3x3(f32 a1, f32 a2, f32 a3, f32 b1, f32 b2, f32 b3, f32 c1, f32 c2, f32 c3)
{
return a1 * det2x2(b2, b3, c2, c3) - b1 * det2x2(a2, a3, c2, c3) + c1 * det2x2(a2, a3, b2, b3);
}

/** thanks to szecs frome GameDev.net **/
FASTCALL struct Matrix4x4 getInvertedMatrix4x4(struct Matrix4x4 matrix)
{
struct Matrix4x4 ret;

f32 a11 = det3x3(matrix.m[5], matrix.m[6], matrix.m[7], matrix.m[9], matrix.m[10], matrix.m[11], matrix.m[13], matrix.m[14], matrix.m[15]);
f32 a21 = -det3x3(matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[9], matrix.m[10], matrix.m[11], matrix.m[13], matrix.m[14], matrix.m[15]);
f32 a31 = det3x3(matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[5], matrix.m[6], matrix.m[7], matrix.m[13], matrix.m[14], matrix.m[15]);
f32 a41 = -det3x3(matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[5], matrix.m[6], matrix.m[7], matrix.m[9], matrix.m[10], matrix.m[11]);

f32 a12 = -det3x3(matrix.m[4], matrix.m[6], matrix.m[7], matrix.m[8], matrix.m[10], matrix.m[11], matrix.m[12], matrix.m[14], matrix.m[15]);
f32 a22 = det3x3(matrix.m[0], matrix.m[2], matrix.m[3], matrix.m[8], matrix.m[10], matrix.m[11], matrix.m[12], matrix.m[14], matrix.m[15]);
f32 a32 = -det3x3(matrix.m[0], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[6], matrix.m[7], matrix.m[12], matrix.m[14], matrix.m[15]);
f32 a42 = det3x3(matrix.m[0], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[6], matrix.m[7], matrix.m[8], matrix.m[10], matrix.m[11]);

f32 a13 = det3x3(matrix.m[4], matrix.m[5], matrix.m[7], matrix.m[8], matrix.m[9], matrix.m[11], matrix.m[12], matrix.m[13], matrix.m[15]);
f32 a23 = -det3x3(matrix.m[0], matrix.m[1], matrix.m[3], matrix.m[8], matrix.m[9], matrix.m[11], matrix.m[12], matrix.m[13], matrix.m[15]);
f32 a33 = det3x3(matrix.m[0], matrix.m[1], matrix.m[3], matrix.m[4], matrix.m[5], matrix.m[7], matrix.m[12], matrix.m[13], matrix.m[15]);
f32 a43 = -det3x3(matrix.m[0], matrix.m[1], matrix.m[3], matrix.m[4], matrix.m[5], matrix.m[7], matrix.m[8], matrix.m[9], matrix.m[11]);

f32 a14 = -det3x3(matrix.m[4], matrix.m[5], matrix.m[6], matrix.m[8], matrix.m[9], matrix.m[10], matrix.m[12], matrix.m[13], matrix.m[14]);
f32 a24 = det3x3(matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[8], matrix.m[9], matrix.m[10], matrix.m[12], matrix.m[13], matrix.m[14]);
f32 a34 = -det3x3(matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[4], matrix.m[5], matrix.m[6], matrix.m[12], matrix.m[13], matrix.m[14]);
f32 a44 = det3x3(matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[4], matrix.m[5], matrix.m[6], matrix.m[8], matrix.m[9], matrix.m[10]);

f32 det = (matrix.m[0] * a11) + (matrix.m[4] * a21) + (matrix.m[8] * a31) + (matrix.m[12] * a41);
f32 oodet = 1.0f / det;

ret.m[0] = a11 * oodet;
ret.m[1] = a21 * oodet;
ret.m[2] = a31 * oodet;
ret.m[3] = a41 * oodet;

ret.m[4] = a12 * oodet;
ret.m[5] = a22 * oodet;
ret.m[6] = a32 * oodet;
ret.m[7] = a42 * oodet;

ret.m[8] = a13 * oodet;
ret.m[9] = a23 * oodet;
ret.m[10] = a33 * oodet;
ret.m[11] = a43 * oodet;

ret.m[12] = a14 * oodet;
ret.m[13] = a24 * oodet;
ret.m[14] = a34 * oodet;
ret.m[15] = a44 * oodet;

return ret;
}

OpenGL should be multiplying the matrices correctly, and I am inverting the model view matrix as instructed.

Any ideas why its not working?

Share this post


Link to post
Share on other sites
Try this, if it isn't working, I won't reply any more:



static struct Matrix4x4 proj, modl, clip;

static struct Plane * p;

glGetFloatv(GL_PROJECTION_MATRIX, &proj.m[0]);
glGetFloatv(GL_MODELVIEW_MATRIX, &modl.m[0]);

clip = multiplyMatrix4x4(proj, modl);

clip = getInvertedMatrix4x4(clip);

....



It's based on a shadow mapping code I downloaded.
And try to use own multiplication code with correct indexing (as mentioned).

Share this post


Link to post
Share on other sites
With using your matrix multiplication code and inverting the clip matrix **everything** (including off-screen objects) is now rendered.


clip = multiplyMatrix4x4(proj, modl);

clip = getInvertedMatrix4x4(clip);



/** thanks to szecs at GameDev.net **/
FASTCALL struct Matrix4x4 multiplyMatrix4x4(struct Matrix4x4 a, struct Matrix4x4 b)
{
struct Matrix4x4 c;

c.m[0] = a.m[0] * b.m[0] + a.m[4] * b.m[1] + a.m[8] * b.m[2] + a.m[12] * b.m[3];
c.m[1] = a.m[1] * b.m[0] + a.m[5] * b.m[1] + a.m[9] * b.m[2] + a.m[13] * b.m[3];
c.m[2] = a.m[2] * b.m[0] + a.m[6] * b.m[1] + a.m[10] * b.m[2] + a.m[14] * b.m[3];
c.m[3] = a.m[3] * b.m[0] + a.m[7] * b.m[1] + a.m[11] * b.m[2] + a.m[15] * b.m[3];
c.m[4] = a.m[0] * b.m[4] + a.m[4] * b.m[5] + a.m[8] * b.m[6] + a.m[12] * b.m[7];
c.m[5] = a.m[1] * b.m[4] + a.m[5] * b.m[5] + a.m[9] * b.m[6] + a.m[13] * b.m[7];
c.m[6] = a.m[2] * b.m[4] + a.m[6] * b.m[5] + a.m[10] * b.m[6] + a.m[14] * b.m[7];
c.m[7] = a.m[3] * b.m[4] + a.m[7] * b.m[5] + a.m[11] * b.m[6] + a.m[15] * b.m[7];
c.m[8] = a.m[0] * b.m[8] + a.m[4] * b.m[9] + a.m[8] * b.m[10] + a.m[12] * b.m[11];
c.m[9] = a.m[1] * b.m[8] + a.m[5] * b.m[9] + a.m[9] * b.m[10] + a.m[13] * b.m[11];
c.m[10] = a.m[2] * b.m[8] + a.m[6] * b.m[9] + a.m[10] * b.m[10] + a.m[14] * b.m[11];
c.m[11] = a.m[3] * b.m[8] + a.m[7] * b.m[9] + a.m[11] * b.m[10] + a.m[15] * b.m[11];
c.m[12] = a.m[0] * b.m[12] + a.m[4] * b.m[13] + a.m[8] * b.m[14] + a.m[12] * b.m[15];
c.m[13] = a.m[1] * b.m[12] + a.m[5] * b.m[13] + a.m[9] * b.m[14] + a.m[13] * b.m[15];
c.m[14] = a.m[2] * b.m[12] + a.m[6] * b.m[13] + a.m[10] * b.m[14] + a.m[14] * b.m[15];
c.m[15] = a.m[3] * b.m[12] + a.m[7] * b.m[13] + a.m[11] * b.m[14] + a.m[15] * b.m[15];
}


*sigh*. Any ideas?

Share this post


Link to post
Share on other sites
Turns out the matrix multiplication code is the problem, still working to fix it however.

The clip matrix is being returned as the following values:

{{1.#QNAN0, 1.#QNAN0, 1.#QNAN0, 1.#QNAN0}
{1.#QNAN0, 1.#QNAN0, 1.#QNAN0, 1.#QNAN0}
{1.#QNAN0, 1.#QNAN0, 1.#QNAN0, 1.#QNAN0}
{1.#QNAN0, 1.#QNAN0, 1.#QNAN0, 1.#QNAN0}}


I have since re-written the matrix multiplication code:

FASTCALL struct Matrix4x4 multiplyMatrix4x4(struct Matrix4x4 a, struct Matrix4x4 b)
{
struct Matrix4x4 c;
s32 i, j, k;

for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
for (k = 0; k < 4; k++)
c.m[i * 4 + j] += a.m[i * 4 + k] * b.m[k * 4 + j];

return c;
}


This new multiplyMatrix4x4() function does not give weird values.

Now the inverted clip matrix is being returned as the following values:

{{0.000000, -1.#IND00, 0.000000, 0.000000}
{0.000000, -1.#IND00, 0.000000, 0.000000}
{0.000000, 0.000000, 0.000000, 0.000000}
{-1.#IND00, -1.#IND00, 0.000000, -1.#IND00}}


Any ideas?

Share this post


Link to post
Share on other sites
Just a quick comment on your matrix multiplication code: you're not zeroing out c before you perform the multiplication, which means that the result may or may not be correct. (This may not be causing you any problems currently - for example, it may be that the memory just happens to be zeroed already for whatever reason - but it's something that should be fixed nonetheless.)

Share this post


Link to post
Share on other sites
I don't know. I thought functions cannot return arrays, just single values or pointers.
So he uses arrays that's really just pointers, that had been deallocated after returning from the functions. But it worked in some parts of his code for some reason, so I don't know.
Should use output params as pointers in the param. list. (void functions of course)

Share this post


Link to post
Share on other sites

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  

  • Forum Statistics

    • Total Topics
      628275
    • Total Posts
      2981740
  • Similar Content

    • By mellinoe
      Hi all,
      First time poster here, although I've been reading posts here for quite a while. This place has been invaluable for learning graphics programming -- thanks for a great resource!
      Right now, I'm working on a graphics abstraction layer for .NET which supports D3D11, Vulkan, and OpenGL at the moment. I have implemented most of my planned features already, and things are working well. Some remaining features that I am planning are Compute Shaders, and some flavor of read-write shader resources. At the moment, my shaders can just get simple read-only access to a uniform (or constant) buffer, a texture, or a sampler. Unfortunately, I'm having a tough time grasping the distinctions between all of the different kinds of read-write resources that are available. In D3D alone, there seem to be 5 or 6 different kinds of resources with similar but different characteristics. On top of that, I get the impression that some of them are more or less "obsoleted" by the newer kinds, and don't have much of a place in modern code. There seem to be a few pivots:
      The data source/destination (buffer or texture) Read-write or read-only Structured or unstructured (?) Ordered vs unordered (?) These are just my observations based on a lot of MSDN and OpenGL doc reading. For my library, I'm not interested in exposing every possibility to the user -- just trying to find a good "middle-ground" that can be represented cleanly across API's which is good enough for common scenarios.
      Can anyone give a sort of "overview" of the different options, and perhaps compare/contrast the concepts between Direct3D, OpenGL, and Vulkan? I'd also be very interested in hearing how other folks have abstracted these concepts in their libraries.
    • By aejt
      I recently started getting into graphics programming (2nd try, first try was many years ago) and I'm working on a 3d rendering engine which I hope to be able to make a 3D game with sooner or later. I have plenty of C++ experience, but not a lot when it comes to graphics, and while it's definitely going much better this time, I'm having trouble figuring out how assets are usually handled by engines.
      I'm not having trouble with handling the GPU resources, but more so with how the resources should be defined and used in the system (materials, models, etc).
      This is my plan now, I've implemented most of it except for the XML parts and factories and those are the ones I'm not sure of at all:
      I have these classes:
      For GPU resources:
      Geometry: holds and manages everything needed to render a geometry: VAO, VBO, EBO. Texture: holds and manages a texture which is loaded into the GPU. Shader: holds and manages a shader which is loaded into the GPU. For assets relying on GPU resources:
      Material: holds a shader resource, multiple texture resources, as well as uniform settings. Mesh: holds a geometry and a material. Model: holds multiple meshes, possibly in a tree structure to more easily support skinning later on? For handling GPU resources:
      ResourceCache<T>: T can be any resource loaded into the GPU. It owns these resources and only hands out handles to them on request (currently string identifiers are used when requesting handles, but all resources are stored in a vector and each handle only contains resource's index in that vector) Resource<T>: The handles given out from ResourceCache. The handles are reference counted and to get the underlying resource you simply deference like with pointers (*handle).  
      And my plan is to define everything into these XML documents to abstract away files:
      Resources.xml for ref-counted GPU resources (geometry, shaders, textures) Resources are assigned names/ids and resource files, and possibly some attributes (what vertex attributes does this geometry have? what vertex attributes does this shader expect? what uniforms does this shader use? and so on) Are reference counted using ResourceCache<T> Assets.xml for assets using the GPU resources (materials, meshes, models) Assets are not reference counted, but they hold handles to ref-counted resources. References the resources defined in Resources.xml by names/ids. The XMLs are loaded into some structure in memory which is then used for loading the resources/assets using factory classes:
      Factory classes for resources:
      For example, a texture factory could contain the texture definitions from the XML containing data about textures in the game, as well as a cache containing all loaded textures. This means it has mappings from each name/id to a file and when asked to load a texture with a name/id, it can look up its path and use a "BinaryLoader" to either load the file and create the resource directly, or asynchronously load the file's data into a queue which then can be read from later to create the resources synchronously in the GL context. These factories only return handles.
      Factory classes for assets:
      Much like for resources, these classes contain the definitions for the assets they can load. For example, with the definition the MaterialFactory will know which shader, textures and possibly uniform a certain material has, and with the help of TextureFactory and ShaderFactory, it can retrieve handles to the resources it needs (Shader + Textures), setup itself from XML data (uniform values), and return a created instance of requested material. These factories return actual instances, not handles (but the instances contain handles).
       
       
      Is this a good or commonly used approach? Is this going to bite me in the ass later on? Are there other more preferable approaches? Is this outside of the scope of a 3d renderer and should be on the engine side? I'd love to receive and kind of advice or suggestions!
      Thanks!
    • By nedondev
      I 'm learning how to create game by using opengl with c/c++ coding, so here is my fist game. In video description also have game contain in Dropbox. May be I will make it better in future.
      Thanks.
    • By Abecederia
      So I've recently started learning some GLSL and now I'm toying with a POM shader. I'm trying to optimize it and notice that it starts having issues at high texture sizes, especially with self-shadowing.
      Now I know POM is expensive either way, but would pulling the heightmap out of the normalmap alpha channel and in it's own 8bit texture make doing all those dozens of texture fetches more cheap? Or is everything in the cache aligned to 32bit anyway? I haven't implemented texture compression yet, I think that would help? But regardless, should there be a performance boost from decoupling the heightmap? I could also keep it in a lower resolution than the normalmap if that would improve performance.
      Any help is much appreciated, please keep in mind I'm somewhat of a newbie. Thanks!
    • By test opty
      Hi,
      I'm trying to learn OpenGL through a website and have proceeded until this page of it. The output is a simple triangle. The problem is the complexity.
      I have read that page several times and tried to analyse the code but I haven't understood the code properly and completely yet. This is the code:
       
      #include <glad/glad.h> #include <GLFW/glfw3.h> #include <C:\Users\Abbasi\Desktop\std_lib_facilities_4.h> using namespace std; //****************************************************************************** void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow *window); // settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; //******************************* int main() { // glfw: initialize and configure // ------------------------------ glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // glfw window creation GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "My First Triangle", nullptr, nullptr); if (window == nullptr) { cout << "Failed to create GLFW window" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // glad: load all OpenGL function pointers if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "Failed to initialize GLAD" << endl; return -1; } // build and compile our shader program // vertex shader int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); glCompileShader(vertexShader); // check for shader compile errors int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl; } // fragment shader int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); glCompileShader(fragmentShader); // check for shader compile errors glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl; } // link shaders int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); // check for linking errors glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog); cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // set up vertex data (and buffer(s)) and configure vertex attributes float vertices[] = { -0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // bind the Vertex Array Object first, then bind and set vertex buffer(s), //and then configure vertex attributes(s). glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // note that this is allowed, the call to glVertexAttribPointer registered VBO // as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind glBindBuffer(GL_ARRAY_BUFFER, 0); // You can unbind the VAO afterwards so other VAO calls won't accidentally // modify this VAO, but this rarely happens. Modifying other // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind // VAOs (nor VBOs) when it's not directly necessary. glBindVertexArray(0); // uncomment this call to draw in wireframe polygons. //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render loop while (!glfwWindowShouldClose(window)) { // input // ----- processInput(window); // render // ------ glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // draw our first triangle glUseProgram(shaderProgram); glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to // bind it every time, but we'll do so to keep things a bit more organized glDrawArrays(GL_TRIANGLES, 0, 3); // glBindVertexArray(0); // no need to unbind it every time // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) glfwSwapBuffers(window); glfwPollEvents(); } // optional: de-allocate all resources once they've outlived their purpose: glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); // glfw: terminate, clearing all previously allocated GLFW resources. glfwTerminate(); return 0; } //************************************************** // process all input: query GLFW whether relevant keys are pressed/released // this frame and react accordingly void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } //******************************************************************** // glfw: whenever the window size changed (by OS or user resize) this callback function executes void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); } As you see, about 200 lines of complicated code only for a simple triangle. 
      I don't know what parts are necessary for that output. And also, what the correct order of instructions for such an output or programs is, generally. That start point is too complex for a beginner of OpenGL like me and I don't know how to make the issue solved. What are your ideas please? What is the way to figure both the code and the whole program out correctly please?
      I wish I'd read a reference that would teach me OpenGL through a step-by-step method. 
  • Popular Now