(this is a computer graphics project)

the big idea

The general direction of computer graphics development is, naturally, focused on the increase of realism - higher levels of detail, more advanced lighting, better effects and simulation. This is very cool, of course, and has led to incredible looking visuals.

but i dont want to deal with the cutting edge.

This is the start of a series of 'experiments' in low(er)-poly graphics. Specifically, I want to explore/create various mesh generation techniques. As of right now, I plan for each of these experiments to center around some abstract representation that defines a model less explicitly than directly specified vertices/edges/faces, and a mesh generation method to go with it.

Mesh generation is a well studied subject, and it has yielded efficient and specification-faithful techniques. My main focus, however, is more on making methods that generate interesting looking meshes. Of course, efficiency and faithfulness are still important, but I see them as secondary - this is, in a way, an art project.

plato suggests that physical objects in our physical world world, that we see/touch, are mere portrayals or reflections of some true form of the object, an abstract 'concept' of the essense of object. you can draw a million circles, but none of them is entirely the true concept of a circle, while all of them share that they are portrayals of the form of a circle.

in my dreams i see it all

A farther-fetched idea I had a while ago was for a representation method that was able to be (relatively easily) described via 2D drawings that I could make on paper in a notebook. I don't entirely know how this would work, but it seems that the representation would have to be both simple and quite abstract.

This would also require making a visual descriptor language alongside the representation method. In my head I imagined something like the Words of Creation.

anyway

This website is a means for me to track, (semi)-formalize, and share these projects, as well as some hopefully interesting renders that come out of them. It will also hopefully motivate me to work on this consistently.

[1] boolean fields and wrapping

This is something I tried a bit ago that I see as the first attempt of this.

wrapped sphere
wrapped sphere
flat shading
with flat shading

representation

The 'blackbox' form of this object representation is simply a function that, given a point (in 3D), returns whether the point is in the object or not.

For example, to describe a sphere of radius 5 centered at the origin, the function would be

bool in_sphere(point p):
    return distance(p, (0, 0, 0)) <= 5

mesh generation

The generation method creates a mesh of equilateral(-ish) triangles that 'shells' the underlying form to approximate its shape.

The method takes as input:

  1. the underlying form as a function bool(point) in_obj
  2. a specified side length float s
  3. an angle step float alpha

The mesh generation is intuitively like 'wrapping' the object with a triangle mesh. We first start with a single triangle (of side length s) on the surface of the object. Then, based on the assumption that a single edge must belong to exactly two triangles, we perform a floodfill-like exploration from edges belonging to only a single triangle, 'rotating' a new triangle around the edge until we find a valid vertex on the surface of the object. We do this until we no longer have any single-triangle edges.

A (very) rough outline of the method is as following (ignoring the nuances that come with interference checking and rotation):

Mesh wrap(
    bool(point) in_obj,
    float s,
    float alpha
):
    mesh = new empty Mesh

    v_0 = any point on the surface
    v_1 = a point on the surface a distance s from v_0
    v_m_0, v_m_1 = add v_0, v_1 to mesh
    e = add edge(v_0, v_1) to mesh

    edge_q = new empty queue
    add e to edge_q
    while (edge_q not empty):
        new_tri = add_triangle(
            mesh,
            edge_q.pop(),
            in_obj,
            s,
            alpha
        )

        add all lonely edges of new_tri to edge_q
        // lonely edges defined as being part of only one triangle
    
    return mesh
tri* add_triangle(
    Mesh m,
    Edge e,
    bool(point) in_obj,
    float s,
    float alpha
):
    v_start = any point a distance s from both endpoints of e

    for 0 <= cur_angle < 360; step = alpha:
        v_pot = rotate v_start by cur_angle around e

        next_angle = cur_angle+alpha
        next_v_pot = rotate v_start by next_angle around e

        if in_obj(v_pot) == in_obj(next_v_pot):
            continue
        
        if v_pot is in or near m:
            close_vs = vertices in m close (< a configured distance) to v_pot
            for close_v in close_vs:
                pot_tri = triangle(close_v, e.v1, e.v2)
                if pot_tri does not interfere with m:
                    add pot_tri to m
                    return pot_tri
            continue
        
        v_m = add v_pot to m
        tri = triangle(v_m, e.v1, e.v2)
        add tri to m
        return tri

    return null
wrapping a sphere
20 iterations of wrapping a sphere

Due to its stochatic nature and arbitrary collision checking, the generated mesh usually ends up with some missing triangles that I have to fill in manually, which is annoying, but the generation algorithm can definitely be improved.

its a start i guess

This algorithm is wildly inefficient and a little naive, but it's a fine start and does produce some nice looking meshes. The representation method is very unrestrictive and easy to specify, so you can do things like this:

bool in_cube(point p):
    return (-2 <= p.x <= 2) and (-2 <= p.y <= 2) and (-2 <= p.z <= 2)

bool in_sphere(point p):
    return distance(p, (2, 2, 2)) <= 2

bool in_cylinder(point p):
    return distance((p.x, p.y), (1.5, 1.5)) <= 1

bool in_amalgamation(point p):
    return (in_cube(p) or in_sphere(p)) and not in_cylinder(p)
wrapped amalgamation
wrapped amalgamation

However, I think this freedom also limits the ability of mesh generation methods. It's impossible(?) to find surface points faster than just guessing and checking, as only a boolean function is given to represent objects.

The next step seems to be adding some more descriptive returns to the underlying representation than just in/out.

notes

  • i might also post some other stuff on here too