1
$\begingroup$

I have plotted a series of blue and red spheres to replicate an atomic structure.

I would like the blue and red spheres to remain distinct; such that when I rotate the model, and the spheres overlap, the colours stay blue and red.

The problem I am facing is that the colours mix where the spheres overlap.

E.g) Plotting Blue and Red spheres. I want the spheres to remain blue of red, however when I move the camera, the overlapping spheres appear pink at the intersection.

blue and red spheres ovelap to give purple. I want only red and blue to show How do I stop the colours from mixing into this purple-ish tone and keep my spheres red and blue?

See my source code below for more information:


import bpy
import numpy as np
 
sizes = { 'In' : 0.7, 'Ga' : 0.7 }
colors = { 'In' : (0.0, 0.0, 255.0, 0.3), 'Ga' : (255.0, 0.4, 0.4, 0.7), 'bond': (0.05, 0.05, 0.05 , 0.1) }
             
for key in colors.keys():
    bpy.data.materials.new(name=key)
    bpy.data.materials[key].diffuse_color = colors[key]
    bpy.data.materials[key].specular_intensity = 0.1
 
def distance(a, b):
    return np.sqrt(np.dot(a - b, a - b))
 
def normalize(a):
    return np.array(a) / np.sqrt(np.dot(a, a))
 
class Structure:
    def __init__(self, filename):
        with open(filename, 'r') as f:
            data = f.readlines()
         
        self.n_atoms = int(data[0].split()[0])
         
        self.atom_list = [line.split()[0] for line in data[2:]]
         
        coordinates = []
         
        for i, line in enumerate(data[2:]):
            coordinates.append([float(value) for value in line.split()[1:4]])
         
        self.coordinates = np.array(coordinates)
        self.bonds = set()
     
    def add_bonds(self, atom_types, cutoff):
        for i in range(self.n_atoms - 1):
            position_1 = self.coordinates[i]
            element_1 = self.atom_list[i]
             
            if not element_1 in atom_types:
                continue
             
            for j in range(i + 1, self.n_atoms):
                position_2 = self.coordinates[j]
                element_2 = self.atom_list[j]
                 
                if not element_2 in atom_types:
                    continue
                 
                dist = distance(position_1, position_2)
                if dist <= cutoff:
                    self.bonds.add((i, j))
     
    def draw_bonds(self):
        for atom_1, atom_2 in self.bonds:
            pos_1 = self.coordinates[atom_1]
            pos_2 = self.coordinates[atom_2]
             
            difference = pos_2 - pos_1
            center = (pos_2 + pos_1) / 2.0
            magnitude = distance(pos_1, pos_2)
             
            bond_direction = normalize(difference)
            vertical = np.array((0.0, 0.0, 1.0))
            rotation_axis = np.cross(bond_direction, vertical)
            angle = -np.arccos(np.dot(bond_direction, vertical))
     
            bpy.ops.mesh.primitive_cylinder_add(radius=0.1, 
                                                depth=magnitude, 
                                                location=center)
            bpy.context.active_object.data.materials.append(bpy.data.materials['bond'])
            bpy.ops.object.shade_smooth()
            bpy.ops.transform.rotate(value=angle, axis=rotation_axis)
     
    def draw_atoms(self):
        for element, position in zip(self.atom_list, self.coordinates):
            bpy.ops.mesh.primitive_uv_sphere_add(radius=sizes[element], location=position)
            bpy.context.active_object.data.materials.append(bpy.data.materials[element])
            bpy.ops.object.shade_smooth()    
     
    def draw_structure(self):
        self.draw_atoms()
#        self.draw_bonds()
 
InGaN = Structure(r'\8x8x5-xIn=0.5.xyz')
InGaN.draw_structure()

$\endgroup$
1
  • $\begingroup$ Does it also happen with material alpha 1? Can you please add a sample data file? See blender.stackexchange.com/questions/55965/… re copying object rather than multiple create primitive operator calls. $\endgroup$
    – batFINGER
    Commented Jun 10, 2020 at 13:28

2 Answers 2

2
$\begingroup$

Try changing the "color" line to:

colors = { 'In' : (0.0, 0.0, 255.0, 1.0), 'Ga' : (255.0, 0.4, 0.4, 1.0), 'bond': (0.05, 0.05, 0.05 , 1.0) }
$\endgroup$
1
  • $\begingroup$ This did the trick! Thanks for the one-line fix :) $\endgroup$
    – hkh
    Commented Jun 10, 2020 at 15:04
1
$\begingroup$

The materials you create in your script seem to be translucent, as you can clearly make out the contours of what is behind individual balls (hence the "color-blending" effect resulting in this pinkish color).

I do not know why this is the case, since the API clearly states that the default blend mode is set to OPAQUE when creating a new material via scripting.

My suggestion would be to create your necessary materials manually with Blender's powerful Node-Editor to get the desired result.

If you need an amount of different materials that would be unfeasible to create individually by hand, start out with one handmade non-translucent "Base"-material and copy it via scripting and just modify it's diffuse color every time you copy it.

$\endgroup$

You must log in to answer this question.

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