# 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. – atomicbezierslinger Jun 9 '15 at 14:07
• Oh, I didn't know I could do that! Thanks @atomicbezierslinger !! – uhoh Jun 9 '15 at 14:27
• You mix a diffuse and transparent node in the material – VRM Jun 9 '15 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 '15 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. – zeffii Jun 9 '15 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[1]  # 'Shader' is used twice, so you access via index.

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

mix.inputs[0].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 '15 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 '15 at 9:21
• yeah, they can be imported from other files if you find a nice combo and want to reuse – zeffii Jun 10 '15 at 9:23