2
$\begingroup$

I need to know the volume of the shadow cast by an object by a light or at least the coordinates of the contour of the shadow in the floor. I thought that it would be possible to get the coordinates from the rendering, as it must calculate them to generate the shadow. Im new to both python and blender so I'm not sure if it's posible.

I found a diferent methode with the api in this question Using Blender to investigate the cast of shadows from an object onto a specific point in space , but i'm wondering if its posible to extract the information directly from the rendering.

$\endgroup$
0
$\begingroup$

I haven't been able to extract the data directly from the render, but i have been able to put together a code from different sources that works. I'm posting it here in case somebody finds it useful.

import bpy
from mathutils import Vector, Matrix
import random

# In case we wabnt to asign a random color to the shadow
def get_random_color():
    ''' generate rgb using a list comprehension '''
    r, g, b = [random.random() for i in range(3)]
    return r, g, b

#We create the material for the shadow and assignt transparency
mat_shadow=bpy.data.materials.new('shadow')
#mat_shadow.diffuse_color=get_random_color()
mat_shadow.diffuse_color=(0.01,0.01,0.01)
mat_shadow.alpha=0.5      
mat_shadow.use_transparency = True        

#From this point we assign the objects we are gonna work with
#We assign the light, a point in this case
LightObj = bpy.data.objects['Point']
light_mx = LightObj.matrix_world
light_location = Vector((light_mx[0][3], light_mx[1][3], light_mx[2][3]))

#We select the surface we want the shadow 
Surface_Object = bpy.data.objects['Plane']
surface_mx = Surface_Object.matrix_world
imx=surface_mx.inverted()

#we select the test objects
test_names = [ 'Cube','Suzanne']
test_objs = [bpy.data.objects[name] for name in test_names]
test_mxs = [ob.matrix_world for ob in test_objs]

#We loop througth the objects to create the shadows
for ob, mx in zip(test_objs, test_mxs):
    pol_accepted=[]
    shadow_mesh=[]
    shadow_object=[]
    cont=0
    #We loop through all the faces of the object
    for poly in ob.data.polygons:     
        Loca_hit=[] # Where the ray hit
        Vertice_hit=[] #if vertice hits we save it here
        hits=0
        #We loop through the vertices of the face
        for indx in poly.vertices:
            i=indx #We save the indx in a variable, if not it will change to -1 when used 
            P0=imx * light_location
            P1=imx *mx*ob.data.vertices[i].co
            #We check that the object it's not higher than the light
            #We should check in a more general case, that the point of the light it's farther than the point of the object
            if P0[2]>P1[2]:
                #We use the equation of the line that goes through two points to select the point of the line that
                #goes through P0 and P1, bellow the plane
                d=P1-P0
                n=(surface_mx[2][3]-1)/d[2]
                Pfin=P1+d*n
                re,loc, no, ind = Surface_Object.ray_cast(P0, Pfin,distance=1.70141e+10)
                if re:
                    hits += 1
                    Loca_hit.append(surface_mx *loc)
                    Vertice_hit.append(surface_mx *P1)
        #We only consider a face has a shadow if at least three points touch, the minimum to crerate a face
        if hits>=3:
            #We create the vertices with the hits and the original points, the faces to make a 3D mesh of each face shadow
            verts=Vertice_hit+Loca_hit
            f1=[n for n in range(hits)]
            f2=[n+hits for n in range(hits)]
            faces=[tuple(f1),tuple(f2),(0,hits,hits*2-1,hits-1)]
            for n in range(hits-1):
                faces.append((n,n+hits,n+1+hits,n+1))
            name='shad'+str(cont)
            shadow_mesh.append(bpy.data.meshes.new(name))
            shadow_object.append(bpy.data.objects.new(name,shadow_mesh[-1]))
            bpy.context.scene.objects.link(shadow_object[-1])

            shadow_mesh[-1].from_pydata(verts, [], faces)
            # Calculate the edges
            shadow_mesh[-1].update(calc_edges=True)


        cont += 1
    # We change the name of the first

    if len(shadow_object)!=0:
        shadow_object[0].name='Shadow_'+str(ob.name) #We change the name of the first shadow

        #We are going to join all the meshes together from the object
        scene = bpy.context.scene
        ctx = bpy.context.copy()
        # one of the objects to join
        ctx['active_object'] = shadow_object[0]
        ctx['selected_objects'] = shadow_object
        # we need the scene bases as well for joining
        ctx['selected_editable_bases'] = [scene.object_bases[ob.name] for ob in shadow_object]

        bpy.ops.object.join(ctx)

        # Assign material to object
        if shadow_object[0].data.materials:
            # assign to 1st material slot
            shadow_object[0].data.materials[0] = mat_shadow
        else:
            # no slots
            shadow_object[0].data.materials.append(mat_shadow)

        shadow_object[0].show_transparent = True 

enter image description hereenter image description here

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.