mamannon

02-29-2016, 05:42 AM

Hello everybody,

I'm coding a tiled forward rendering system without compute shader, because I use OpenGL 3.3. That's why I need to calculate the frustums struct on the CPU side and I load it to the GPU memory as a uniform buffer object. That's not a speed problem, because this is done only on program start: it includes only X and Y coordinates ie. frustums' sides. However, the Z coordinate, ie the frustums' depths, may become a bottleneck because they are continuously stored in CPU memory, where seeking deepest values are done, and then loaded back to GPU memory. But I hope using glReadPixels with streaming double pixel buffer objects is fast enough. Actually those frustums needs two depths for lightning, back and front, but I'm working only back side now, because that's enough for oclusion testing with bounding boxes.

Okay, that was introduction... The problem is that I cannot get OpenGL view frustum and outer bounds of tiled rendering frustums grid equal. I show relevant samples of my code, then descibe what's happening. My Frustum projection:

//define local variables

float windowRatio=(float)widthI/(float)heightI;

float angle=60;

float near=0.5;

float far=20000;

float top=near*tan(PiiI/180*angle/2);

float bottom=-top;

float right=top*windowRatio;

float left=-right;

//this is OpenGL PROJECTION matrix

projektioI[0]=near/right;

projektioI[1]=0;

projektioI[2]=0;

projektioI[3]=0;

projektioI[4]=0;

projektioI[5]=near/top;

projektioI[6]=0;

projektioI[7]=0;

projektioI[8]=0;

projektioI[9]=0;

projektioI[10]=-1*(far+near)/(far-near);

projektioI[11]=-1;

projektioI[12]=0;

projektioI[13]=0;

projektioI[14]=-2*(far*near)/(far-near);

projektioI[15]=0;

Tiled rendering frustums grid is a collection of Y and X aligned plane equations of form (a*x+b*y+c*z+d=0). I put here only the outmost ones. Every plane is defined with three points, one of them is always origo. The other two are corner points of the near plane of the OpenGL view frustum.

float origo[3]={0, 0, 0};

//Right plane, two points:

float rightTop[3]={0, 0, -near};

rightTop[1]=near*tan(PiiI/180*angle/2);

rightTop[0]=rightTop[1]*windowRatio;

float rightBottom[3]={rightTop[0], -rightTop[1], -near};

//left plane, two points:

float leftTop[3]={-rightTop[0], rightTop[1], -near};

float leftBottom[3]={-rightTop[0], -rightTop[1], -near};

//top plane, one point (another one already/still defined):

leftTop[0]=-rightTop[0];

leftTop[1]=rightTop[1];

leftTop[2]=-near;

//bottom plane, one point (another one already/still defined):

leftBottom[0]=-rightTop[0];

leftBottom[1]=-rightTop[1];

leftBottom[2]=-near;

Below is a function I use to calculate plane equations:

void Render::calculatePlaneEquations(float* tasoYhtalo, const int& index, const float* origo, const float* vektori1, const float* vektori2) {

//let's calculate cross product V1xV2

float v1[3]={origo[0]-vektori1[0], origo[1]-vektori1[1], origo[2]-vektori1[2]};

float v2[3]={vektori2[0]-origo[0], vektori2[1]-origo[1], vektori2[2]-origo[2]};

float N[3]={v1[1]*v2[2]-v1[2]*v2[1], -v1[0]*v2[2]+v1[2]*v2[0], v1[0]*v2[1]-v1[1]*v2[0]};

//normalize the normal vector

float siirto=sqrt(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]);

N[0]=N[0]/siirto;

N[1]=N[1]/siirto;

N[2]=N[2]/siirto;

//let's calculate the coefficient d of the equation ax+by+cz+d=0

siirto=N[0]*origo[0]+N[1]*origo[1]+N[2]*origo[2];

//return the coefficients a, b, c, and d of ax+by+cz+d=0

tasoYhtalo[4*index]=N[0];

tasoYhtalo[4*index+1]=N[1];

tasoYhtalo[4*index+2]=N[2];

tasoYhtalo[4*index+3]=siirto;

}

The planes we've got by calculatePlaneEquations are used in the geometry shader by function like below (there are two similar functions, one for X coord and another for Y coord. This is for X coord):

//these are from main fuction. I've tried these both in vertex shader and geometry shader, this time it's geometry shader

"vec4 verteksi=gl_in[i].gl_Position;\n"

"verteksi=modelViewProjection*verteksi;\n"

"verteksi.y=-1.0*verteksi.y;\n" //this negation is needed, or the depth picture is upside down

"verteksi.x=-1.0*verteksi.x;\n" //I'm not sure is this negation correct...

//this is the geometry shader function

"void seekHorizontalTile(in float pystyTaso[4*(MAKSIMIRIVI/BLOCK_SIZE+2)], in vec4 verteksi, inout int x) {\n"

"int tila=0;\n"

"do {\n"

//this is the place we use plane equations

