Houdini & Procedural Workflows / Maya / Houdini / Python
Day 9 - Functions in Houdini with Python | Houdini 10 Day Introduction to Python
Written guide

Playing loads YouTube content. YouTube processes data after activation.
Watch on YouTubeIf the player is blocked or unavailable, use Watch on YouTube.
Turn repeated Python operations into a function, then use that function to assign texture files to a Houdini Principled Shader. Day 9 moves from small examples of arguments and return values to a practical shelf tool driven by a material selection and a texture folder.
The lesson builds on the earlier conditions and loops exercises. It uses Python 3 syntax, including f-strings, and a historical Principled Shader interface; the exact Houdini build is unverified. The captions explain the tool’s logic but do not preserve every pasted shader-parameter assignment. This guide reconstructs the supported workflow without inventing a complete material importer.
Defining a function does not call it
At 00:00, open the Python Shell to observe output and edit a shelf tool. A def statement creates a function; the indented body describes what will happen when it is called. Merely running a tool containing that definition need not print anything.
The recording’s greeting example can be normalized as:
def greet(name="Jim"):
print(f"Hello, {name}")
greet()
greet("Alice")
greet("Bob")
The calls print greetings for Jim, Alice and Bob. The default is used when no argument is supplied; the two explicit arguments customize the same operation. Leaving the indentation ends the function body, but does not itself run the function. The call expression does that. This corrects an imprecise spoken explanation around 01:00.
That distinction returns later when the material tool initially produces no file listing: its function has been defined but never called. Check for the call before debugging the function’s internal loop.
Return a result instead of relying on a local variable outside
At 04:00, the instructor creates a local message inside a function, then tries to print that name from outside. It is not available there in the example, producing an error. A function’s local names and the caller’s names are separate scopes.
At 05:01, replace the internal print with a return and assign the returned value in the caller:
def greeting(name):
message = f"Hello, {name}"
return message
text = greeting("Alice")
print(text)
Calling this function alone does not print the returned string in the shelf-tool execution context. Assigning it and printing it does. The caller can use a different variable name, as text does here; the return passes a value, not the local name itself.
The source describes this as encapsulation and loosely warns about globals. It should not be read as saying that an unrelated global name automatically overrides a function parameter. Python’s function documentation explains local bindings and argument passing; keeping inputs and outputs explicit makes the tool easier to reason about.
Choose positional and keyword arguments deliberately
At 07:01, the lesson replaces text with an addition example. Defaults and keyword calls can be written as:
def add(x=4, y=6):
return x + y
print(add()) # 10
print(add(y=10)) # 14
print(add(y=10, x=2)) # 12
print(add(2, 5)) # 7
print(add(2, y=20)) # 22
Positional arguments follow the parameter order. Keywords identify parameters by name, so their order can differ. For this ordinary call syntax, place positional arguments before keyword arguments: the source demonstrates that a call shaped like add(x=2, 20) is invalid. The last valid example above uses a small, explicit value to make the arithmetic easy to inspect; it is not an exact transcription of the instructor’s changing numeric entry.
The small Python examples here were checked as standalone Python. That check does not execute the Houdini shader tool.
Pass the material and texture folder into the tool
At 11:01, import hou for Houdini operations and os for directory access. Obtain the selected material node and a folder path, then pass both into an apply-textures function. SideFX documents hou.selectedNodes() as returning a tuple, although the recording calls it a list. Index zero selects one returned node, not every selected material.
For the demonstrated setup, select one compatible Principled Shader. An empty selection would make direct index-zero access fail; an unrelated selected node would not have the expected shader parameters. Selection alone does not establish material compatibility.
The first diagnostic is deliberately simple: loop through os.listdir(path) and print each returned name. At 14:01, the instructor remembers to call the function, after which the filenames appear. Confirm that listing before making any shader changes. If the folder cannot be read, or its files differ from the expected naming convention, the later assignments cannot work as intended.
The source promotes an optional texture pack through an affiliate link. Those particular purchased textures are not required for the programming exercise; the important dependency is a consistent, understood naming convention.
Match filenames, enable a texture, then assign its path
At 15:00, the folder contains several map types and multiple versions of a diffuse file. The tool filters for the intended JPEG rather than also choosing the .tx version. The instructor suggests a condition checking for a string such as diffuse.jpg within the filename.
That is a naming-rule match, not an inspection of image contents. Check the actual spelling and extension in your folder. A substring test also does not guarantee exactly one match: more than one matching name could cause later assignments to replace earlier ones. The recorded tool does not resolve such conflicts or define a complete file-validation policy.
For the base-color branch, enable the shader’s texture-use parameter and assign the path to its texture-file parameter. The recording builds a full path from the folder and filename. Hover over the shader controls to inspect their internal parameter names; visible labels alone are not necessarily the names used by Python.
At 17:01, similar branches are pasted for additional maps. The captions name roughness, glossiness, reflection, metalness and ambient occlusion, but do not reliably preserve the complete assignments or any required conversions. Roughness and glossiness should not be treated as interchangeable solely because their filenames match a condition. The Principled Shader reference is a separate parameter reference, not evidence that every pasted mapping in this recording has been verified.
Check normal, displacement and the rendered result separately
At 18:00, the normal and height branches also set effect scales. The instructor reduces a scale to about −0.01 for the demonstrated textures. This is a source-specific adjustment, not a general normal-map convention or safe displacement amount. The normal texture type is already selected in that scene; a script that assumes it will inherit that prerequisite.
At 19:00, switching to another folder and selected shader demonstrates the function’s reuse. The material preview is dark, so the instructor assigns the material to geometry, adds a skylight, creates a Sphere and applies a polar UV Project around 21:02. The render makes the difference between the two textures easier to see.
Separate three checks: filenames were found, the intended shader fields were assigned, and the material rendered plausibly on UV-mapped geometry. A successful parameter assignment does not prove correct color-space handling, normal orientation, displacement scale or a complete production material. Those broader checks remain outside this introductory tool. The next lesson adds a window around the same material-and-path function.