# Render semi-transparent self-intersecting surface in cycles

I am looking at mesh surfaces that intersect themselves and show both sides (inside/outside). It's easier to put one here than to explain. So far, I can not cycles render it with transparency. It renders completely opaque for me. I'm toggling alt-z and it changes from semi-transparent to opaque in normal view (object mode) but always opaque in render. It is interesting to watch when toggling backface culling.

I give the shape here, because there may be some strange thing due to the variety of surface normal directions.

I don't want glass or refraction, I just want to see the surfaces themselves in some way that I can see inside.

NOTE: it DOES render semi-transparent in blender render. Using 2.74. I think I will have to play with multiple lights and diffuseness before it looks excellent.  import bpy
import numpy as np

nth, nph = 101, 200
th  = np.linspace(-0.5*np.pi, 0.5*np.pi, nth)
phi = np.linspace(-np.pi, np.pi, nph)
y3 = np.sin(th) - 1.8*np.sin(th)**3
X = np.cos(phi)[None,:] * np.cos(th)[:,None]
Y = np.sin(phi)[None,:] * np.cos(th)[:,None]
Z = np.zeros_like(X)            + y3[:,None]

verts = [tuple(thing) for thing in zip(X.flatten(), Y.flatten(), Z.flatten())]
faces = []
for ith in range(nth-1):
for iph in range(nph-1):
v1 = nph*ith + iph
v2 = v1 + 1
v3 = v2 + nph
v4 = v3 - 1
faces.append((v1, v2, v3, v4))

purple = bpy.data.materials.new("transPurple")
purple.diffuse_color = (1.0, 0.0, 0.67)
purple.alpha = 0.2
purple.use_transparency = True

me = bpy.data.meshes.new('wow')
ob = bpy.data.objects.new('wow', me)

ob.location = (0.0, 0.0, 2.0)
me.from_pydata(verts,[],faces)
ob.active_material = purple
ob.show_transparent = True
bpy.data.objects['wow'].select = False
bpy.data.objects['wow'].select = True

• To increase the clarity of your explanation please include screen snapshots of the rendered mesh. Please show your successful version and unsuccessful version. Annotations are welcome. Jun 9, 2015 at 14:07
• Oh, I didn't know I could do that! Thanks @atomicbezierslinger !!
– uhoh
Jun 9, 2015 at 14:27
• You mix a diffuse and transparent node in the material Jun 9, 2015 at 14:54
• I'm running/animating everything from the script @NoviceInDisguise. Mesh is changing shape and alpha, frame-by-frame, and (some day in the future) on a per-face basis. Are there script commands?
– uhoh
Jun 9, 2015 at 14:57
• uhoh - there's no scarcity economy here, ask as many unique questions as you want. Lumping them into one ongoing question really defeats the purpose of the site, and makes it difficult to find all these nuggets of information that get shared in the comments. Jun 9, 2015 at 16:55

two useful functions

import bmesh
import bpy

def set_smooth(ob):
mesh = ob.data
smooth_states = [True] * len(mesh.polygons)
mesh.polygons.foreach_set('use_smooth', smooth_states)
mesh.update()

def remove_doubles(ob, d=0.00001):
bm = bmesh.new()
bm.from_mesh(ob.data)
bmesh.ops.remove_doubles(bm, verts=bm.verts[:], dist=d)
bm.to_mesh(ob.data)
bm.free()


Those two functions aren't really meant for real-time stuff, but neither is bpy.ops stuff. They can however be integrated in a key-framed and rendered animation, no problem.

import bmesh
import bpy
import numpy as np

# set_smooth() and remove_doubles() are declared elsewhere

nth, nph = 101, 200
th  = np.linspace(-0.5*np.pi, 0.5*np.pi, nth)
phi = np.linspace(-np.pi, np.pi, nph)
y3 = np.sin(th) - 1.8*np.sin(th)**3
X = np.cos(phi)[None,:] * np.cos(th)[:,None]
Y = np.sin(phi)[None,:] * np.cos(th)[:,None]
Z = np.zeros_like(X)            + y3[:,None]

verts = [tuple(thing) for thing in zip(X.flatten(), Y.flatten(), Z.flatten())]
faces = []
for ith in range(nth-1):
for iph in range(nph-1):
v1 = nph*ith + iph
v2 = v1 + 1
v3 = v2 + nph
v4 = v3 - 1
faces.append((v1, v2, v3, v4))

me = bpy.data.meshes.new('wow')
me.from_pydata(verts,[],faces)
ob = bpy.data.objects.new('wow', me)
ob.location = (0.0, 0.0, 2.0)
set_smooth(ob)
remove_doubles(ob)


setting up a cycles material via script is a little bit involved, and you might ask yourself, how much of this should I be doing via script or manually setting up the material and only adjust certain parameters via script at render time..

import bpy

scene = bpy.context.scene
scene.render.engine = 'CYCLES'

my_mat = bpy.data.materials.new('cool_mat')
my_mat.use_nodes = True
nodes = my_mat.node_tree.nodes

# material output and diffuse nodes are present already, get them by name
mat_output = nodes.get("Material Output")
diffuse = nodes.get("Diffuse BSDF") # new(type='ShaderNodeBsdfDiffuse')
# mix and transparent are added by the script

mat_output.location = 300, 0
mix.location = 0, 0
diffuse.location = -200, 50
transp.location = -200, -100

b = mat_output.inputs['Surface']

a = diffuse.outputs['BSDF']
b = mix.inputs  # 'Shader' is used twice, so you access via index.

a = transp.outputs['BSDF']
b = mix.inputs

mix.inputs.default_value = 0.8  # factor
diffuse.inputs['Color'].default_value = [0.8, 0.0, 0.191713, 1.0]
transp.inputs['Color'].default_value = [1.0, 0.152801, 0.582658, 1.0]


which combined, gives something like this: I suspect a node setup like this might be a little more interesting: (note: I have ambient occlusion on!) The idea is to make the color or factor a function of the cross product between surface normal and the up vector (0,0,z). Using the nodes allows you to embrace vector mathematics for the adjustment of the shader at local points.

or taking the viewing angle into account too: And notice there is a backfacing output on the Geometry node, worth exploring

• Sweet! This is incredible @zeffii - there is a huge amount of stuff here to learn! Thank you! (I wish I could vote more than once on this answer)
– uhoh
Jun 9, 2015 at 23:29
• OK now I see that I need to read in depth about shaders. That seems to be where the optics is implemented. Thank you @zeffii for taking the time to show how to build nodes from script, and I can understand why sometimes drawing them can be faster.
– uhoh
Jun 10, 2015 at 9:21
• yeah, they can be imported from other files if you find a nice combo and want to reuse Jun 10, 2015 at 9:23