PDA

View Full Version : Transform feedback Shader Approach + Faceculling



ananass
11-04-2014, 01:59 AM
Greetings everyones,

i'm a beeginner in OpenGl, i've just finished some tutorials to know more about this library.

My objectif is retrieve the coordinates of vertices of the visible to the camera.

To do this, first of all, i tried simply to capture the coordinates of vertices of a renderrend cube via the vertex shader, this is the code:



// Shader sources
const GLchar* vertexSource =
"#version 150 core\n"
"in vec3 position;"
"in vec3 color;"

"out vec3 Color;"
"out vec3 outposition;"
"uniform mat4 model;"
"uniform mat4 view;"
"uniform mat4 proj;"

"void main() {"
"outposition=position;"
" Color = color;"

" gl_Position = proj * view * vec4(position, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 150 core\n"
"in vec3 Color;"

"out vec4 outColor;"


"void main() {"
" outColor = vec4(Color, 1.0) ;"
"}";

int main()
{
sf::ContextSettings settings;
settings.depthBits = 24;
settings.stencilBits = 8;

sf::Window window(sf::VideoMode(800, 600, 32), "OpenGL", sf::Style::Titlebar | sf::Style::Close, settings);

glEnable(GL_DEPTH_TEST);

// Initialize GLEW
glewExperimental = GL_TRUE;
glewInit();

// Create and compile the vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);

// Create and compile the fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);

// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");



const GLchar* feedbackVaryings[] = { "outposition" };
glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);

glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);

// Create Vertex Array Object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create a Vertex Buffer Object and copy the vertex data to it
GLuint vbo;
glGenBuffers(1, &vbo);


GLfloat vertices2[] = {
//Frontie red
-1.5f, -1.0f, 1.5f, 1.0f, 0.0f, 0.0f,
1.5f, -1.0f, 1.5f, 1.0f, 0.0f, 0.0f,
1.5f, 1.0f, 1.5f, 1.0f, 0.0f, 0.0f,
-1.5f, 1.0f, 1.5, 1.0f, 0.0f, 0.0f,

//Right vert

1.5f, -1.0f, -1.5f, 0.0f, 1.0f, 0.0f,
1.5f, 1.0f, -1.5f, 0.0f, 1.0f, 0.0f,
1.5f, 1.0f, 1.5f, 0.0f, 1.0f, 0.0f,
1.5f, -1.0f, 1.5f, 0.0f, 1.0f, 0.0f,

//Back bleu

-1.5f, -1.0f, -1.5f, 0.0f, 0.0f, 1.0f,
-1.5f, 1.0f, -1.5f, 0.0f, 0.0f, 1.0f,
1.5f, 1.0f, -1.5f, 0.0f, 0.0f, 1.0f,
1.5f, -1.0f, -1.5f, 0.0f, 0.0f, 1.0f,

//Left jaune

-1.5f, -1.0f, -1.5f, 1.0f, 1.0f, 0.0f,
-1.5f, -1.0f, 1.5f, 1.0f, 1.0f, 0.0f,
-1.5f, 1.0f, 1.5f, 1.0f, 1.0f, 0.0f,
-1.5f, 1.0f, -1.5f, 1.0f, 1.0f, 0.0f,
};

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);

// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);

GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));

GLint uniModel = glGetUniformLocation(shaderProgram, "model");

// Set up projection
glm::mat4 view = glm::lookAt(
glm::vec3(0.0f, 3.0f, 6.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f)
);
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));

glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
GLint uniProj = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));

// Create transform feedback buffer
GLuint tbo;
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), NULL, GL_STATIC_READ);

// Perform feedback transform
glEnable(GL_RASTERIZER_DISCARD);

glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);

glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, 48);
glEndTransformFeedback();

glDisable(GL_RASTERIZER_DISCARD);

glFlush();

// Fetch and print results
GLfloat feedback[48];
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
int k=0;
for(int i=0;i<48;i++)
{ cout<<feedback[i]<<" ";
k++;
if(k==3){cout<<endl; k=0;}
}

while (window.isOpen())
{
sf::Event windowEvent;
while (window.pollEvent(windowEvent))
{
switch (windowEvent.type)
{
case sf::Event::Closed:
window.close();
break;
}
}

// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Calculate transformation
glm::mat4 model;
model = glm::rotate(
model,
(GLfloat)clock() / (GLfloat)CLOCKS_PER_SEC * 180.0f,
glm::vec3(0.0f, 0.0f, 1.0f)
);

glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));

// Draw cube
glDrawArrays(GL_QUADS, 0, 48);