"float suunta=pystyTaso[4*x]*verteksi.x+pystyTaso[4*x+1]*verteksi.y+pystyTaso[4*x+2]*verteksi.z+pystyTaso[4*x+3];\n"

"if (tila==0 && x<=tiiliaX) {\n"

"if (suunta>0) {\n"

"x=x+(tiiliaX-x+2)/2;\n"

"} else {\n"

"tila=1;\n"

"}\n"

"} else {\n"

"tila=1;\n"

"}\n"

"if (tila==1 && x>=0) {\n"

"if (suunta<0) {\n"

"x=x-1;\n"

"} else {\n"

"tila=2;\n"

"}\n"

"} else {\n"

"if (tila!=0) {\n"

"tila=2;\n"

"}\n"

"}\n"

"} while (tila<2);\n"

"}\n"

So, everyting seems to be fine, except that objects are rendered only on the center of window, not upper, lower, left or right area of window. The culling boudary is very sharp and it is always symmetric with window boudaries. I first figured that there is a bug somewhere in depth code... But that's not the case, the problem persist if I remove the oclusion testing. It turns out that there is a scaling problem.

I got an idea to use a different view angle between OpenGL view frustum and tiled rendering frustums grid. I found that if I use

atan(1/sqrt(3))=30 degrees with Z value of sqrt(3)

60 degrees view angle for OpenGL view frustum and

atan(1/sqrt(3)/2)=49.106605 degrees with Z value of sqrt(3)/2

98.213210 degrees view angle for tiled rendering frustums grid, I got an exact match with culling boundary and window boundaries. However, now oclusion test fails: this is a natural consequence, because the tiles are now stretched outside the area their depth data is measured for. This is a confusing result and I'm hoping I have done some stupid mistake or there is some way I can fix this problem.

I hope somebody can give me advice or at least give me answers to the following questions:

1. As far as I know, the vertex shader built-in out parameter gl_Position and geometry shader built-in in parameter gl_in[index].gl_Position have exactly the same values in all their four components, x, y, z and w. OpenGL won't do any changes between the shaders. Is this true?

2. I suppose, that somewhere between geometry shader and fragment shader OpenGL does all conversions:

-x, y, z are divided by w

-z coordinate gets squeezed from -1 to 1 into 0 to 1; this also delinearizes z coordinate (though I have a feeling this is done before geometry shader...)

-z coordinate negation (the change from right handed coordinate system into the left handed coordinate system)

-x and y coordinates are changed from -1 to 1 range into the framebuffer pixel range: from 0 to the number of pixels on axis

3. Between a fragment shader and the final framebuffer there are no coordinate conversions. Is this true?

Please help me :dejection:

I'm coding a tiled forward rendering system without compute shader, because I use OpenGL 3.3. That's why I need to calculate the frustums struct on the CPU side and I load it to the GPU memory as a uniform buffer object. That's not a speed problem, because this is done only on program start: it includes only X and Y coordinates ie. frustums' sides. However, the Z coordinate, ie the frustums' depths, may become a bottleneck because they are continuously stored in CPU memory, where seeking deepest values are done, and then loaded back to GPU memory. But I hope using glReadPixels with streaming double pixel buffer objects is fast enough. Actually those frustums needs two depths for lightning, back and front, but I'm working only back side now, because that's enough for oclusion testing with bounding boxes.

Okay, that was introduction... The problem is that I cannot get OpenGL view frustum and outer bounds of tiled rendering frustums grid equal. I show relevant samples of my code, then descibe what's happening. My Frustum projection:

//define local variables

float windowRatio=(float)widthI/(float)heightI;

float angle=60;

float near=0.5;

float far=20000;

float top=near*tan(PiiI/180*angle/2);

float bottom=-top;

float right=top*windowRatio;

float left=-right;

//this is OpenGL PROJECTION matrix

projektioI[0]=near/right;

projektioI[1]=0;

projektioI[2]=0;

projektioI[3]=0;

projektioI[4]=0;

projektioI[5]=near/top;

projektioI[6]=0;

projektioI[7]=0;

projektioI[8]=0;

projektioI[9]=0;

projektioI[10]=-1*(far+near)/(far-near);

projektioI[11]=-1;

projektioI[12]=0;

projektioI[13]=0;

projektioI[14]=-2*(far*near)/(far-near);

projektioI[15]=0;

Tiled rendering frustums grid is a collection of Y and X aligned plane equations of form (a*x+b*y+c*z+d=0). I put here only the outmost ones. Every plane is defined with three points, one of them is always origo. The other two are corner points of the near plane of the OpenGL view frustum.

float origo[3]={0, 0, 0};

//Right plane, two points:

float rightTop[3]={0, 0, -near};

rightTop[1]=near*tan(PiiI/180*angle/2);

rightTop[0]=rightTop[1]*windowRatio;

float rightBottom[3]={rightTop[0], -rightTop[1], -near};

//left plane, two points:

float leftTop[3]={-rightTop[0], rightTop[1], -near};

float leftBottom[3]={-rightTop[0], -rightTop[1], -near};

//top plane, one point (another one already/still defined):

leftTop[0]=-rightTop[0];

