Complicating Sci-fi Models Procedurally: What is Greeble

GemesUtra

Video Games Guides, Reviews & News

Complicating Sci-fi Models Procedurally: What is Greeble

image

To begin with, let me complain that “greeble” is a terrible word that needs to be banished from the dictionary.

Well, removing the stone from the soul, let’s go to the explanation. Greeble is small repetitive details added to a model to give it a sense of scale and a certain aesthetic. Greebles became popular thanks to classic science fiction films, in which the” model ” was often a physical sculpture:

If you already know from my tutorial on extrusion how to extrude procedural meshes, you know how to add mushrooms. Adding simple mushrooms to a mesh can be realized by extruding all mesh polygons to a random length.

However, you may have noticed that the tutorial presented above only deals with the extrusion of triangles, while in the image at the beginning of the article, the greeble are square. I had to configure the mesh to be divided into quadrilaterals, and many meshes often consist of polygons with more than three indexes. So in this tutorial, we will learn how to extrude a polygon with n indexes and apply this algorithm to the entire mesh to create greebles. We will also learn a couple of ways to make variations in the greebling algorithm to obtain less uniform results.

The surface normal

First, let’s learn how to calculate the normal of a polygon with arbitrary n indexes. If we can assume that this polygon is planar, that is, all its vertices are on the same plane, the process does not differ from calculating the normal of a polygon with three indexes.

A surface normal is a perpendicular to the face of a polygon, which can be calculated by taking the vector product of two vectors pointing along the edge of the polygon.

Then we normalize this vector so that its length is equal to 1, since we only need the direction from the surface normal, not the length.

function getFaceNormal (mesh, poly)
  Vec3 v1 = mesh:getVertex(poly[1])
  Vec3 v2 = mesh:getVertex(poly[2])
  Vec3 v3 = mesh:getVertex(poly[3])
  Vec3 e1 = v2 - v1
  Vec3 e2 = v3 - v2
  Vec3 normal = e1:cross(e2)
  return normal:normalize()
end

If we cannot assume with certainty that the polygon is planar, then the above algorithm gives preference to the plane on which the first two indexes are located. For a more accurate representation of the direction in which the polygon points, we can instead take the average of all the vector products of the edges:

function getFaceNormal (mesh, poly)
  Vec3 n = Vec3(0, 0, 0)
  for i = 1, #poly -2 do
    Vec3 v1 = mesh:getVertex(poly[1])
    Vec3 v2 = mesh:getVertex(poly[1+ i])
    Vec3 v3 = mesh:getVertex(poly[2+ i])
    n:add((v2 - v1):cross(v3 - v1))
  end
  return n:normalize()
end

An example showing the extrusion of a planar quadrilateral.

Extrusion

Now that we have information about the surface normal, we are ready to extrude the polygon in the direction of the normal. Simply put, to extrude a polygon, we create new vertices by moving the old vertices in the direction of the surface normal.

More detail:

  1. Create new vertices “above” the old ones in the normal direction.

    New vertices can be calculated as follows:

    (the position of the old vertices) + (the direction of the normal)

    This “shifts” the old position in the direction of the surface normal.

    For example, look at the image above, where v1 moves in the normal direction to v5.

  2. Create quadrilaterals to link new and old vertices.

    Note that one new quadrilateral is created for each index in the new polygon.

    For example, look at a quadrilateral created from v8, v7, v3, and v4.

  3. Replace the old polygon with a new polygon created by the new vertices. For example, look at a quadrilateral created from v5, v6, v7, and v8.
function extrudePoly (mesh, polyIndex, length)
  int[] poly = mesh.polys[polyIndex]
  int[] newPoly = []
  Vec3 n = getFaceNormal(mesh, poly)

  -- (1) Create extruded verts
  for j = 1, #poly do
    local p = mesh:getVertex(poly[j])
    newPoly[#newPoly + 1] = #mesh.verts
    -- length determines the length of the extrusion
    mesh:addVertex(p + (n*length))
  end

  -- (2) Stitch extrusion sides with quads
  for j0 = 1, #poly do
    local j1 = j0 % #poly + 1
    mesh:addQuad(
      poly[j0],
      poly[j1],
      newPoly[j1],
      newPoly[j0]
    )
  end

  -- (3) Move existing face to extruded verticies
  for j = 1, #poly do
    mesh.polys[pi][j] = newPoly[j]
  end
end

Uniform greebling.

Mesh greebling

Now that we have the get Surface Normal() function and the extrude () function, greebling is very easy! We simply apply the extrude() function to each mesh polygon. We use random length extrusion so that each extruded polygon has a slightly different size, which creates a sense of texture. The algorithm shown below is applied to the cube shown above, which consists entirely of quadrilaterals.

function greeble (mesh)
  for i = 1, #mesh.polys do
    -- these random values are arbitrary :p
    float length = random:getUniformRange(0.1, 1.0)
    extrudePoly(mesh, i, length)
  end
  return mesh
end

Congratulations, our greebling is working. But we can do more! Now the greebling is fairly uniform. Here are two examples of modifications to make it more interesting.

Modification 1: the presence of greebling depends on randomness

It’s pretty simple: just roll the dice to determine whether to apply greebling to each polygon. This makes greebling less uniform. The algorithm shown below is applied to the cube shown above.

 for i = 1, #mesh.polys do
   if random:chance(0.33) then
     float length = random(0.1, 1.0)
     extrudePoly(mesh, i, length)
   end
 end
 return mesh
end

Modification 2: adding extrusion scale

This requires to change the algorithm of the extrusion. When we create the vertices of an extruded polygon, we can reduce them towards the center of the polygon by a random amount to make the object look more interesting.

To begin with, our extrude() function should get an additional parameter that determines the amount of narrowing of the new polygon. We will define it as Vec3 called scale. To move a vertex toward the center, we interpolate the position of the vertex between its original position and the center of the polygon by the value scale.

-- find the center of the poly
Vec3 c = mesh:getFaceCentroid(poly)
for j = 1, #poly do
  local p = mesh:getVertex(poly[j])
  newPoly[#newPoly + 1] = #mesh.verts
  self:addVertex (
    math.lerp(c.x, p.x, scale.x) + n.x * length,
    math.lerp(c.y, p.y, scale.y) + n.y * length,
    math.lerp(c.z, p.z, scale.z) + n.z * length
  )
  mesh:addVertex(p + (n*length))
end

Now you can use it in the greebling algorithm by scaling it to a random value for each polygon. So we get the image shown above.

function greeble (mesh)
  for i = 1, #mesh.polys do
    float length = random:getUniformRange(0.1, 1.0)
    Vec3 scale = (random:getUniformRange(0.1, 1.0),
                  random:getUniformRange(0.1, 1.0),
                  random:getUniformRange(0.1, 1.0))
    extrudePoly(mesh, i, length, scale)
  end
  return mesh
end

The end

Great, we’ve reached completion! I hope this tutorial was useful for you.

One thought on “Complicating Sci-fi Models Procedurally: What is Greeble

Leave a Reply

Your email address will not be published. Required fields are marked *