I am quite new to Blender and I am trying to create a visualisation of my scientific results. I started by creating dummy data of the centreline and then used Bezier curve for interpolation. Then I assigned bevel to it (a simple square, defined by polyline) and thus created a 3D object (I also closed the caps). At least I hope it's a 3D object, I'm not sure if I should also use some fill function.
Nevertheless, I managed to create all this, put it in an environment, where I surround my object with walls and put in there a light. I assigned it a glass like material. When I render (cycles), my image has strange black stripes and I'm not sure how to fix. I have it all in a script which you can find bellow.
Of course, I don't mind if the solution is not in a Python script, I will manage to convert it. I would just like to understand why they are appearing and how to solve it.
import bpy
import numpy as np
# delete everything
print("\n\n\n\nNew run")
objs = [ob for ob in bpy.context.scene.objects]
bpy.ops.object.delete({"selected_objects": objs})
print("\n\n\n\n")
def dump(obj):
for attr in dir(obj):
if hasattr( obj, attr ):
print( "obj.%s = %s" % (attr, getattr(obj, attr)))
def polyline_from(coords, name, closed):
# create the Curve Datablock
curvedata = bpy.data.curves.new(name, type='CURVE')
curvedata.dimensions = '3D'
# map coords to spline
polyline = curvedata.splines.new('POLY')
polyline.points.add(len(coords)-1)
for i, coord in enumerate(coords):
x,y = coord
polyline.points[i].co = (x, y, 0, 1)
if(closed):
polyline.use_cyclic_u = True
# create Object
objectdata = bpy.data.objects.new(name+"Object", curvedata)
return objectdata
def curve_from(coords, name, closed, res=10):
# create the Curve Datablock
curvedata = bpy.data.curves.new(name, type='CURVE')
curvedata.dimensions = '3D'
curvedata.resolution_u = res
# map coords to spline
spline = curvedata.splines.new('BEZIER')
spline.bezier_points.add(len(coords)-1)
for i, p in enumerate(spline.bezier_points):
x,y,z = coords[i]
p.co = (x, y, z)
p.handle_left = (x, y, z)
p.handle_right = (x, y, z)
#Set automatic
p.handle_right_type = 'AUTO'
p.handle_left_type = 'AUTO'
if(closed):
spline.use_cyclic_u = True
# create Object
objectdata = bpy.data.objects.new(name+"Object", curvedata)
return objectdata
# create scene
scene = bpy.context.scene
scene.world.color = (0,0,0)
# select render engine
scene.render.engine = 'CYCLES'
# Set render resolution
scene.render.resolution_x = 1920
scene.render.resolution_y = 1080
# Generate fake data
def data(t):
x = np.linspace(0, 1, 10)
y = 0*x
z = x**2 * 0.2 * np.sin(t)
return np.array([x, y, z]).T
centerline_points = data(0)
# Cross section
cross_section_points = 0.5*np.array([
[-0.1,-0.1],
[ 0.1,-0.1],
[ 0.1, 0.1],
[-0.1, 0.1]
])
centerline = curve_from(
coords=centerline_points,
name="Centerline",
closed=False,
res=13
)
cross_section = polyline_from(
coords=cross_section_points,
name="CrossSection",
closed=True
)
# add bevel object
centerline.data.bevel_object = cross_section
centerline.data.use_fill_caps = True
# attach to scene and validate context
scene.collection.objects.link(centerline)
# clear all previous animation data
centerline.animation_data_clear()
# set first and last frame index
total_time = 2*np.pi # Animation should be 2*pi seconds long
fps = 10 # Frames per second (fps)
scene.frame_start = 0
scene.frame_end = int(total_time*fps)+1
# loop of frames and insert keyframes every 10th frame
keyframe_freq = 1
nlast = bpy.context.scene.frame_end
for n in range(nlast):
t = total_time*n/nlast
# Do computations
new_points = data(t)
# Check if n is a multiple of keyframe_freq
if n%keyframe_freq == 0:
# Set frame like this
scene.frame_set(n)
# Set current location like this
for (i, p) in enumerate(centerline.data.splines.active.bezier_points):
x,y,z = new_points[i]
p.co = (x,y,z)
p.keyframe_insert(data_path="co")
p.handle_left = (x, y, z)
p.keyframe_insert(data_path="handle_left")
p.handle_right = (x, y, z)
p.keyframe_insert(data_path="handle_right")
# create material
material = bpy.data.materials.new("Material")
centerline.active_material = material
material.use_nodes = True
material.node_tree.links.remove(material.node_tree.links[0])
material.node_tree.nodes.remove(material.node_tree.nodes['Principled BSDF'])
glass_node = material.node_tree.nodes.new("ShaderNodeBsdfGlass")
glass_node.inputs["Color"].default_value = (0.0, 0.8, 1.0, 1.0)
glass_node.inputs["Roughness"].default_value = 0.1
material.node_tree.links.new(material.node_tree.nodes["Material Output"].inputs["Surface"], glass_node.outputs["BSDF"])
# create plane
minx = -0.1
maxx = 1.1
miny = -0.25
maxy = 0.25
minz = -0.25
maxz = 0.25
bpy.ops.mesh.primitive_plane_add(size=30, location=(0.0,0.0,minz), rotation=(0.0,0.0,0.0))
basez1 = bpy.context.active_object
bpy.ops.mesh.primitive_plane_add(size=30, location=(0.0,0.0,15), rotation=(0.0,0.0,0.0))
basez2 = bpy.context.active_object
bpy.ops.mesh.primitive_plane_add(size=30, location=(0.0,-15,0.0), rotation=(-np.pi/2,0.0,0.0))
basey1 = bpy.context.active_object
bpy.ops.mesh.primitive_plane_add(size=30, location=(0.0,15,0.0), rotation=(np.pi/2,0.0,0.0))
basey2 = bpy.context.active_object
bpy.ops.mesh.primitive_plane_add(size=30, location=(-15,0.0,0.0), rotation=(0.0,-np.pi/2,0.0))
basex1 = bpy.context.active_object
bpy.ops.mesh.primitive_plane_add(size=30, location=(15,0.0,0.0), rotation=(0.0,np.pi/2,0.0))
basex2 = bpy.context.active_object
# create material
base_material = bpy.data.materials.new("BaseMaterial")
base_material.use_nodes = True
base_bsdf = base_material.node_tree.nodes["Principled BSDF"]
texture_image = base_material.node_tree.nodes.new('ShaderNodeTexImage')
texture_path = "/Users/jantomec/Downloads/fabric_pattern_05_8k_png/fabric_pattern_05_rough_8k.png"
texture_image.image = bpy.data.images.load(texture_path)
texture_image.texture_mapping.scale = (10,10,10)
base_material.node_tree.links.new(base_bsdf.inputs['Base Color'], texture_image.outputs['Color'])
basez1.active_material = base_material
basez2.active_material = base_material
basey1.active_material = base_material
basey2.active_material = base_material
basex1.active_material = base_material
basex2.active_material = base_material
# add light
# create light datablock, set attributes
light_data = bpy.data.lights.new(name="light_2.80", type='POINT')
light_data.energy = 10000
light_data.cycles.samples = 4
# create new object with our light datablock
light_object = bpy.data.objects.new(name="Light", object_data=light_data)
light_object.location = (7,-10,10)
# link light object
bpy.context.collection.objects.link(light_object)
# add camera
camera_data = bpy.data.cameras.new(name='Camera')
camera_object = bpy.data.objects.new('Camera', camera_data)
bpy.context.scene.collection.objects.link(camera_object)
# Set camera fov in degrees
camera_object.data.angle = 35*np.pi/180.0
# Set camera rotation in euler angles
xc = 1.3
yc = -3.0
zc = 0.75
midpoint = [(minx+maxx)/2, (miny+maxy)/2, (minz+maxz)/2]
camera_object.rotation_mode = 'XZY'
camera_object.rotation_euler[0] = np.pi/2
camera_object.rotation_euler[2] = np.arctan2(xc - midpoint[0], -(yc - midpoint[1]))
camera_object.rotation_euler[0] = np.pi/2 - np.arctan2(zc, np.sqrt((xc - midpoint[0])**2 * (yc-midpoint[1])**2)) + 0.07
# Set camera translation
camera_object.location = (xc, yc, zc)