2
votes

Let's say that I have my geometry and created an index buffer containing the triangle adjacency information. Then, changed the draw mode from GL_TRIANGLES to GL_TRIANGLE_ADJACENCY. The question is, can I convert the geometry from triangle adjacency to triangles strip using the geometry shader?

something like this:

layout(triangles_adjacency) in;
layout(triangle_strip, max_vertices = 3) out;

in Vertex
{
    vec3 normal;
} vertex[];

out FragmentVertexData
{
  vec3 normal;
  vec3 fragpos;
} VertexOut;


void main()
{
    for(int i = 0 ; i < gl_in.length(); i+=2)
    {
        gl_Position = ProjectionMatrix * ViewMatrix * ModelMatrix * gl_in[i].gl_Position;
        VertexOut.normal = vertex[i].normal;
        VertexOut.fragpos =  vec3(ModelMatrix * gl_in[i].gl_Position);
        VertexOut.fragpos = gl_Position; 
        EmitVertex();
    }
    EndPrimitive();
}

I already tried and in fact it draws to geometry but something is wrong with the normals. Do I have to index them too? am I missing one step?

enter image description here

and this is how I am using the fragpos and normal in the fragment shader

vec3 normal = normalize(VertexOut.normal);
  vec3 lightDir = normalize(light.position - VertexOut.fragpos);

This is my algorithm to write the triangle adjacency index buffer:

void Loader::FindAdjacencies(const aiMesh * paiMesh, vector<int>& indices)
{
    // Step 1 - find the two triangles that share every edge
    for (uint i = 0; i < paiMesh->mNumFaces; i++) 
    {
        const aiFace& face = paiMesh->mFaces[i];

        Face Unique;

        // If a position vector is duplicated in the VB we fetch the 
        // index of the first occurrence.
        for (uint j = 0; j < 3; j++)
        {
            uint Index = face.mIndices[j];
            aiVector3D& v = paiMesh->mVertices[Index];

            if (m_posMap.find(v) == m_posMap.end()) 
            {
                m_posMap[v] = Index;
            }
            else 
            {
                Index = m_posMap[v];
            }

            Unique.Indices[j] = Index;
        }

        m_uniqueFaces.push_back(Unique);

        Edge e1(Unique.Indices[0], Unique.Indices[1]);
        Edge e2(Unique.Indices[1], Unique.Indices[2]);
        Edge e3(Unique.Indices[2], Unique.Indices[0]);

        m_indexMap[e1].AddNeigbor(i);
        m_indexMap[e2].AddNeigbor(i);
        m_indexMap[e3].AddNeigbor(i);

    }

    // Step 2 - build the index buffer with the adjacency info
    for (uint i = 0; i < paiMesh->mNumFaces; i++) 
    {
        const Face& face = m_uniqueFaces[i];

        for (uint j = 0; j < 3; j++) 
        {
            Edge e(face.Indices[j], face.Indices[(j + 1) % 3]);
            assert(m_indexMap.find(e) != m_indexMap.end());
            Neighbors n = m_indexMap[e];
            uint OtherTri = n.GetOther(i);
            uint minus1 = (uint)-1;
            bool comp = (OtherTri != minus1);
            assert(comp);

            const Face& OtherFace = m_uniqueFaces[OtherTri];
            uint OppositeIndex = OtherFace.GetOppositeIndex(e);

            indices.push_back(face.Indices[j]);
            indices.push_back(OppositeIndex);
        }
    }

}

Unfortunately, only works for close geometries. That is the reason I am testing it with a cube. I tried with bunny.ply, but part of the model has holes and I will have to edit it in blender.

and this is the obj file:

# Blender v2.76 (sub 0) OBJ File: ''
# www.blender.org
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.333333 0.666667
vt 0.333333 1.000000
vt 0.000000 1.000000
vt 0.000000 0.666667
vt 0.000000 0.333333
vt 0.333333 0.333333
vt 0.333333 0.000000
vt 0.666667 0.000000
vt 0.000000 0.000000
vt 1.000000 0.333333
vt 0.666667 0.333333
vt 0.666667 0.666667
vt 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -0.000000 0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000
f 2/1/1 3/2/1 4/3/1
f 8/1/2 7/4/2 6/5/2
f 5/6/3 6/7/3 2/8/3
f 6/9/4 7/7/4 3/6/4
f 3/10/5 7/11/5 8/8/5
f 1/11/6 4/12/6 8/1/6
f 1/4/1 2/1/1 4/3/1
f 5/6/2 8/1/2 6/5/2
f 1/11/3 5/6/3 2/8/3
f 2/5/4 6/9/4 3/6/4
f 4/13/5 3/10/5 8/8/5
f 5/6/6 1/11/6 8/1/6

Thank you very much!

This is my vertex shader:

#version 430 core
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in vec3 normal;

out VertexData
{
    vec3 normal;
    vec2 textCoord;
} vertex;

// Values that stay constant for the whole mesh.

void main(){
  gl_Position =  vec4(vertexPosition,1.0f);
  vertex.textCoord = texCoord;
  vertex.normal = normal;
}

and my fragment shader:

#version 430 core

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 

struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Material material;
uniform Light light;

out vec4 color;

uniform float LightIntensity;
uniform vec3 LightPos;
uniform vec3 ViewPos;

in FragmentVertexData
{
    vec3 normal;
    vec3 fragpos;
    vec2 texCoord;
} VertexOut;


