# Raycast-influenced Shapekeys

Are there any possible geometry node setups (even if scripts are needed) that may allow Raycast to trigger multiple shapekeys based on rays touching whatever faces/vertices each shapekey is influencing?

Contingency: Only if above requires vertex grouping, is there a way to convert/link shapekey influences into vertex groups? (don't need to require vertex groups, of course)

Goal: Autonomously dynamic shapekeys influenced by light direction.

Example: The ray reaches the left cheek of the face, activating the shapekey #1 (spikey cheek) and as the light reaches the ear it activates shapepkey #2 (spikey ear). As the light leaves both, it recedes the value of spikey ear and spikey cheek.

• That's a very abstract description.. can you give a more concrete example? Commented May 16, 2022 at 20:12
• The example edited in might help get things into perspective. Essentially, the question is simply: can shapekeys be influenced by the raycast geometry node? Commented May 16, 2022 at 21:53
• the short answer is: yes. But the question is: will it be performant? and that answer is: no, because you will get a field as result (e.g. is hit or hit position) and if you ask for the value in the output node via python script, this script will be executed n-times and so it will be laggy as hell. Commented May 17, 2022 at 7:16
• Ahh now I understand. So the possibility is out there, but one has to factor in the possible issue is performance and reliability. Dang. I was really hoping to get really innovative with this idea but didn't know where to start lol. Really appreciate the suitable insight my friend. Commented May 17, 2022 at 22:13

In this example the cube starts growing, when a ray hits it and it starts shrinking, when no ray hits it.

I first will explain, how to set this up. Afterwards I will talk about some performance considerations.

# How to set it up

We start by constructing the cube and add two shape keys to it:

Value 1 means small, Value 0 means tall.

Next, we need a light. And as we would like to use raycasting to check what is hit by the light, we need a source for the rays. We would like to have six rays on the edge of the cone of light and one in the center. Thus, we create a hexagon with a center point, place it at the light position and bring its normal in line with the light direction:

One could use a parallel projection and scale the hexagon to the right size, so that the rays will cover the cone of light at the desired distance. Alternatively, you could move the center point of the hexagon along its normal and thus create a cone of rays. When you adjust the angle correctly and scale the hexagon down, the cone of light will have the right size independent of the distance of the target.

After setting this up, we add an Empty and create a driver that transfers the rotation of the empty to the light and the hexagon. This way we can define an animation based on markers with the rotation of the empty.

Now let’s do the raycasting. We add the following Geometry Nodes setup to the cube:

The rays are casted from the hexagon, that we provide by the input parameter Lightplane. They are casted into the direction of the normals. The result of the raycasts is stored in the ID of the related point of the hexagon. Afterwards, we check all these points. If at least one has the ID 1, we set the output attribute shouldGrow to true and otherwise to false.

The output attribute has to be configured properly, so that the script below works. I would have liked to use the Instance domain for the output, but I did not find a way to read the result with python. Maybe someone else can help out here. Thus, I ended up in using the Face domain. Additionally it is important to provide an attribute name in the modifier panel.

What is left is, to change the value of our shape key based on the value of this output attribute. We do this with the following script:

import bpy

def grow(scene):
delta = 0.01
# get evaluated cube object
evaluated_object = bpy.data.objects["Cube"].evaluated_get(bpy.context.evaluated_depsgraph_get()).data
# read the first value in the shouldGrow output attribute of the object
should_grow = evaluated_object.attributes['shouldGrow'].data[0].value

if should_grow:
delta = delta * -1

# modify the shape key
current_value = bpy.data.shape_keys["Key"].key_blocks["Key 1"].value
bpy.data.shape_keys["Key"].key_blocks["Key 1"].value = current_value + delta

# register the above method, so that it is called before every change of frames
bpy.app.handlers.frame_change_pre.append(grow)

This script creates a function, that is called every time, when the frames change. The script evaluates the cube object, reads the shouldGrow output and modifies the shape key correspondingly. This script is executed exactly once per frame. To run the example, you first have to run the script, so that the function is registered. Then you can start the animation.

# Performance considerations

As long as you create something like a movie, performance should be good enough. But if you want to create an interactive solution, you will have to have a look at the computation time.

Before I go into detail: Different from what Chris has stated in his comment, the script will only be executed, when you call it. In the above example, the script will be called once per frame and not once per frame and some other n.

Nevertheless, the execution time of the node setup depends on two factors:

• number of rays
• number of faces of the target object

The above example takes 0.19 ms on my computer. It uses 7 ray casts and the target object has 34 faces.

First let’s have a look at the impact of the number of rays. Therefore, I created a setup, where I distribute points on the Lightplane and project them parallel onto the target. This is the result with 34 faces of the target object:

ray count computation time / ms
6 0.19
76 0.34
774 0.45
7700 2.80
77500 9.00
775000 73.00

As one can see, the ray count is proportional to the execution time for a high number of rays. But you won’t really need that much rays, I guess. So, one could see the number of rays as negligeable.

This, you can’t say about the number of faces. I used the Subdivide Mesh node to get the execution time for different numbers of faces of the target object. Here is the result:

face count computation time / ms
34 0.19
136 0.43
544 0.62
2200 0.93
8700 2.10
34800 7.00
139000 29

And same as the number of rays, the computation time is proportional to the number of faces of the target object. Having 6 rays and 139k faces, I get 29 ms on my computer, which means, you will have about 34 frames per second ignoring all other computation needs. If you go beyond, you will need more/better hardware or it will be laggy in an interactive solution.

• Very well said! I can get a good grasp of the foundation and can delve a bit further into your concept. Really have to thank you my friend. Although there are limitations, as the goal is to be interactive as your animate the model, the limits on what is and isn't possible can be measured and allows me to branch into a new perspective in the multi-faceted research overall! Commented May 20, 2022 at 22:17