4

I think that Blender might do what I have in mind, but I am not a Blender and Python guru, so I am looking for directions.

I need to determine if a point in space is obscured by an object, i.e. does it cast the shadow over it (there is one source of light, the sun). This has to be done for:

  • many different objects of different shapes but of similar size: they are fairly simple with just a few surfaces (e.g. a collection of cubes of slightly different sizes and orientations). Each is stored in one OBJ, so there are many OBJs. The analysis should be done one by one.
  • a collection of many points that need to be tested, each has different 3D coordinates. For each point it should be determined if a shadow is cast over it or not, so the result is binary.
  • for a different position of the sun (e.g. 100-200), to mimic the variable position of the sun during the day and throughout the year.

I am interested in two things: how to automate this, and is there a function in Blender that determines if a point is obscured or not.

What I have so far is shown below. For the simplicity of the approach let's assume that all points that need to be tested have the same Z coordinate (i.e. they are planar).

for each OBJ:
    +Load the OBJ
    +Place a plane at the Z value of the points to be tested
    for each position of the sun:
            +Render the image so only the plane with the extent of the shadow is visible
            (option Cast Only, and the camera is orthogonal and has a top view.)
            +Export the image
            +Import the image with an image processing library in Python
            for each point to be tested is a shadow cast over it:
                            +Find the pixel in the image that corresponds to
                            the position of the point
                            if the pixel value at that point is dark:
                                   it is obscured
                            if not:
                                   it is not obscured

Is there a better method for this? Please note that the points might have different Z coordinates, so my method is not optimal (it would require multiple planes).

flotr
  • 162
  • 10

2 Answers2

5

I would like to point out that the blender python has an API for ray_casting objects so you don't have to manually loop over all the faces of the object your self.

http://www.blender.org/api/blender_python_api_2_63_8/bpy.types.Object.html?highlight=object.ray_cast#bpy.types.Object.ray_cast

import bpy
from mathutils import Vector, Matrix

SunObj = bpy.data.objects['Sun']
sun_mx = SunObj.matrix_world

Surface_Object = bpy.data.objects['Test Points']
surface_mx = Surface_Object.matrix_world
for v in Surface_Object.data.vertices:
    v.select = False

#here some simple test objects
test_names = ['Torus', 'Suzanne', 'Cone']
test_objs = [bpy.data.objects[name] for name in test_names]
test_mxs = [ob.matrix_world for ob in test_objs]

#we get the sun location from the world matrix
sun_location = Vector((sun_mx[0][3], sun_mx[1][3], sun_mx[2][3]))

#Test test points are stored in object data
test_points = [surface_mx * v.co for v in Surface_Object.data.vertices]

hits = 0
for ob, mx in zip(test_objs, test_mxs):

    imx = mx.inverted() #ray_cast happens in local space
    for i, pt in enumerate(test_points):
        loc, no, indx = ob.ray_cast(imx * sun_location, imx * pt)

        #simulate shadow with selection of the test vert
        if indx != -1:
            hits += 1
            Surface_Object.data.vertices[i].select = True

print(hits) 

a sample blend https://www.dropbox.com/s/004eu6aaqxg6zxy/ray_cast_sun.blend?dl=0

enter image description here

Also works on non planar points

patmo141
  • 777
  • 7
  • 16
4

You dont need to render any images or setup any planes. This is what you should do:

for each OBJ:
    +Load the OBJ

