10

One of my favorite techniques in Blender is editing vertex normals to make stylized trees like in this article: link

The technique works in a nutshell like this:

  1. create leaves texture and model in shape of three quads
  2. create a blob and add particle system on it, use leaves object as source
  3. turn particles into independent objects, join it all into one object
  4. increase the size of the blob
  5. add data transfer modifier to the leaves and change vertex normals so they point outwards towards the blob

Relevant gif that outlines the steps

I'm trying to replicate this in geometry nodes of 3.0, but I got stuck on the last step. So, using geometry nodes 3.0, can I adjust vertex normals so they point outwards of the shape for this 'stylized shading'.

Dalibor-P
  • 1,034
  • 2
  • 11
  • 26

2 Answers2

9

This is a lovely technique.

The approach, here, is to capture the normals of the blob on which the leaves are instanced, and pass it out to the GN modifier, so it can be picked up by your shader.

enter image description here

This GN group takes the blob as an input, constructs the 3-plane leaves, and instances them on the blob surface. It exposes the scale and density of the leaves to the modifier as inputs, and passes out the normals of the original blob in the 'Normal' output field.

It also duplicates the blob, scaled down, as a further instancing surface, to increase the illusion of volume. Out in the modifier, I named the Normal output blob_normal, and passed it to this shader, via an Attribute node:

enter image description here

The individual UV mapping of the leaf-planes survive the GN treatment, and can be accessed through the named attribute uv_map. Here, that's used to make a procedural leaf-alpha-map for the planes, so their square edges are somewhat concealed. (You may be using an image-map; then the only relevant parts of this tree would be the nodes in yellow.)

enter image description here

(Blender 3.0 Candidate .. I didn't pack the world in the file. If you need one for lighting, you will have to fish out one of your own)

Robin Betts
  • 76,260
  • 8
  • 77
  • 190
  • Oops, really, you should expose the set material to the GN input, too, so you can use the group for green trees, yellow trees, whatever. I'm sure there are other tweaks you will be making for yourself. – Robin Betts Nov 28 '21 at 20:55
  • 1
    Thanks, I was just about to say it was impossible in comments lol :) Shame that we can't just output actual normals anymore though, GN feels like a constant game of one step forward, one step back. – Nathan Nov 28 '21 at 23:31
  • 2
    @Nathan Very frustrating .. We are right? There's no way to set actual UV maps or actual normals on the geometry? Just attributes to be picked up by the shader? Surely there must be plans to make that happen.. for export, and everything... – Robin Betts Nov 28 '21 at 23:56
  • 1
    Apart from providing solution to my question, you have also showed that UV maps DON'T get destroyed after using Realize instances node like I thought they were. I'll forward this to another question that a different person had a month ago. – Dalibor-P Nov 29 '21 at 16:30
  • @aphatetic_artist thanks! I've blown time away more than once figuring that kind of thing out for myself. It's not clear from the docs. Folks here are helpful, though. – Robin Betts Nov 29 '21 at 17:20
  • @RobinBetts Do you think that this look can be achieved in Cycles? – Dalibor-P Nov 29 '21 at 18:32
  • @a Still working on it.. I think your comment may have unearthed another error. – Robin Betts Nov 30 '21 at 17:11
  • In 3.2.2, this file just has zero normals for the custom attribute, disconnecting it in the Geometry Node tree or replacing it in the material with a zero vector has no effect on the final appearance, and it seems to look the same as the 3.0 render. Did this actually work, or is this effect just achieved by zeroing the normals? – Bloop Aug 21 '22 at 04:34
  • @RobinBetts Also, the Capture Attribute node is set to float, which leads me to believe this wasn't working in 3.0 the same way it doesn't in 3.2.2. If that is intentionally a float, could you explain why? – Bloop Aug 21 '22 at 04:42
  • 1
    @Bloop Spotted! Thank you! At first sight this part of the answer is complete rubbish. I think the attributr has to be transferred to be meaningful. Can't remember how it got here before posting. Attending to it – Robin Betts Aug 21 '22 at 08:31
1

While you can't edit vertex normals directly from Geometry Nodes can you still access the attribute from Python and use that to set your vertex normals.

Assuming you already have the same setup as in Robin Betts's answer:

  1. Enable Auto Smooth.
  2. Apply the Geometry Nodes modifier and all modifiers before it.
  3. Go to the Scripting Workspace.
  4. Create a new text file in the text editor on the right and paste the following in it:
import bpy

ob = bpy.context.object me = ob.data

normals = [] for v in me.attributes['name_of_your_normals_attribute'].data: normals.append(v.vector)

me.normals_split_custom_set_from_vertices(normals)

  1. Change name_of_your_normals_attribute to the name the appropriate Attribute Output from the geometry node modifier (blob_normal in Robin Betts's answer).
  2. With your object selected, click the play-button icon on the top of the text-editor to run the script (Shortcut: Alt + P).
  3. Click inside the 3D viewport to update it and see the result.

Limitation here is that this is destructive (aka, not a modifier) and requires the Geometry Nodes modifier (and all modifiers prior to it) to be applied. If exporting for a game engine or something similar this shouldn't be too much of an issue as long as you keep a back-up of the original object with the modifiers still in place.

I'm also aware this isn't exactly the answer to the question as it's not using geometry nodes to achieve this result. But I stumbled across the same problem, already aware of the solution Robin Betts gave (which is the correct way of doing things as long as your model stays inside Blender), but required the result to be exportable to a game engine. So this is the solution I ended up going with. If anybody knows a more elegant solution I would be glad to hear it.