Introduction to Arnold Plugin Development (Patreon)
Downloads
Content
I’ve been maintaining the Lentil-BHGC camera plugin for Arnold for a few years, so I thought it would be interesting to use that experience to write about the process of creating cross platform C++ plugins for Arnold.
We’ll look at how Arnold plugins are structured and how to set up a convenient development environment for them. All files discussed are available for download at the bottom of the article.
Let’s roll!
Software
For Arnold dev on Windows I use VS Code, Visual Studio Community 2022 and CMake. You’ll also need a version of the Arnold SDK. (The various Arnold DCC plugins won’t work for development. The SDK is required.)
Here are the links to download everything. If you don’t have an Arnold subscription you can apply for a trial that gives you access for one month.
VS Code: https://code.visualstudio.comVisual Studio: https://visualstudio.microsoft.com/vs/community/CMake: https://cmake.org/download/#latestArnold: https://www.autodesk.com/products/arnold/overviewWhen installing and configuring Visual Studio, make sure to select the Desktop Development with C++ workload.
VS Code has a ton of extensions to improve the C++ and CMake experience. I recommend:
C/C++ from MicrosoftCMake Tools from MicrosoftDevelopment Environment
I like to use a similar setup to the one I use when developing for Gaffer. The basic idea is to create a simple batch file that launches a Visual Studio Native Command Prompt, which then launches VS Code and opens your workspace. To start off I set up a project folder structure something like this:
Open up VS Code, select your top level folder and save a workspace.
Create a batch file similar to this:
The idea with the PATH initialization section is that we want to have only the bare minimum apps included in PATH. It’s very easy to blow things up if you have extraneous programs sneaking in. For example, you might have a CG app that comes with its own version of Python. If that app is in your PATH along with your actual Python install then you’re in for a lot of problems as your development environment unintentionally picks the “wrong” Python. Best to keep everything as locked down as possible, with only things you’re absolutely sure you need. A good starting place is to copy the default PATH in the Visual Studio terminal (since it includes all required development stuff), then prune further from there. (Type echo %PATH% to print the current PATH value.)
The last two lines launch the Visual Studio Prompt and run VS Code:
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"code C:\onedrive\main_projects\scenes\patreon\arnold_plugin\code\plugin.code-workspace
Run the batch file and VS Code will open with our workspace loaded.
There’s one more step before our environment is ready to go: we need to point VS Code to our Arnold SDK in order for VS Code’s syntax highlighting to work. Otherwise, everything we bring in from Arnold will have red squiggles and cause problems in VS Code (even if it compiles correctly). There are a few ways to do this, but one quick one is to open up c_cpp_properties.json (Ctrl-Shift-P > C/C++: Edit Configurations (JSON)) and add our Arnold include folder to includePath, like so:
That should do it, we’re ready to build!
Writing a Plugin
Let’s check out the Arnold docs and pick a sample plugin to build. The camera example is a good one to start with:
https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_dev_guide_plugins_av_Camera+Nodes_html
Copy the C++ code, paste it into a new file in VS Code and save it our src folder as camera.cpp. If all our configuration went well then VS Code should find ai.h in our Arnold SDK path and will provide syntax highlighting for everything.
You can mouse over Arnold functions to get a description of what they do, and Right Click > Go to Definition to jump to the Arnold code. Note that Arnold isn’t open source. We can see everything in the headers, but the actual function code is hidden.
Let’s look at how this camera plugin is structured. Arnold provides a bunch of conveniently-named entry points to Arnold’s inner workings, many of which are required for a functioning plugin. Here we have node_parameters, node_initialize, node_finish, and a few more. Each of these sections can host custom code that modifies Arnold’s behavior at that point in the scene and object lifecycle and rendering process.
First off, note that Arnold usually uses two prefixes for all its keywords: At and Ai. It’s not publicly confirmed, but the general consensus is that At stands for Arnold Type and Ai is Arnold Interface. That is, At indicates Arnold’s custom data types, such AtString, a wrapper for a string type, AtNode, a structure for describing Arnold nodes, etc. Ai indicates Arnold functions like AiCameraUpdate to update a camera or AiMax to get the maximum of two input values.
Also note the use of the term node. We can think of any Arnold plugin as a graph of nodes connected to each other, much like we’d set up a node tree in Houdini or Gaffer. Each node performs actions and influences the behavior of other connected nodes.
Let's look at what each section of code does:
AI_CAMERA_NODE_EXPORT_METHODS
Required custom name for any plugin.
enum
This first enum is unused and can be deleted. The person who wrote this example might have used it in an earlier version and didn’t get around to removing it.
struct MyCameraData
Creates a data type with a single custom float variable, tan_fov.
node_parameters
All the custom parameters we want our camera node to have. In this case it’s fov, set to a default value of 60.
node_initialize
This runs once when the camera node is first created. Note the highlighted node keyword that isn’t defined anywhere in our plugin code. This is one of Arnold’s default keywords and it refers to the current node. Think of it as meaning something like AiCameraInitialize(this).
node_update
Runs after the node is initialized and whenever some value changes. For example, if we were running an IPR session and we changed our fov parameter it would trigger a node_update.
node_finish
What to do when node is de-initialized. In this case we fetch all the node’s data and delete it.
create_camera_ray
The core of any camera plugin. This is where all our custom math goes and where we manipulate data from the scene to get our desired output. Note that we have some more Arnold keywords here, such as input and output. Each of these has parameters like input.sx for the screen space X coordinate of our input or output.weight for the weight of the output pixel. The math implemented here creates distortion in the final image.
camera_reverse_ray
The opposite of camera_create_ray. Instead of specifying a screen space coordinate and finding the corresponding value in 3D space, camera_reverse_ray takes a 3D space input and returns the corresponding 2D screen space coordinate.
node_loader
This is a required section of any plugin that tells Arnold how to load our node. Here we give it a custom name, mycamera, and set its type to AI_NODE_CAMERA.
Note the if (i != 0) syntax. The variable i is the counter in the loop Arnold uses when loading multiple sections in a plugin. Our camera plugin only has one part, the camera, so this means i == 0 is the first iteration of the loop and is the place where our first (and last) node loads. When the loop increments to 1 (that is, not zero) we’ve already loaded everything and the loader exits with return false.
As an aside, I have to say I dislike this old-fashioned Variable stuff = (Variable)(more_stuff) syntax that is prevalent in this example. It’s so awkward. There’s a newer way to do the same thing that I find more readable. Before and after:
MyCameraData data = (MyCameraData)AiNodeGetLocalData(node);auto data = static_cast<MyCameraData>(AiNodeGetLocalData(node));
This is apparently more modern with better type safety too, so it’s a win-win!
The code is ready to build, so next let’s create a quick CMake setup that will work on both Windows and Linux (even Mac too) and can be easily expanded down the road as needed.
Building with CMake
We’ll create two CMakeLists.txt files, one in our top level folder and one in our plugin src folder. Here’s the full text of both files:
And here’s a general summary of what everything does:
cmake_minimum_required
Not too important in this case, any recent version of CMake should be fine. This is useful when using newer CMake features that aren’t present in versions from several years ago.
project
The name of our project.
set(name value)
Used throughout our CMakeLists.txt files to assign values to custom variables. The first part of this file sets up all the custom paths needed to build our project on Windows and Linux.
if/elseif/endif
Here we check what operating system we’re running and set paths as required, plus add a few standard warning suppression and optimization options.
message
Prints something to the console. Good way to provide information about what CMake is doing and elaborate with detailed warnings or errors as needed.
include_directories
Where CMake should look for header files. In this case it’s just our Arnold include folder and our main source folder.
link_directores
Where CMake should look for libraries to link to. Here it’s the folder with Arnold’s pre-compiled libraries.
add_subdirectory
Where CMake should look next after processing this CMakeLists.txt. In our case it’s our src folder.
install
Where to put everything when the build is done.
Moving on to the other CMakeLists.txt in our src folder:
add_library
The name of the final library we’ll create and what source files it uses.
target_link_libraries
What our final library depends on. In this case, Arnold’s ai library.
set_target_properties
By default, CMake will output libcamera.dll, but we don’t want the “lib” part. This replaces that prefix with an empty string, “”.
After setting up CMake, I like to create a couple batch files: one to generate the Visual Studio solution and the other to build it:
generate.bat
cd C:\onedrive\main_projects\scenes\patreon\arnold_plugin\codemkdir buildcd buildcmake ..
build.bat
cd C:\onedrive\main_projects\scenes\patreon\arnold_plugin\code\buildcmake --build . --config Release --target install
I add the folder containing these files to my development PATH. This way you don’t have to remember the CMake commands or even be in the right folder when running them. Just type generate or build in the VS Code console and everything will be taken care of for you.
So, let’s try building our camera plugin!
Success!
We’ve now got camera.dll in our release folder.
Let’s add a third batch file, render. This one adds our plugin to ARNOLD_PLUGIN_PATH so Arnold knows where to find our DLL. It then renders a simple scene from the command line using kick. We can get the sample scene for the camera plugin from the Arnold site. Save it as camera.ass.
render.bat
set ARNOLD_PLUGIN_PATH=C:\onedrive\main_projects\scenes\patreon\arnold_plugin\code\release\pluginDemo_2025.7.0\bincd C:\onedrive\Software\arnold\sdk-7.3.7.0\binkick -t 12 -v 6 C:\onedrive\main_projects\scenes\patreon\arnold_plugin\scenes\camera.ass -o C:\Users\Brian\Desktop\arnoldPlugin.exr
After we’ve generated and built, simply type render. There we go, a working Arnold plugin! The noise and vignetting implemented in camera_create_ray and reverse_camera_ray are working great:
One very useful aspect of Arnold is that we can change plugin parameters via command line when we run kick. For example, we could change the fov parameter to 100 in our camera plugin simply by adding -set mycamera.fov 100 to the kick command. It’s very powerful.
Next Steps
We’ve built a simple plugin here and covered the general workflow. Plugins can become exceptionally complicated. Lentil-BHGC is one such example. It brings together a wide range of techniques under one roof and requires a lot of complex math, AOV manipulation and more. The original Lentil code is still open source, so you can study it if you like. It will require a lot of changes to work in modern Arnold versions. (That’s where Lentil-BHGC comes in!)
One other step that would be required for creating a fully-featured plugin would be DCC integration. Every DCC (Maya, C4D, Houdini, etc.) requires a custom UI to interact with any parameters we created in our plugin.
Real talk: this part of the process is kind of a nightmare. UI development in most DCCs is unfriendly and poorly documented. Gaffer is one of the easiest and 3ds Max isn’t too bad. Maya, Houdini and C4D can be very annoying. I might write a follow up to this article talking about Arnold plugin UIs, so stay tuned!
Conclusion
Now, enjoy playing around with Arnold plugin authoring! You now have a complete foundation for building more complex plugins, expanding on all the topics we covered here. The Arnold docs have a lot more examples of other plugin types, including Imagers and Filters. It’s a broad and robust platform for implementing all sorts 3D and 2D rendering and image processing algorithms.