1
$\begingroup$

I am trying to write a small Python script in Blender which should be able to detect whether a surface of a mesh is hit by the light or not.

I wrote this code based on some replies and comments. In the scene, you can see two cubes and a light (sun). Obviously, the ray hits first the larger cube, meaning that the smaller cube is shadow. I also get a correct result in terms of location of the hit. But how can I automatically tell that the smaller cube is shadowed? An idea could be to measure the distance between the source and the obstacle (larger cube), and since this is smaller than the distance between the source and the target (smaller cube), I can assume that the smaller cube is in shadow. However, I was just wondering if this makes sense, because I saw other answers where this approach of measuring the distance did not seem to be implemented (e.g. https://blender.stackexchange.com/a/269656/52670)

import bpy

bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(0, 0, 8), scale=(4, 4, 4))
bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(0, 0, 1), scale=(1, 1, 1))
bpy.ops.object.light_add(type='SUN', align='WORLD', location=(0,0, 40), scale=(1, 1, 1))

loc_origin=bpy.data.objects['Sun'].location
loc_target=bpy.data.objects['Cube.001'].location
direction = target-origin

scene = bpy.context.scene
depsgraph = bpy.context.evaluated_depsgraph_get()
result = scene.ray_cast(depsgraph, origin, direction)

print(result[0])
print(result[1])
$\endgroup$
6
  • 1
    $\begingroup$ Hello, could you elaborate upon your use case ? I think you should look into raycasting $\endgroup$
    – Gorgious
    Commented Jul 20, 2022 at 12:15
  • $\begingroup$ Hey. In my scene, I have a mesh (for the moment a simple cube) and a light object. When I go to "rendered" in viewport shading, I can see which surfaces of my cube are hit by the light, and which surfaces are shadowed. I would like to write a Python script that can detect automatically this. Ideally, the script should simply output for example "0" if the surface is shadowed, and "1" if the surface is illuminated. $\endgroup$
    – Alex Mekx
    Commented Jul 20, 2022 at 12:25
  • 2
    $\begingroup$ Are you using Eevee or Cycles (Or another engine) ? $\endgroup$
    – Gorgious
    Commented Jul 20, 2022 at 13:07
  • $\begingroup$ I can see that Eevee is selected as default engine. $\endgroup$
    – Alex Mekx
    Commented Jul 20, 2022 at 13:16
  • $\begingroup$ You answered what you'd want the detection to detect, but how are you going to apply it? That helps us ensure that a solution we give isn't overkill or not enough for your application. $\endgroup$ Commented Jul 25, 2022 at 12:31

1 Answer 1

2
+50
$\begingroup$

As suggested, I would recommend raytracing. I'd recommend the code recently shared in this answer as a place to start: https://blender.stackexchange.com/a/269656/52670 - instead of "occluded objects from the camera", you want "faces occluded from the location of the light." So, replace the camera's world location with the light's world location. Granted, this will only test visibility based on the position of the light, not the light itself. It is precise, but only as precise as the rays you give it.

The critical function - scene.ray_cast() - is documented here: https://docs.blender.org/api/current/bpy.types.Scene.html?highlight=ray_cast#bpy.types.Scene.ray_cast You will likely want the object as well as the face index that the function gives back to you:

faces_hit = [] # dictionary of objects and the faces that were hit

# within loop iterating over rays hitting object from light
is_hit, _, _, face_index, obj, _ = bpy.context.scene.ray_cast(bpy.context.view_layer.depsgraph, bpy.data.objects['My_Light'].location, dir)

if is_hit and obj.name = 'MyObjectName':
  faces_hit.append(face_index)

# after loop, use the face indices as you'd like
print('Was it hit:', is_hit)
print(face_index)
print(obj.name + 'was hit first')

Regarding your additional question:

But how can I automatically tell that the smaller cube is shadowed?

You answered your own question here:

Obviously, the ray hits first the larger cube, meaning that the smaller cube is [in] shadow.

Since you never hit the smaller cube, it's likely that it is in shadow. You can get which object is hit from the ray_cast function, as shown in my update code sample.

As to detect whether the cube is shadowed from all light rays of a given light - you just need to shoot more rays in more directions. Try shooting them toward each face, vertex, etc. Again, this is not taking the actual rendered lighting into account, but comparing distances and line intersections. And I edited my code sample to show that ray_cast tells you can see which object was hit :) If you shoot twenty rays and an object was never hit - it's likely that it is in shadow. This is an automatic solution, but its precision and accuracy is up to you.

The distance measuring is already done as part of the ray_cast - from the origin, it shoots a ray and determines what it hits first - anything behind that first object is not hit - and retrieves the data, (hit result (boolean), hit location, hit normal, index of face hit, object that was hit, hit object matrix), as shown in the documentation I linked.

$\endgroup$
3
  • 1
    $\begingroup$ Hey, I looked at your suggestions, and I tried to write a code (see edits in the question), but there is something not clear about how the ray_cast works. Regarding your questions, it is actually enough to export a 0-1 output. That is my final goal. I don't need to do anything else (at least in Blender). $\endgroup$
    – Alex Mekx
    Commented Jul 27, 2022 at 11:37
  • $\begingroup$ Okay, I just wanted to clarify, as additional application may change how the problem is solved. Updated my answer to your question. $\endgroup$ Commented Jul 27, 2022 at 15:26
  • 1
    $\begingroup$ Thanks a lot :) $\endgroup$
    – Alex Mekx
    Commented Jul 27, 2022 at 16:58

You must log in to answer this question.

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