Problem
You have a character who you want to make it so that a texture or effect will only show in certain parts of the body, but you do not want to create a whole new grayscale texture to serve as a mask. For example, let’s say you want to create an effect where displacement occurs on their arms, but nowhere else.
Solution

Vertex groups! With vertex groups, you are able to assign a value between 0 and 1 to any given group of vertices.
In Blender, Black=0, and White=1 (most of the time), therefore you are using them to achieve the exact same effect as a black/white mask, but without having to generate a new image file!
Thinking about things in simple math terms is very helpful when playing around with shader effects.
There is also the added benefit that you already have the vertex groups if you’ve rigged your character. Another big benefit is that they will be normalized, meaning that even if they overlap, their value will never go over 1.
There is one major catch, however.
In Blender, as of writing this post (currently version 5.0.0), Vertex Groups cannot be used in the Shader nodes in EEVEE, but can be used without issue in Cycles.
There is a way to solve this though, and that’s with Geometry Nodes!
Why geometry nodes?
For some reason, when attempting to use the Attribute node in Shader Nodes in EEVEE, it cannot directly call the vertex groups in your mesh. It’s something that supposedly has to do with how the vertex groups are referenced internally. However, when you create a Geometry Nodes setup that calls on those vertex groups, you can pass along their influence information to a new attribute that will then be readable by both EEVEE and Cycles! Here’s how we can set that up:
Displacement texture example


For this example, we want to make it so that the displacement texture for muscle striations will affect only one area, and that the effect will be driven by a property, in this case, the scale of a bone. On the left, we have the vertex group have no effect, and on the right, we have the vertex group take effect to reveal the texture.

Here is a zoomed in portion of the texture we’re trying to reveal. The gray parts correspond to a value of 0.5, while the darker parts correspond to lower and lower values. When connected to Bump and Displacement nodes, darker values displace inwards, and lighter values displace outwards.
The goal here is for us to reveal parts of the texture gradually so that we cause muscular striations only in specific parts by either no strength, or maximum strength.
Geometry nodes setup: Passing data through

First, go on Geometry Nodes and create a new setup. You will need the Named Attribute, Math Multiply and Math Add nodes.

Now, you will type a vertex group name and select it. Note how it says points and float. That means this will be a float value that is determined per point, rather than a set value for all points. In Geometry Nodes, points mean vertices. So each vertex in the group can have a different value here.
Then, you connect the Attribute output to Multiply, and that to Add. If you want the value of the vertex group to be unchanged, change the second Multiply field to 1, and the second Add field to 0.
What we have here is: DEF.Shoulder * 0.5 + 0.5

Next, you will use the Store Named Attribute nodes and connect the Geometry Group Input into it, and the Geometry output into the Geometry Group Output.
Then, you can give your new attribute a new name. I’ve chosen StriationExample here.

This is the basic setup for how I take vertex group data and pass it to a new attribute that can be used within Shader Nodes.
If you go into the Shader Nodes now and use the Attribute node, you’ll be able to type StriationExample, and the attribute will be usable in both EEVEE and Cycles.
Geometry nodes setup: Driving values
You’re probably wondering why have those math nodes if we didn’t even do anything with them. They exist so we can use drivers!
For my Mr. Animal series, in the case of this post Mr. Behemoth, all of the muscle striation visibility is driven by the scale of the growth bones.


So now let’s look on what the left pectoral vertex groups look like, and what we can do with them.

Here, the left pectoral is composed of three vertex groups, DEF.PecUpper_L, DEF.PecMid_L, and DEF.PecLow_L. What we need to do here is to add these values together, then we need to connect them to a Math Multiply node, where the second socket is where we’ll put in a driver.

If we right click to see the driver, we’ll see a couple things:
- It’s looking at MCH.GrowPec_L.
- It’s using a single property, in the case of this particular driver, the scale of the Z axis of that bone.
- It’s using a scripted expression, where it’s taking scale, and subtracting by 1.
The result of this is that when MCH.GrowPec_L has a scale of 1 (i.e. at rest), the value this driver outputs is 0. This is because it’s taking the three vertex groups we used before, adding them, and then multiplying them by 1 – 1.
So now that we have this value on the second field in the Multiply node, what we’re getting is:
Combined vertex group value * (1 – 1) = 0.
However, when we scale that bone by 2, we get:
Combined vertex group value * (2 – 1) = Original combined vertex group value.
If we instead scale the bone up by 1.5, we would then get half of the strength of that vertex group!
It’s super important here to remember that all of these values are being calculated on a per-point basis.
For example, let’s look at the left shoulder again.

See the white dot at the very center of the shoulder? That specific vertex has a value of 0.531 for the DEF.Shoulder_L vertex group. That means if we did the same as what we did above, when scaling the bone to 2, we’d get a value of 0.531.
And here, the left pectoral.

Note how the selected vertex at the very center has values of 0.592, 0.225, and 0.114 for the groups DEF.PecMid_L, DEF.PecUpper_L, and DEF.PecLow_L respectively. When we combine them in Geometry Nodes with those math nodes, we end up with that specific vertex having a value of 0.931.

*Note about drivers: You can very easily get a new driver by going to the property you want to act as the driver, and right click selecting “Copy as New Driver.” Then you can go in any field you want to drive and right click select “Paste driver.” This is how I created all of the drivers for this effect.
Geometry nodes setup: Driving multiple values
Now that we know how to retrieve vertex group data, add it together, and drive it by a property, we also need to make it so that we put all of that information into that single Named Attribute that will output from Geometry Nodes.
It’s actually simpler than it might seem, because all we need to do is to keep adding!




And adding, and adding, and adding until we converge into a single math node that we connect to the Value input of the Store Named Attribute node. By doing this, we’ve taken all the calculations in each point that is affected by the vertex groups, and output a combined float that will be captured by a named attribute. Whatever name we give it here in Store Named Attribute will be what we’ll use in Shader Nodes.
This is one of the biggest reasons why I like vertex groups for this. When we add them all together, their values will always be, at most, 1. That means you get really predictable results.
Shader nodes: Accessing an attribute
Now you’ll be able to use the Stored Name Attribute as an attribute in the Attribute node in Shader Nodes.

Here are the things you’ll need for this particular effect:
- An Attribute node with the name you chose in Geometry Nodes.
- A Math Multiply node.
- And then whatever textures you want to reveal or hide, as well as whatever corresponding nodes they need, in our case being Bump and Displacement.
For this one, we connected the Factor of our attribute into a Multiply node so that on the second field, we’d be able to define a value that we’d be multiplying by, and then connecting that to the distance and scale inputs in the Bump and Displacement nodes respectively, since we want the displacement texture to only show in the areas we’re calculating a positive value for.
Distance and height values are generally best kept fairly low, between 0.01 and 0.05. It’s also important to remember that true displacement requires a highly sudbivided mesh. If you make the values really high, it’ll just stretch out the mesh too much and look ugly, so keep them fairly low.

For Bump nodes, we need them to come after the Normal Map, since they act on top of it. Then that node connects to the Normal input of the Principled BSDF node.

For Displacement, we need that to connect to Displacement input of the Material Output.
Result

With all of that set up, now we have:
- A texture that we can control the total distance of the displacement.
- A texture that is driven by bones.
- A texture that only appears in specific parts of the mesh.
- An effect that works in both EEVEE and Cycles.
If this is all too heady and difficult to parse just from reading this, you can also download Mr. Behemoth and check out his Geometry Nodes and Shader Nodes tabs!
Any questions, feel free to post in the replies, DM me on Bluesky, or contact my email!
Leave a Reply