3
$\begingroup$

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)

Black stripes

$\endgroup$
3

1 Answer 1

3
$\begingroup$

You can correct that by smoothing a bit the shape edges.

In that case, to do it:

Reverse the Bevel object (as if not, the final shape has inverted normals):

#Reversed indices to make the final shape correctly oriented
cross_section_points = 0.5*np.array([
    [-0.1, 0.1],
    [ 0.1, 0.1],
    [ 0.1,-0.1],
    [-0.1,-0.1]
])

Add a Bevel modifier to the "centerline" object:

# Bevel modifier
bevel_modifier = centerline.modifiers.new("Bevel", "BEVEL")
bevel_modifier.width = 0.005
bevel_modifier.segments = 3
bevel_modifier.limit_method = 'ANGLE'

Note: first though this was caustics and that should be (?). Though, this is not working here.

$\endgroup$
1
  • $\begingroup$ Thank you! It appears that sharp edges cause problems when rendering transparent objects. I didn't know that. $\endgroup$
    – Jan12
    Commented Nov 3, 2020 at 12:01

You must log in to answer this question.

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