leftTop[1]=rightTop[1];

leftTop[2]=-near;

//bottom plane, one point (another one already/still defined):

leftBottom[0]=-rightTop[0];

leftBottom[1]=-rightTop[1];

leftBottom[2]=-near;

Below is a function I use to calculate plane equations:

void Render::calculatePlaneEquations(float* tasoYhtalo, const int& index, const float* origo, const float* vektori1, const float* vektori2) {

//let's calculate cross product V1xV2

float v1[3]={origo[0]-vektori1[0], origo[1]-vektori1[1], origo[2]-vektori1[2]};

float v2[3]={vektori2[0]-origo[0], vektori2[1]-origo[1], vektori2[2]-origo[2]};

float N[3]={v1[1]*v2[2]-v1[2]*v2[1], -v1[0]*v2[2]+v1[2]*v2[0], v1[0]*v2[1]-v1[1]*v2[0]};

//normalize the normal vector

float siirto=sqrt(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]);

N[0]=N[0]/siirto;

N[1]=N[1]/siirto;

N[2]=N[2]/siirto;

//let's calculate the coefficient d of the equation ax+by+cz+d=0

siirto=N[0]*origo[0]+N[1]*origo[1]+N[2]*origo[2];

//return the coefficients a, b, c, and d of ax+by+cz+d=0

tasoYhtalo[4*index]=N[0];

tasoYhtalo[4*index+1]=N[1];

tasoYhtalo[4*index+2]=N[2];

tasoYhtalo[4*index+3]=siirto;

}

The planes we've got by calculatePlaneEquations are used in the geometry shader by function like below (there are two similar functions, one for X coord and another for Y coord. This is for X coord):

//these are from main fuction. I've tried these both in vertex shader and geometry shader, this time it's geometry shader

"vec4 verteksi=gl_in[i].gl_Position;\n"

"verteksi=modelViewProjection*verteksi;\n"

"verteksi.y=-1.0*verteksi.y;\n" //this negation is needed, or the depth picture is upside down

"verteksi.x=-1.0*verteksi.x;\n" //I'm not sure is this negation correct...

//this is the geometry shader function

"void seekHorizontalTile(in float pystyTaso[4*(MAKSIMIRIVI/BLOCK_SIZE+2)], in vec4 verteksi, inout int x) {\n"

"int tila=0;\n"

"do {\n"

//this is the place we use plane equations

"float suunta=pystyTaso[4*x]*verteksi.x+pystyTaso[4*x+1]*verteksi.y+pystyTaso[4*x+2]*verteksi.z+pystyTaso[4*x+3];\n"

"if (tila==0 && x<=tiiliaX) {\n"

"if (suunta>0) {\n"

"x=x+(tiiliaX-x+2)/2;\n"

"} else {\n"

"tila=1;\n"

"}\n"

"} else {\n"

"tila=1;\n"

"}\n"

"if (tila==1 && x>=0) {\n"

"if (suunta<0) {\n"

"x=x-1;\n"

"} else {\n"

"tila=2;\n"

"}\n"

"} else {\n"

"if (tila!=0) {\n"

"tila=2;\n"

"}\n"

"}\n"

"} while (tila<2);\n"

"}\n"

So, everyting seems to be fine, except that objects are rendered only on the center of window, not upper, lower, left or right area of window. The culling boudary is very sharp and it is always symmetric with window boudaries. I first figured that there is a bug somewhere in depth code... But that's not the case, the problem persist if I remove the oclusion testing. It turns out that there is a scaling problem.

I got an idea to use a different view angle between OpenGL view frustum and tiled rendering frustums grid. I found that if I use

atan(1/sqrt(3))=30 degrees with Z value of sqrt(3)

60 degrees view angle for OpenGL view frustum and

atan(1/sqrt(3)/2)=49.106605 degrees with Z value of sqrt(3)/2

98.213210 degrees view angle for tiled rendering frustums grid, I got an exact match with culling boundary and window boundaries. However, now oclusion test fails: this is a natural consequence, because the tiles are now stretched outside the area their depth data is measured for. This is a confusing result and I'm hoping I have done some stupid mistake or there is some way I can fix this problem.

I hope somebody can give me advice or at least give me answers to the following questions:

1. As far as I know, the vertex shader built-in out parameter gl_Position and geometry shader built-in in parameter gl_in[index].gl_Position have exactly the same values in all their four components, x, y, z and w. OpenGL won't do any changes between the shaders. Is this true?

2. I suppose, that somewhere between geometry shader and fragment shader OpenGL does all conversions:

-x, y, z are divided by w

-z coordinate gets squeezed from -1 to 1 into 0 to 1; this also delinearizes z coordinate (though I have a feeling this is done before geometry shader...)

-z coordinate negation (the change from right handed coordinate system into the left handed coordinate system)

-x and y coordinates are changed from -1 to 1 range into the framebuffer pixel range: from 0 to the number of pixels on axis

3. Between a fragment shader and the final framebuffer there are no coordinate conversions. Is this true?

Please help me :dejection: