Improving the Procedural
Modelling Workflow

by Dave Pagurek

What is procedural modelling?

It's because of detail

  • Adding detail in 2D
    • Some is hidden
    • Can be abstracted
  • Adding detail in 3D
    • Might not be hidden from all angles
    • Need a concrete shape to render

What do you do about it?

Sometimes it's easier to describe how to make instances of an object rather than make them all by hand.

Generating from grammars

Breaking sentences up into trees of grammar rules

Thehandsomeauthordisseminatedknowledgefromhisexpansivebrain.
ARTICLEADJNOUNVERBNOUNPREPADJADJNOUN
NOUN PHRASENOUN PHRASE
NOUN PHRASE
VERB PHRASE
CLAUSE
SENTENCE

Breaking sentences up into trees of grammar rules

Isaywords.
NOUNVERBNOUN
VERB PHRASE
CLAUSE
SENTENCE

Breaking sentences up into trees of grammar rules

Isaywords.
NOUNVERBNOUN
VERB PHRASE
CLAUSE
SENTENCE
SENTENCECLAUSE (COMPOUND | ε)
COMPOUNDand CLAUSE (COMPOUND | ε)
CLAUSENOUN VERB PHRASE
VERB PHRASEVERB (NOUN | ε)
NOUNabacus | babboon | cantaloupe | ...
VERBarrive | bamboozle | capitulate | ...

Going the other way, starting from the rules

Ants cover surfaces and I raise questions.
CLAUSE (COMPOUND | ε)
and CLAUSE (COMPOUND | ε)
NOUN VERB PHRASE
VERB (NOUN | ε)
abacus | babboon | cantaloupe | ...
arrive | bamboozle | capitulate | ...

Grammars for constructing shapes

Generating drawing commands instead of sentences

Let's draw a tree with these tools

Let's draw a tree with these tools

translate(0, 50)
BRANCH
lineTo(0, -50)
translate(0, -50)
circle(20)

Let's group some things into a rule called BRANCH.

Copy-and-paste that a few times

Writing that as a grammar

translate(0, 50) BRANCH scale(0.5) BRANCH ...
translate(0, 50) NODE
BRANCH (SUB BRANCH | ε)
scale(0.5) NODE
lineTo(0, -50) translate(0, -50) circle(20)

How would we do multiple branches?

Updating the grammar to have two branches

TREEtranslate(0, 50) NODE
NODEBRANCH (SUB BRANCH SUB BRANCHES | ε)
SUB BRANCH SUB BRANCHES scale(0.5) NODE push() rotate(-30) NODE pop() push() rotate(30) NODE pop()
BRANCHlineTo(0, -50) translate(0, -50) circle(30)

Making it less regular

Add the command random(a, b), which will generate a random number between a and b inclusive, and allow more than 2 branches:

TREEtranslate(0, 50) NODE
NODEBRANCH (SUB BRANCHES | ε)
SUB BRANCHES scale(0.5) push() scale(random(0.4, 0.8)) rotate(-30) rotate(random(-40, 40)) NODE pop()
push() rotate(30) NODE pop() (SUB BRANCHES | ε)
BRANCHlineTo(0, -50) translate(0, -50) circle(30)

Some examples

It would be nice to get rid of weird results like this one.

Controlling procedural modelling
at the grammar level

Trying to make foolproof instructions

How do you keep good results but prevent bad ones like this?

The years keep comingandthey don't stop comingandthey don't stop comingandthey don't stop comingandthey don't stop coming
CLAUSECLAUSECLAUSECLAUSECLAUSE
COMPOUND
COMPOUND
COMPOUND
COMPOUND
SENTENCE

How to stop at 4 chained clauses?

Maybe by flattening out the recursive part of the grammar:

SENTENCECLAUSE (COMPOUND | ε)
COMPOUND and CLAUSE (COMPOUND | ε) (and CLAUSE | ε) (and CLAUSE | ε) (and CLAUSE | ε)
CLAUSENOUN VERB_PHRASE
VERB_PHRASEVERB (NOUN | ε)
NOUNabacus | babboon | cantaloupe | ...
VERBarrive | bamboozle | capitulate | ...

Still has limitations

Controlling procedural
modelling using search

When it becomes easier to specify how good a result is than
to specify how to only make good results

Specifying what "good" means

One way is to compare a result against a target silhouette:

Who's that Pokémon?

Silhouette score algorithm

  1. Flood fill all closed areas in the image
  2. Set score = 0
  3. For each pixel x, y in the image:
    • If image[x][y] == target[x][y], add 1 to the score
  4. Return score

Picking the best result

Result images as probabilistic samples

The probability of a specific sample being picked depends on the choices the grammar made when generating it.

  1. Every time a | is used, the probability is multiplied by 0.5 because half the time it generates one option, and the other half of the time, it generates the other
    • Our grammar makes one choice for every branch after the first one, and our ideal tree has eight
    • The probability gets multiplied by 0.58 = 0.00390625
  1. As an approximation, every time random(a, b) is used, the probability is multiplied by 0.1 if we make the (generous) assumption that it picks one of 10 values between a and b and not a continuous number
    • Each branch makes two of these calls, and we have eight
    • The probability gets multiplied by (0.1 × 0.1)8 = 1 × 10-16

Altogether, that's around 3.9 × 10-19

Markov Chain Monte Carlo (MCMC) sampling

Instead of comparing completely random samples, branch off from a reference that you know is good.

  1. Pick an initial reference
  2. Mutate the reference to create a proposal
    • If the proposal is better than the reference, make it the new reference
    • If it's worse, randomly still make it the proposal, where this is less likely to happen the worse it scores
  3. Go back to step 2

Markov Chain Monte Carlo (MCMC) sampling

Aside: what does "mutate" mean here?

something = something + 1 1 / 8
VAR VAR OP NUM NUM OP NUM
EXPR
EXPR
EXPR
ASSIGNMENT
...
ASSIGNMENTVAR = EXPR
EXPR(VAR | NUM) (OP EXPR | ε)
OP+ | - | * | /
...

In the context of images

Usually means replacing a part of the image with another

Sequential Monte Carlo (SMC) sampling

Re-rendering the whole image is slow. Instead, take samples for each piece added.

  1. Create n samples with the top-level grammar rule
  2. For each sample:
    • Break down one grammar rule into some combination of terminals/other rules
    • Add the score for any new shapes to the score for the sample
  3. Make n new samples by copying from the last set, where a higher score means it is more likely to be copied
  4. Go back to step 2

Sequential Monte Carlo (SMC) sampling

Better score functions

Scoring for 3D

Properties of a good score function

My solution: Model skeletons

Model skeleton examples

Guiding curves

Alignment

Distance

What it's like to use

Editing environment

Split-screen with grammar and curves

What can you make with it?

What can you make with it?

What's next for this project?

Questions?