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.