// Swap buffers
window.display();
}


glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);

glDeleteBuffers(1, &vbo);

glDeleteVertexArrays(1, &vao);
}



This code render me all the vetices-coordinates of the cube.

Now, i would perform the faceculling to have only the vertices visible to the camera.

How can add this to the code please?

Thank you

carsten neumann
11-04-2014, 02:43 AM
Since you are rendering points I'm assuming you want to find all vertices that are inside the camera frustum? Or did you want to get the vertices that would be visible if the faces of the object where drawn?
In the first case I think you'll need a geometry shader because you want to discard some vertices and the vertex shader has a strict 1-to-1 relationship between the number of vertices that go in and those that come out. In a geometry shader you could test a vertex against the camera frustum and if you find it to be inside emit it with a call to glEmitVertex() in the shader, otherwise skip that call and it will not end up in the captured transform feedback data.
In the second case I'm not sure if transform feedback is the right tool, because OpenGL determines hidden surfaces at the fragment level (using the depth buffer) not at the level of primitives or vertices. In any case I think you would first render the scene normally (with filled faces) to a depth buffer and in a second pass render points again and in the geometry shader manually test them against the depth buffer (and the camera frustum).

ananass
11-04-2014, 03:20 AM
Thanks for you response.

I think i'm in the second case. Becase i have to get the vertices that construct the faces front to the camera. (imagine a mesh with lot of faces..) .

Unfortunately i really don't understand you idea for the second case. Can you more detail please, or give a piece of code, or tutorial it can supports me?

carsten neumann
11-04-2014, 04:49 AM
Hmm, now I'm even less sure I understand what you ultimately want: do you care about vertices or faces? If you only grab the visible vertices you'll miss those faces that are partially outside the camera frustum - because you miss the corner vertex that is outside the frustum. Or are you perhaps just looking for glCullFace (https://www.opengl.org/sdk/docs/man/html/glCullFace.xhtml) to filter out front/back facing polygons?

FWIW, what I was suggesting earlier: you want the vertices that are in the camera frustum and not behind any faces of objects.
- render all object faces (as e.g. GL_TRIANGLES) with depth testing and depth writes enabled - color does not matter, you can even disable color writes (glColorMask), we only care about the depth buffer from this step
- switch the depth test to GL_LEQUAL (glDepthFunc)
- at this point if you render anything and it passes the depth test you know that it is at least as close to the camera as anything you've rendered so far
- render the vertices of your objects as GL_POINTS
- the points that end up being rendered (that is those that pass the depth test) are those visible to the camera - the problem is collecting them

The last point is problematic because the depth test normally runs after the fragment shader (way too late for capture with transform feedback). In recent versions of GLSL you can use layout(early_fragment_tests) to move the tests before the fragment shader - that means you only need a way to store the vertex information from the fragment shader (which is still too late for transform feedback). One option here is to use an atomic counter and imageStore to write to successive pixels of an image.

Alternatively, if you care about the visible faces you could do something like this:
- assign each face a unique color
- render all object faces (as e.g. GL_TRIANGLES) where you draw the face with its assigned color (no lighting/shading, just the flat color).
- read back the color buffer produced by this and go through all pixels, if a pixel contains one of the unique face colors you know that the corresponding face is visible.

Instead of colors you could also render to a framebuffer object (FBO) with a color attachment of GL_R32UI internal format and have your fragment shader write unique ids (perhaps simply the built in gl_PrimitiveId) for each face to that.

ananass
11-04-2014, 06:46 AM
Oké, i had enabled Depth_Test with glEnable(GL_DEPTH_TEST); and glDepthFunc(GL_LEQUAL);. the step of rendering the vertices as GL_Points means that i have to draw them to see if it really render me the visible vertices. I did it with glDrawArrays(GL_POINTS, 0, 16 ); but it renders me all the vertices of the cube. Is there and approriate function that render the GL_POINTS that passes the testDepth ?

carsten neumann
11-04-2014, 07:30 AM
That should not happen if you first rendered the faces of the cube as GL_TRIANGLES. Are you perhaps clearing the depth buffer between rendering the faces and the points?

ananass
11-04-2014, 07:47 AM
That's what i did:

while (window.isOpen())
{
sf::Event windowEvent;
while (window.pollEvent(windowEvent))
{
switch (windowEvent.type)
{
case sf::Event::Closed:
window.close();
break;
}
}

// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT );// | GL_DEPTH_BUFFER_BIT);

// Calculate transformation
glm::mat4 model;
model = glm::rotate(
model,
(GLfloat)clock() / (GLfloat)CLOCKS_PER_SEC * 180.0f,
glm::vec3(0.0f, 0.0f, 1.0f)
);

glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));