void main(){


  // color of the object
  vec3 objectColor = vec3(1.0f, 0.5f, 0.31f);


  // Ambient

  vec3 ambient = light.ambient * material.ambient ;

  vec3 normal = normalize(VertexOut.normal);
  vec3 lightDir = normalize(light.position - VertexOut.fragpos);  

  float diff = max(dot(lightDir,normal), 0.0);
  vec3 diffuse = light.diffuse * diff * material.diffuse ;

  vec3 viewDir = normalize(ViewPos - VertexOut.fragpos);
  vec3 reflectDir = reflect(-lightDir, normal);  

  vec3 halfwayDir = normalize(lightDir + viewDir );

  float spec = pow(max(dot(normal, halfwayDir), 0.0), material.shininess);


  vec3 specular = light.specular * spec * material.specular ; 

  color = vec4((ambient + diffuse + specular) * objectColor, 1);

}
1

1 Answers

4
votes

A triangle adjacency, contains adjacency data for triangles, so that adjacent triangles can be accessed. The Geometry Shader stage has access to 6 vertices and attributes, which forms 4 triangles. 3 vertices form the triangle which is currently rendered. The other 3 vertices are forming the neighbor (adjacent) triangles, in combination with the 3 side edges of the currently rendered triangle. (see GL_ARB_geometry_shader4 and Primitive).

triangle adjacency

If a Geometry Shader should pass the rendered triangle to the next shader stage, then it has to process the three points only, which form the triangle, and not its adjacency.

Further issues:

  • The world space position of the model is overwritten by VertexOut.fragpos = gl_Position;
  • The normal vector has to be transformed to world space:
    VertexOut.normal = mat3(ModelMatrix) * vertex[i].normal;

The geometry shader should look somehow like this:

layout(triangles_adjacency) in;
layout(triangle_strip, max_vertices = 3) out;

in Vertex
{
    vec3 normal;
} vertex[];

out FragmentVertexData
{
    vec3 normal;
    vec3 fragpos;
} VertexOut;

uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;

void main()
{
    for (int i = 0; i < 6; i += 2)
    {
        vec4 fragPos      = ModelMatrix * gl_in[i].gl_Position;
        VertexOut.normal  = mat3(ModelMatrix) * vertex[i].normal;
        VertexOut.fragpos = fragPos.xyz;
        gl_Position       = ProjectionMatrix * ViewMatrix * fragPos;
        EmitVertex();
    } 
    EndPrimitive();
}


If the normal vectors are per face, your adjacency algorithm will fail:

The algorithm which generates the adjacency indices does not consider, that each point of a face is defined by a pair of vertex position and normal vector. Different faces have may have identically corner positions, but they always have different normal vectors. Since you put all vertex positions in a map m_pos Map, where the position is the key, the distinction of the normal vector is lost. When you have a cube, with normal vectors per face, the each vertex position has to be 3 times in the vertex buffer, because it is shared by 3 sides of the cube and has 3 different normal vectors.
If you have a mesh, where the normal vectors are per vertex (e.g a sphere), the your algorithm will proper work. But your algorithm fails if the normal vectors are per face, as is the case with a cube.


An algorithm that keeps the faces and only adds the adjacency to the faces would look like this:

#include <array>
#include <vector>
#include <map>

using TIndices  = std::vector<int>;
using TFace     = std::array<int, 3>;
using TFaces    = std::vector<TFace>;
using TVertex   = std::array<float, 3>;
using TVertices = std::vector<TVertex>;

void GenerateAdjacencies( const TVertices &vertices, const TFaces &faces, TIndices &adj )
{
    // associate each geometric vertex position with an unique ID
    std::vector<int>      uniqueMap;
    std::map<TVertex,int> tempUniqueVertices;
    int uniqueIndex = 0;
    for ( size_t vI = 0; vI < vertices.size(); ++ vI )
    {
        auto vIt = tempUniqueVertices.find( vertices[vI] );
        if ( vIt == tempUniqueVertices.end() )
        {
            tempUniqueVertices[ vertices[vI] ] = uniqueIndex;
            uniqueMap.push_back( uniqueIndex );
            uniqueIndex ++;
        }
        else
            uniqueMap.push_back( vIt->second );
    }
    tempUniqueVertices.clear();

    // find all edges and associate the edge with all the points, which form a triangle with it. 
    std::map< std::tuple<int, int>, std::vector<int> > edges;
    for ( auto & f : faces )
    {
      for ( int pI = 0; pI < 3; ++ pI )
      {
        int edgeU[2]{ uniqueMap[f[pI]], uniqueMap[f[(pI+1) % 3]] };
        int i0 = edgeU[0] < edgeU[1] ? 0 : 1;
        edges[{ edgeU[i0], edgeU[1-i0] }].push_back( f[(pI+2) % 3] );
      }
    }

    // create the adjacencies
    for ( auto & f : faces )
    {
        for ( int pI = 0; pI < 3; ++ pI )
        {
            int edgeU[2]{ uniqueMap[f[pI]], uniqueMap[f[(pI+1) % 3]] };
            int   i0   = edgeU[0] < edgeU[1] ? 0 : 1;
            auto &adjs = edges[{ edgeU[i0], edgeU[1 - i0] }];
            int   adjI = adjs.size() > 1 && adjs[0] == f[(pI+2) % 3] ? 1 : 0;
            adj.push_back( f[pI] );
            adj.push_back( adjs[adjI] );
        }
    }
}

See the example