for each position of sun:
    for each point to be tested:
         +Create a ray starting at point with direction of sun (sun's local Z axis)
         +Test if ray intersects any of OBJs, if yes:
             it is obscured
         if not:
             it is not obscured

sun's local Z axis you get from up_vector=(0,0,1) and sun's world_matrix:

from mathutils import Vector
up = Vector((0,0,1))    # global Z vector
up.rotate(bpy.context.scene.objects['SUN_NAME_HERE'].matrix_world)
# up is now sun's local Z axis - the ray direction

The part Test if line intersects any of OBJs is done by looping over all OBJs triangles and testing mathutils.geometry.intersect_ray_tri() function.

If your object have lots of triangles test the OBJs bounding box first.

Jaroslav Jerryno Novotny
  • 51,077
  • 7
  • 129
  • 218
  • Thanks. I didn't expect it to be so simple. I will vote your answer after I get some reputation. – flotr Dec 24 '14 at 08:40
  • 1
    I fixed the api intersect call, it was old 2.49 api. This will work in current blender. – Jaroslav Jerryno Novotny Dec 25 '14 at 11:42
  • I have started to implement this, and I am stuck at the mathutils function: I have managed to convert each triangle from the OBJ to the Vector type, but I do not know how to do the same for the ray of the sun. – flotr Dec 25 '14 at 22:16
  • 1
    point_loc = mathutils.Vector((x,y,z)) where x,y,z are coords of point to be tested. sun_loc = sun.location where sun is bpy.data.objects['YOUR_SUN_NAME']. Then ray is (sun_loc - point_loc) and ray origin is point_loc – Jaroslav Jerryno Novotny Dec 25 '14 at 22:37
  • Thanks, but as far as I know when it comes to the sun, the sun.location is irrelevant because the location of a lamp of the type Sun does not affect the rendered result. Only the rotation is important (which I do not know how to use here). – flotr Dec 26 '14 at 08:01
  • 1
    Oh sure, i forgot that your rays are parallel. The direction of sun is its local Z axis. Updated the answer with how to get it. – Jaroslav Jerryno Novotny Dec 26 '14 at 10:28
  • Jerryno, thank you very much for your prompt answer. I've also up-voted your answer now that I have enough reputation :) – flotr Dec 26 '14 at 10:33
  • Jerryno, I have finally implemented the algorithm. It works, but not completely as I had it in mind, because the computations go "beyond" the point to be tested. For instance, when a point is above a plane and between the sun and the point there is nothing, the function returns an "obstruction" (i.e. an intersection), since the ray "continues" to the plane. Do you know how to solve this, and to "stop" the ray at the point to be tested without going beyond it? I can compare the height values of the point and the intersection, but I am sure that there is a better solution. – flotr Dec 29 '14 at 21:51
  • 1
    I see, consider 2 vectors point-intersection and ray. If their dot product is greater than 0 they have the same orientation. If its < 0 the intersection was a false positive then. http://www.blender.org/api/blender_python_api_2_60_1/mathutils.html?highlight=vector.dot#mathutils.Vector.dot – Jaroslav Jerryno Novotny Dec 29 '14 at 22:05
  • 1
    Vectors have same oriented direction if their cross product is zero and dot product is greater than zero. There's no need to test the direction (the vectors are on a line), so the orientation test should be enough. – Jaroslav Jerryno Novotny Dec 29 '14 at 22:09
  • Jerryno, I only have a question about the statement "up.rotate(bpy.context.scene.objects['SUN_NAME_HERE'].matrix_world)". It seems to be location-dependent. If I move the sun to another location (which is irrelevant since we are dealing with the sun, and the ray remains the same), the values of "up" change. Further, I have made some detailed tests and it seems that the visibility test is wrong in some cases. Do you know if this statement is correct? – flotr Jan 01 '15 at 19:53
  • 1
    I am sure the rotate() func is not location dependent even if the input is world-matrix. It also takes Euler class which you can get with matrix_world.to_euler() but it will (should) yield the same result. Values of up will change everytime you call rotate() on it, it actually changes the vector forever. I am grasping at straws now, if you could post relevant parts of the code somewhere it would help – Jaroslav Jerryno Novotny Jan 01 '15 at 22:07
  • Jerryno, you are absolutely right. I am sorry, I had two odd bugs that misled me, which I have solved now. Thank you once again for your help. – flotr Jan 02 '15 at 16:43