6
$\begingroup$

I've started getting into Freestyle, and found that for a rough, sketchy look, the spatial noise or perlin noise modifier under Geometry tab works great, however when I animate things, these modifiers noise source also gets animated (as noises normally do, they get randomly calculated every frame) - and so the outline of the animation changes from frame to frame.

Is there any way or workaround to stop this and have a consistent outline for my objects?

$\endgroup$
1
  • 1
    $\begingroup$ Since the freestyle is calculated in screen-space (I think), I don't think you can force the noise to stay the same when objects move. But on my PC it isn't recalculated every frame. If I have an animation with a stationary object, the noise lines stay the same. $\endgroup$
    – Gorgious
    Jan 14, 2020 at 12:34

1 Answer 1

2
$\begingroup$

As I answered in this thread:

Does anyone know how to make my 3d model's outline look like a boiling line animation effect? like Ed, Edd n Eddy

You can control the noise with a seed, and if you repeat the seed, you also repeat the noise. That is, noise is actually deterministic and for the same inputs you will get the same outputs. Problem is, if for example an object is scaled, you now change one of the inputs. It wouldn't even make sense to expect the noise for a now different (2D) shape to be the same - it's like expecting two chains to have the same shape, when one of the chains is longer...

Gorgious makes a reasonable guess, but I think this render proves it's a wrong guess:

Clarification: I removed every 2nd frame from the animation to show that the noise isn't random.

Shape keys

Here you can see a cube that has all vertices but 2 scale towards its origin. The edge that doesn't scale also doesn't animate noise for the most part of the animation. Eventually, the edge created by internal triangulation of the top face crosses the freestyle edge and makes the renderer divide it into multiple segments:

Rotation

Here you can see a default 32 segments sphere rotating by 360° around Z axis for 32 frames with linear interpolation, effectively producing the same geometry on each frame:

The noise is changing, but not for each edge on each frame, for example, the middle edge doesn't change when E1 passes through it. Sometimes the noise is reused, because apparently the input stays the same. I tried to deal with it by making the triangulation consistent, as well as duplicating the sphere and shrinkwrapping onto the one that doesn't rotate to make sure the vertices stay at the same spots (otherwise applying the transformation matrix could introduce some floating point inaccuracies), but still get this erratic behavior:

I even decided to remove the object rotation and just rotate vertices and then resort them (and I left the shrinkwrap to make sure the vertices are at the same positions):

import bpy, bmesh
from bpy import data as D

def main(C):
    for i in range(1, 33):
        C.scene.frame_set(i)
        dg = C.evaluated_depsgraph_get()
        bm = bmesh.new()
        bm.from_object(D.objects['Sphere'], dg)
        name = f"Frame{i}"
        me = D.meshes.new(name)
        bm.to_mesh(me)
        new_ob = D.objects.new(name, me)
        C.scene.collection.objects.link(new_ob)
        C.view_layer.objects.active = new_ob
        bpy.ops.object.mode_set(mode='EDIT')
        # I'm not sure if old position is used in sorting, so I'm sorting by two axes
        # probably not necessary and only one is needed
        bpy.ops.mesh.sort_elements(type='VIEW_ZAXIS', elements={'VERT', 'EDGE', 'FACE'})
        bpy.ops.mesh.sort_elements(type='VIEW_XAXIS', elements={'VERT', 'EDGE', 'FACE'})
        bpy.ops.object.mode_set(mode='OBJECT')
        new_ob.data.materials.append(D.materials['Material.001'])
        
        for frame, hidden in zip([i-1, i+1, i], [1, 1, 0]):
            for attr in ('hide_render', 'hide_viewport'):
                setattr(new_ob, attr, hidden)
                new_ob.keyframe_insert(data_path=attr, frame=frame)
            

class SimpleOperator(bpy.types.Operator):
    """Evaluate the object for each frame"""
    bl_idname = "object.eval_frames"
    bl_label = "Eval Frames"

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):
        main(context)
        return {'FINISHED'}

def menu_func(self, context):
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label)

# Register and add to the "object" menu (required to also use F3 search "Simple Object Operator" for quick access)
def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()

But still no dice:

Applying the shrinkwrap before sorting, removing the material, removing the UV map, and naming each rendered object frame (the same name, as random per object is based on the name) didn't help either.

So in general I wouldn't go for trying to produce consistent noise in an animated object using freestyle.

$\endgroup$
13
  • $\begingroup$ Interesting! My question was regarding how to avoid this effect. For me, it seems that Freestyle is automatically generating random noise even if the objects are staying still. $\endgroup$ May 20, 2022 at 17:50
  • $\begingroup$ I'm also interested to learn how the noise can be kept static if the objects are moving. $\endgroup$ May 20, 2022 at 17:51
  • $\begingroup$ I just build myself a proof of concept using Geonodes instead of Freestyle, but of course that has its limitations. $\endgroup$ May 20, 2022 at 17:51
  • $\begingroup$ @AnsonSavage see edit. $\endgroup$ May 21, 2022 at 17:22
  • $\begingroup$ Okay, very interesting! I'll try that out!! $\endgroup$ May 21, 2022 at 22:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .