Skip to content

181. Drawing 3D characters

November 6, 2014

Yes, this is how to do 3D lettering (one way, anyway).

It is really hard to do 3D lettering, because setting up vertices for curved letters is very difficult.

But my code can do it for any font of any size.

Of course, I cheated.

That image is not 3D.

Nor is it a single image.

It is 25 images, one behind the other, separated by 0.4 of a pixel. The front image has a brighter colour than the others, to make them look shaded. This creates the illusion of a solid 3D image.

There is one problem I’ll explain below.

You can almost see what I am doing if you look at my setup and draw functions. I put all the detailed code in separate functions (I’m trying the Rapid approach of breaking your code up into little functions that each do one thing).

Underneath, I’ll explain what each function does.

function setup()
    ThreeD=Create3DImage(180) 
    Settings() 
end

function draw()
    background(255)
    SetupCamera()
    ChangeDistance() 
    pushMatrix()
    TranslateRotate() 
    ReverseRotationIfSideOn() 
    DrawLayers()
    popMatrix()
    ChangeRotation()
end

Create3DImage

I create a square flat mesh in memory with “3D” written on it in white. Doing this just makes the drawing faster. (Why white? Because then, I can use the setColors function to alter it to any other colour. If I made it any other colour, then setColors would mix this colour with the new colour, and I don’t want that).

function Create3DImage(size)
    local m=mesh()
    m:addRect(0,0,size,size)
    local img=image(size,size)
    setContext(img)
    fill(255)
    fontSize(144)
    text("3D",size/2,size/2)
    setContext()
    m.texture=img
    return m
end

Settings

I think these are fairly obvious, except nextRotChange. I leave the image facing forward for 5 seconds at the beginning, then I change the rotation randomly (using ChangeRotation) every 15 seconds after that. So nextRotChange just tells me when to do that. You can ignore it and do your own rotations.

function Settings()
    --starting position of image
    pos=vec3(0,0,-500)
    --starting rotation in x,y,z axes
    rot=vec3(0,0,0)
    --change in rotation at each frame
    dRot=vec3(0,0,0)
    --time until we randomly change the rotation
    nextRotChange=5
    --change in z value at each frame
    dZ=1
    --number of layers of text and separation between them
    LayerCount=25
    LayerThickness=0.4
end

SetupCamera

Absolutely standard 3D. The camera is at zero, facing forward into negative z.

function SetupCamera()
    perspective()
    camera(0,0,0,0,0,-500)
end

ChangeDistance

At each frame, we move the image closer or further away, reversing direction when we get too close or too far.

function ChangeDistance()
    pos.z=pos.z+dZ
    if pos.z<-500 or pos.z>-210 then dZ=-dZ end   
end

TranslateRotate

We translate to the current position, and rotate in x,y and z axes (using the rotate function, you have to do them all one by one).

function TranslateRotate()
    translate(pos.x,pos.y,pos.z)
    rotate(rot.x,1,0,0)
    rotate(rot.y,0,1,0)
    rotate(rot.z,0,0,1)
end

ReverseRotationIfSideOn

Here is the one problem of faking 3D with a set of 2D images. If you view them sideon, you see the gaps between the separate images. So this code prevents the rotation from ever showing a sideon view, by reversing the rotation if it gets too close.

function ReverseRotationIfSideOn()
    local v1,v2= modelMatrix()*vec3(0,0,-1),modelMatrix()*vec3(0,0,1)
    if math.abs(v1.z-v2.z)<0.1 then dRot=-dRot end
end

So I need some way of detecting whether we are close to a sideon view. This is hard, because we are rotating in all three axes, x, y and z, and because you can’t simply add all the rotations so far to see where you are (that gives the wrong answer). Hmm…

But I know that modelMatrix can help me (don’t know what it is? – see here. ).

If I have two pixels, one in the front image and one in the back, then as we get close to a sideon position, the z (depth) value of those pixels will get very close, and it will be exactly the same when we are sideon. So all we need to do is test the z difference between two pixels that start off being one behind the other. I can use the vectors (0,0,-1) and (0,0,1) because we’re only worried about z.

I multiply each of them by modelMatrix(), which tells me what their position will be if we apply the current translation and rotations. Then I compare them, and if they are within 0.1 pixels of each other, I reverse direction.

DrawLayers

Drawing the layers is pretty simple.

function DrawLayers()
    --draw inside layers
    ThreeD:setColors(173, 42, 42)
    for i=1,LayerCount do
        translate(0,0,LayerThickness)
        ThreeD:draw()
    end
    --draw front layer
    translate(0,0,LayerThickness)
    ThreeD:setColors(255,0,0)
    ThreeD:draw()
end

ChangeRotation

This function changes the rotation randomly. The only reason I put this in was that if the original (random) rotation turned out to be strange, at least it would change. You can leave this out.

function ChangeRotation()
    rot=rot+dRot
    --change rotation randomly every 15 seconds
    if ElapsedTime>nextRotChange then 
        dRot=vec3(math.random()-0.5,math.random()-0.5,math.random()-0.5)*2
        nextRotChange=ElapsedTime+15
    end
end

The full code is here.

Drawing 3D images in 2D

Things are much simpler in 2D. Simply draw your text several times, moving the x and y position 1 or 2 pixels diagonally up or down each time, and make the top image a bit brighter.

(Footnote – I said I was using a Rapid approach above. I know that’s strictly not true, because Rapid is much more than slicing your code into bits, and my functions are still too tightly coupled. But this is only a simple demo!)

Advertisement
2 Comments
  1. techdojo permalink

    Cool demo and an even cooler use of the available functions to fake the effect – thanks for sharing 🙂

Trackbacks & Pingbacks

  1. Index of posts | coolcodea

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: