17
$\begingroup$

I am trying to develop several shaders that need to take into account the position or orientation of another object in my scene. Seems like a generally useful thing to be able to do.

Here's an example of something that almost does what I want (for a simplified case). Here is a Cycles shader node tree that will shade an object green for points that are more than 1 unit from the origin and red if they are less than 1 unit from the origin.

Node tree

And here I'm moving a cube with the material close to the origin to see the effect.

enter image description here

Here is the crux of what I am looking for... Rather than having the origin be the source of the red colored region, I would like an Empty to be the center of the red colored region. And I would like to move the Empty around rather than the other objects to control the effect. Thanks!

The .blend file for my example can be found at...

http://readyposition.org/PositionBasedShader.blend

I can use a geometry node to get things like normal and position from the shaded object. But I'm unsure of how to get properties from the other object.

I have just started down the road of using drivers. I figured I could create a custom property on the shaded object and add a driver that sets it to the other object's properties. Then I'm hoping I can use an Attribute node to get at the custom property.

However, I'm at a loss how to best do this via the UI. In particular...

  • Can I add a custom property for a vector quantity (e.g. position)?
  • Can I create custom property groups so I can add drivers to x, y, and z at once (as the built in property groups for things like position, rotation, and scale have)?
  • Do I need to do x, y, and z separately and then convert them to vectors with a Combine XYZ node?
  • Can I even refer to a custom property via an Attribute node?
  • Do I need to resort to Python?
  • Can this be done at all or do I need to find some other way with multiple passes?

If someone can provide an extremely simple example for Cycles it would help me a lot. How about a shader for a UV Sphere where the sphere is green where it is within 1 blender unit of an Empty and red where it is more than 1 blender unit from the Empty? Can even just be in x to make things simpler.

$\endgroup$
3
  • 1
    $\begingroup$ Do you've tried animation nodes? $\endgroup$
    – p2or
    Feb 28, 2015 at 13:27
  • $\begingroup$ No, I haven't. Looks interesting. The documentation is a little thin. Can I include animation nodes in a Cycles material node tree? At first glance it looks like a new type of node tree. $\endgroup$
    – Chuck
    Feb 28, 2015 at 13:46
  • 1
    $\begingroup$ Yes that's possible. You can check my answer here: blender.stackexchange.com/questions/23417/… $\endgroup$
    – p2or
    Feb 28, 2015 at 13:57

4 Answers 4

11
$\begingroup$

As you have said you can use drivers were you get X,Y and Z separately from using drivers then combine them and use the result :

here is one of the drivers :
enter image description here

and this is the what changed in the node setup :

enter image description here

and this is the result : I'm moving the Empty object 'which is not visible'

enter image description here

Note : i didn't use driver directly on the 'combine XYZ' because it was clipping negative values

$\endgroup$
1
  • 2
    $\begingroup$ Awesome. I was overcomplicating things by trying to drive custom properties. Driving value nodes makes so much more sense. Thanks. Also thanks to @poor for spending so much time and showing us cool stuff. $\endgroup$
    – Chuck
    Mar 1, 2015 at 19:23
9
$\begingroup$

Since blender 2.67 you can write a custom pynode or use Animation Nodes as shown below:

enter image description here
In this example the material color is generated by the rotation of another object

enter image description here

enter image description here
In this example the color changes to green if the position value x of the empty is greater than 1

enter image description here

Note: Enable cycles first to access the color of the diffuse shader and use a expression node to get the desired and correct color values.

$\endgroup$
9
  • $\begingroup$ Thanks. The example shows, I believe, an animation node tree (new node tree type w/ the plugin) changing a property on another object. Cool. But can you use this in a shader node tree so the Cycles shader can calculate values per ray intersection? Also trying to see if pynode lets you add new node types to a Cycles shader node tree. Docs are thin. $\endgroup$
    – Chuck
    Feb 28, 2015 at 14:37
  • $\begingroup$ I think this blender.stackexchange.com/a/558/9651 explains that these types of nodes might not do what I want. Maybe it is out of date. $\endgroup$
    – Chuck
    Feb 28, 2015 at 14:40
  • $\begingroup$ @Chuck You can use the values for the input soket of your choice. See cycles material output and select the node and input soket in your current material node setup. In which cycles node you wish to plug the values? $\endgroup$
    – p2or
    Feb 28, 2015 at 14:59
  • $\begingroup$ Nice supporting graphics for the answer, thanks! $\endgroup$
    – Sabuncu
    Feb 28, 2015 at 15:04
  • $\begingroup$ I think we might have a slight disconnect on use case here. A really simple example in Cycles would help me a lot. How about a shader for a UV Sphere where the sphere is green where it is within 1 blender unit of an Empty and red where it is more than 1 blender unit from the Empty? $\endgroup$
    – Chuck
    Feb 28, 2015 at 18:08
1
$\begingroup$

You can get the position of an unscaled, unrotated object through the use of object texture coordinates. It's unnecessary to use Python of any kind or any addons.

enter image description here

It's worth thinking about what object coordinates are. For an unscaled, unrotated object, they are simply the world space coordinates of the sample, minus the world space coordinates of the object. By comparing the object coordinates of a translated object with an untranslated object, I can do just a little bit of math and figure out the world space location of the translated object. In this pic, I'm just comparing object location with sample position for visualization purposes.

What if you want position, but you need it from something you want to scale and rotate? Create an empty, parent it to your object, and set up world space limit rotation and limit scale constraints.

How can you measure the scale of an object? By comparing object coordinates of a scaled object with an unscaled object. Here, both objects need to have the same origin and rotation, but they can have different scales-- the scale of the object is just the scaled object coordinates divided by the unscaled object coordinates. Do you need to translate and rotate? Create a child with a limit world space rotation constraint (it will acquire identical translation just by virtue of being a child.)

I think you should be able to see where I'm going with the rotation. What is the Euler angle rotation of an object? Well-- the math on that one is harder than I feel like doing in nodes :) But you can create children to create basis vectors: one, one unit in local x from the object being measured; another, one unit in local y from the object being measured. By measuring the position of these empties, in the same manner as the first example, you can create a set of 3 basis vectors. And, if you want, you can compare them to your world's basis vectors (1,0,0; 0,1,0; 0,0,1) to create a set of Euler angles, if that's what you really want. (You rarely would, because Euler angles are awful for purposes of interpolation, and it's much easier to establish an axis-angle rotation.)

$\endgroup$
1
$\begingroup$

As a script

Make sure you have selected an object with a material, and the material contains a value node. This will create a custom property under the current scene, and drive the value node with the value of the current property.

import bpy

def main():
    ao = bpy.context.active_object
    if not ao:
        print("Select an object")
        return
        
    mat = bpy.context.object.active_material    
    if not mat:
        print("Add a material to the object")
        return
        
    value_node = mat.node_tree.nodes.get("Value")    
    if not value_node:
        print("Add a value node in current material")
        return
    
    driver = value_node.outputs[0].driver_add("default_value").driver
    driver.expression = "my_prop"
    
    var = driver.variables.new()    
    var.name = "my_prop"
    var.type = 'SINGLE_PROP'
    
    target = var.targets[0]
    target.id_type = 'SCENE'
    target.id = bpy.context.scene
    target.data_path = 'my_group.my_prop'
    

class MyPropertyGroup(bpy.types.PropertyGroup):
    my_prop : bpy.props.FloatProperty(default = 0.75)

def register():
    bpy.utils.register_class(MyPropertyGroup)
    bpy.types.Scene.my_group = bpy.props.PointerProperty(type=MyPropertyGroup)
    
def unregister():
    del bpy.types.Scene.my_group
    bpy.utils.unregister_class(MyPropertyGroup)
    
if __name__== "__main__":
    register()
    main()

Result :

enter image description here

$\endgroup$

You must log in to answer this question.

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