// Draw cube
glDrawArrays(GL_TRIANGLES, 0, 36);


glDrawArrays(GL_POINTS, 0, 36);

// Swap buffers
window.display();
}

PS : (Sorry if i ask a lot of questions i'm really beginneer, i apreciate your help. They guided me to Transform Feed Back approach before, i spent one week on understanding it. ).

carsten neumann
11-04-2014, 08:09 AM
Please use
around source code, otherwise the formatting/indention is lost making it unnecessary hard to read.

So does this actually still render the cube? When I was saying GL_TRIANGLES I really meant "appropriate primitive type that produces surfaces" as opposed to GL_POINTS or GL_LINES. If your vertex data is organized for GL_QUADS it probably will not work (unmodified) if you try to render it as GL_TRIANGLES, so keep it as GL_QUADS.
I think it would also be helpful if you could clarify what exactly it is you are after and what you want to do with it. My answers are sort of based on a guessed understanding what you are after, but if previously transform feedback was suggested for the job, perhaps I'm making suggestions to solve a problem you don't actually want to solve ;)

ananass
11-04-2014, 08:31 AM
i knew what you have meant with GL_TRIANGLES. I have 2 Cube. A Cube constituting with QUADS, and another constituting with Triangles :p so i wanted to test both..

I think that we're at the good way. So if you want me to more clarify : i want to retrieve the coordinates of the vertices visible from a point of view (camera). these coordinates will help me after to bring some Saliency Values i've already compute it for every vertex. that's the goal ! so for example if the camera make a zoom to the head of a humain mesh, i have to capture the vertices consituting the part visible of the head.

your approach still good for this i think ? :biggrin-new:

if yes, what was wrong with my last rendering of the GL_POINTS

carsten neumann
11-04-2014, 09:03 AM
Ok, I see, thanks. Unfortunately I don't really have a good idea what is wrong with the points rendering, can you post a screenshot? Also, you should still clear the depth buffer at the beginning of a frame (when you clear the color buffer), just not between drawing the cube faces and the points ;)

ananass
11-05-2014, 01:45 AM
Oke, here is my code:


// Shader sources
const GLchar* vertexSource =
"#version 150 core\n"
"in vec3 position;"
"in vec3 color;"

"out vec3 Color;"
"out vec3 outposition;"
"uniform mat4 model;"
"uniform mat4 view;"
"uniform mat4 proj;"

"void main() {"
"outposition=position;"
" Color = color;"

" gl_Position = proj * view * model * vec4(position, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 150 core\n"
"in vec3 Color;"

"out vec4 outColor;"


"void main() {"
" outColor = vec4(Color, 1.0) ;"
"}";

int main()
{
sf::ContextSettings settings;
settings.depthBits = 24;
settings.stencilBits = 8;
sf::Window window(sf::VideoMode(800, 600, 32), "OpenGL", sf::Style::Titlebar | sf::Style::Close, settings);

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);


// Initialize GLEW
glewExperimental = GL_TRUE;
glewInit();

// Create and compile the vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);

// Create and compile the fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);

// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);

// Create Vertex Array Object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// Create a Vertex Buffer Object and copy the vertex data to it
GLuint vbo;
glGenBuffers(1, &vbo);

//cube avec des faces triangulaires
GLfloat vertices[] = {

-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,

0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f,
};

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);

GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
GLint uniModel = glGetUniformLocation(shaderProgram, "model");

// Set up projection
glm::mat4 view = glm::lookAt( glm::vec3(3.0f, 3.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));

glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
GLint uniProj = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));

while (window.isOpen())
{
sf::Event windowEvent;
while (window.pollEvent(windowEvent))
{
switch (windowEvent.type)
{
case sf::Event::Closed:
window.close();
break;
}
}

// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Calculate transformation
glm::mat4 model;
model = glm::rotate(
model, 0.0f,glm::vec3(0.0f, 0.0f, 1.0f) );
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));

// Draw cube
glDrawArrays(GL_TRIANGLES, 0, 36);

// Draw POINTS
glDrawArrays(GL_POINTS, 0, 36);

// Swap buffers
window.display();

}
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
}


and this is what i got: 1503

If i use only the " glDrawArrays(GL_POINTS, 0, 36);" command, i have this :
1504

ananass
11-05-2014, 07:52 AM
i have an Idea , if we can access to the ZBuffer that contain the depth of the vertices. We can compare these values to the Z values of the vertices after tranformation, and here we can easly fin the coordinates that are visible ! :D, am I right?