<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.gaffer.wiki/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Murraystevenson</id>
	<title>Gaffer Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.gaffer.wiki/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Murraystevenson"/>
	<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/wiki/Special:Contributions/Murraystevenson"/>
	<updated>2026-04-10T23:46:22Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=415</id>
		<title>Template:LatestGafferVersion</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=415"/>
		<updated>2024-09-10T16:32:08Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;1.4.12.0&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=414</id>
		<title>Template:LatestGafferVersion</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=414"/>
		<updated>2024-08-15T21:34:54Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;1.4.11.0&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=413</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=413"/>
		<updated>2024-08-12T00:49:43Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* P */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.|thumb|900x900px]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|Shuffling channels using OSLCode.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Pixel Coordinates ===&lt;br /&gt;
When generating patterns, it&#039;s important to know where in the image the currently shaded pixel is located. OSLImage provides two builtin variables for locating the currently shaded pixel. &lt;br /&gt;
&lt;br /&gt;
==== P ====&lt;br /&gt;
&amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; contains the position of the center of the current pixel relative to the origin of the image [https://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithImages/AnatomyOfAnImage/index.html#data-window data window].&lt;br /&gt;
[[File:OslImageP.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; as colours, the bottom left pixel has a value of (0.5, 0.5, 0) and the top right pixel a value of ( 3.5, 3.5, 0 ).]]&lt;br /&gt;
&lt;br /&gt;
==== u, v ====&lt;br /&gt;
&amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; contain the position of the center of the current pixel remapped to 0-1 across the image [https://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithImages/AnatomyOfAnImage/index.html#display-window display window].&lt;br /&gt;
[[File:OslImageUV.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; as colours. The bottom left pixel has a value of ( 0.125, 0.125, 0 ) and the top right pixel a value of ( 0.875, 0.875, 0 ).]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=412</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=412"/>
		<updated>2024-08-12T00:49:14Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* u, v */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.|thumb|900x900px]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|Shuffling channels using OSLCode.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Pixel Coordinates ===&lt;br /&gt;
When generating patterns, it&#039;s important to know where in the image the currently shaded pixel is located. OSLImage provides two builtin variables for locating the currently shaded pixel. &lt;br /&gt;
&lt;br /&gt;
==== P ====&lt;br /&gt;
&amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; contains the position of the center of the current pixel relative to the origin of the image data window.&lt;br /&gt;
[[File:OslImageP.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; as colours, the bottom left pixel has a value of (0.5, 0.5, 0) and the top right pixel a value of ( 3.5, 3.5, 0 ).]]&lt;br /&gt;
&lt;br /&gt;
==== u, v ====&lt;br /&gt;
&amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; contain the position of the center of the current pixel remapped to 0-1 across the image [https://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithImages/AnatomyOfAnImage/index.html#display-window display window].&lt;br /&gt;
[[File:OslImageUV.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; as colours. The bottom left pixel has a value of ( 0.125, 0.125, 0 ) and the top right pixel a value of ( 0.875, 0.875, 0 ).]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=411</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=411"/>
		<updated>2024-08-12T00:48:26Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Pixel Coordinates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.|thumb|900x900px]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|Shuffling channels using OSLCode.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Pixel Coordinates ===&lt;br /&gt;
When generating patterns, it&#039;s important to know where in the image the currently shaded pixel is located. OSLImage provides two builtin variables for locating the currently shaded pixel. &lt;br /&gt;
&lt;br /&gt;
==== P ====&lt;br /&gt;
&amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; contains the position of the center of the current pixel relative to the origin of the image data window.&lt;br /&gt;
[[File:OslImageP.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; as colours, the bottom left pixel has a value of (0.5, 0.5, 0) and the top right pixel a value of ( 3.5, 3.5, 0 ).]]&lt;br /&gt;
&lt;br /&gt;
==== u, v ====&lt;br /&gt;
&amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; contain the position of the center of the current pixel remapped to 0-1 across the image display window.&lt;br /&gt;
[[File:OslImageUV.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; as colours. The bottom left pixel has a value of ( 0.125, 0.125, 0 ) and the top right pixel a value of ( 0.875, 0.875, 0 ).]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageP.png&amp;diff=410</id>
		<title>File:OslImageP.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageP.png&amp;diff=410"/>
		<updated>2024-08-12T00:46:32Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Murraystevenson uploaded a new version of File:OslImageP.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Visualisation of the value of P in OSLImage.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=409</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=409"/>
		<updated>2024-08-12T00:44:52Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.|thumb|900x900px]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|Shuffling channels using OSLCode.|thumb|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Pixel Coordinates ===&lt;br /&gt;
When generating patterns, it&#039;s important to know where in the image the currently shaded pixel is located. OSLImage provides two builtin variables for locating the currently shaded pixel. &lt;br /&gt;
&lt;br /&gt;
==== P ====&lt;br /&gt;
&amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt;contains the position of the center of each pixel relative to the origin of the image data window.&lt;br /&gt;
[[File:OslImageP.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; as colours, the bottom left pixel has a value of (0.5, 0.5, 0) and the top right pixel a value of ( 3.5, 3.5, 0 ).]]&lt;br /&gt;
&lt;br /&gt;
==== u, v ====&lt;br /&gt;
&amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; contain the position of the center of each pixel remapped to 0-1 across the image display window.&lt;br /&gt;
[[File:OslImageUV.png|none|thumb|900x900px|Visualising the values of &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; as colours. The bottom left pixel has a value of ( 0.125, 0.125, 0 ) and the top right pixel a value of ( 0.875, 0.875, 0 ).]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=408</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=408"/>
		<updated>2024-08-12T00:42:38Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|frame|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|frame|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|frame|Shuffling channels using OSLCode.]]&lt;br /&gt;
&lt;br /&gt;
=== Pixel Coordinates ===&lt;br /&gt;
When generating patterns, it&#039;s important to know where in the image the currently shaded pixel is located. OSLImage provides two builtin variables for locating the currently shaded pixel. &lt;br /&gt;
&lt;br /&gt;
==== P ====&lt;br /&gt;
&amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt;contains the position of the center of each pixel relative to the origin of the image data window.&lt;br /&gt;
[[File:OslImageP.png|none|thumb|640x640px|Visualising the values of &amp;lt;code&amp;gt;P&amp;lt;/code&amp;gt; as colours, the bottom left pixel has a value of (0.5, 0.5, 0) and the top right pixel a value of ( 3.5, 3.5, 0 ).]]&lt;br /&gt;
&lt;br /&gt;
==== u, v ====&lt;br /&gt;
&amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; contain the position of the center of each pixel remapped to 0-1 across the image display window.&lt;br /&gt;
[[File:OslImageUV.png|none|thumb|640x640px|Visualising the values of &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; &amp;amp; &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; as colours. The bottom left pixel has a value of ( 0.125, 0.125, 0 ) and the top right pixel a value of ( 0.875, 0.875, 0 ).]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageUV.png&amp;diff=407</id>
		<title>File:OslImageUV.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageUV.png&amp;diff=407"/>
		<updated>2024-08-12T00:37:22Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Visualisation of the values of u &amp;amp; v in OSLImage&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageP.png&amp;diff=406</id>
		<title>File:OslImageP.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageP.png&amp;diff=406"/>
		<updated>2024-08-12T00:36:47Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Visualisation of the value of P in OSLImage.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=405</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=405"/>
		<updated>2024-08-12T00:15:23Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Adding channels to OSLImage ====&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
&lt;br /&gt;
==== Reading channels from shaders ====&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|frame|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|frame|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|frame|Shuffling channels using OSLCode.]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=404</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=404"/>
		<updated>2024-08-12T00:12:47Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Reading channels in an OSLCode node */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|frame|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|frame|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/TutorialUsingTheOSLCodeNode/index.html OSLCode] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
// Providing &amp;quot;&amp;quot; as the layer name will return a color containing the value of the R, G, B channels.&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|frame|Shuffling channels using OSLCode.]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=403</id>
		<title>OSL Image Manipulation</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=OSL_Image_Manipulation&amp;diff=403"/>
		<updated>2024-08-11T23:59:12Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: WIP guide to OSL Image Manipulation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The [[OSLImage]] node allows the use of [https://open-shading-language.readthedocs.io OpenShadingLanguage] shaders to generate or modify pixel values in an image.&lt;br /&gt;
[[File:OslImageFractal.png|none|thumb|720x720px|Using an OSLImage node with a shader written in an OSLCode node to generate a fractal pattern.]]&lt;br /&gt;
Shaders can be built from an existing library of OSL shaders loaded via the [[OSLShader]] node, and arbitrary shader code can be evaluated via the [[OSLCode]] node. An image manipulation shader could be built entirely from existing nodes, written in a single OSLCode node, or assembled from a combination of OSL shaders and OSLCode nodes.&lt;br /&gt;
&lt;br /&gt;
Shaders are evaluated per pixel in the image and typically would read that pixel value from one or more channels in the image, compute a value from those inputs and then output a new value that would replace the input pixel in an existing channel or layer, or create a new channel or layer.&lt;br /&gt;
[[File:OslImageLuminance.png|none|thumb|720x720px|Reading RGB values from and input image, using the OSL luminance shader to compute luminance values of those pixels, and outputting to a new channel named &amp;quot;luminance&amp;quot;.]]&lt;br /&gt;
&lt;br /&gt;
=== Writing channel data ===&lt;br /&gt;
Channels are written by adding inputs to the OSLImage node by clicking on the [[File:PlugAdderIcon.png]] button in the Node Editor or by clicking or dragging a plug onto the [[File:PlugAdderIcon.png]] on the left of the node in the Graph Editor.&lt;br /&gt;
[[File:OslImageAddInputFromNodeEditor.png|none|thumb|Adding an input from the Node Editor]]&lt;br /&gt;
[[File:OslImageAddInputGraphEditor.png|none|thumb|Adding an input from the Graph Editor]]&lt;br /&gt;
You can choose from common channel or layer names from the menu to write to RGB or RGBA layer, or to individual R, G, B, A channels.&lt;br /&gt;
[[File:OSLImageAddChannelsNodeEditor.gif|none|frame|Adding RGB and A plugs from the Node Editor.]]&lt;br /&gt;
You can also create arbitrary channels or layers via the &amp;lt;code&amp;gt;Custom-&amp;gt;Channel&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Custom-&amp;gt;Layer&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Custom-&amp;gt;LayerRGBA&amp;lt;/code&amp;gt; menu items. The channel or layer name can then be customised by by editing the name of the plug in the Node Editor.&lt;br /&gt;
[[File:OSLImageAddCustomChannelsGraphEditor.gif|none|frame|Adding a custom named &amp;quot;luminance&amp;quot; channel, and &amp;quot;diffuse&amp;quot; layer to the image.]]&lt;br /&gt;
{{tip | Behind the scenes, these actions are adding new child plugs to the `channels` plug of the OSLImage node.}}&lt;br /&gt;
&lt;br /&gt;
{{info | The OSLImage node&#039;s channel inputs can either be a Color3fPlug or a FloatPlug, so choosing RGBA from the menu will create two plugs, a Color3fPlug for the RGB channels and a FloatPlug for the A channel. }}&lt;br /&gt;
&lt;br /&gt;
Each channel or layer created provides an input connection ready to receive an OSL shader.&lt;br /&gt;
&lt;br /&gt;
==== Disabling an output ====&lt;br /&gt;
Output layers and channels can be disabled by toggling their &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug.&lt;br /&gt;
[[File:OSLImageDisableChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
==== Removing an output ====&lt;br /&gt;
Previously created output layers and channels can be removed by right clicking on the plug and selecting &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; from the context menu.&lt;br /&gt;
[[File:OSLImageDeleteChannel.gif|none|frame]]&lt;br /&gt;
&lt;br /&gt;
=== Reading channel data ===&lt;br /&gt;
The &amp;lt;code&amp;gt;InChannel&amp;lt;/code&amp;gt; OSL shader can be used to read a specific channel from the processed image.&lt;br /&gt;
[[File:OSLInChannelShuffle.gif|none|frame|Shuffling channels with OSLImage by reading a single channel with an InChannel shader and writing the result to other channels.]]&lt;br /&gt;
The &amp;lt;code&amp;gt;InLayer&amp;lt;/code&amp;gt; OSL shader can be used to read the R, G, B channels of a specified image layer.&lt;br /&gt;
[[File:OSLInLayerShuffle.gif|none|frame|Reading the R, G, B channels with an InLayer shader and shuffling them to a new &amp;quot;shuffled&amp;quot; layer.]]&lt;br /&gt;
&lt;br /&gt;
==== Reading channels in an OSLCode node ====&lt;br /&gt;
When writing an OSL shader in an [[OSLCode]] node, Gaffer provides &amp;lt;code&amp;gt;inChannel( channelName, defaultValue )&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;inLayer( layerName, defaultValue )&amp;lt;/code&amp;gt; functions to access the value of a specific channel or layer for the current pixel. Providing an empty &amp;lt;code&amp;gt;layerName&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;inLayer()&amp;lt;/code&amp;gt; will return a color containing the value of the R, G, B channels.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel, returning 0 if the channel does not exist.&lt;br /&gt;
float R = inChannel( &amp;quot;R&amp;quot;, 0.0 );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;R&amp;quot;, &amp;quot;G&amp;quot;, &amp;quot;B&amp;quot; channels, returning black if the layer doesn&#039;t exist&lt;br /&gt;
color RGB = inLayer( &amp;quot;&amp;quot;, color( 0.0 ) );&lt;br /&gt;
&lt;br /&gt;
// Get the values of the &amp;quot;diffuse.R&amp;quot;, &amp;quot;diffuse.G&amp;quot;, &amp;quot;diffuse.B&amp;quot; channels, returning white if the layer doesn&#039;t exist&lt;br /&gt;
color diffuse = inLayer( &amp;quot;diffuse&amp;quot;, color( 1.0 ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Channels can also be accessed via OSL&#039;s [https://open-shading-language.readthedocs.io/en/v1.13.9.0/stdlib.html#renderer-state-and-message-passing getattribute] function.&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel for the current pixel&lt;br /&gt;
float R;&lt;br /&gt;
getattribute( &amp;quot;R&amp;quot;, R );&lt;br /&gt;
&lt;br /&gt;
// Get the value of the &amp;quot;R&amp;quot; channel from the &amp;quot;diffuse&amp;quot; layer for the current pixel&lt;br /&gt;
float diffuseR;&lt;br /&gt;
getattribute( &amp;quot;diffuse.R&amp;quot;, diffuseR );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[File:OSLCodeShuffle.gif|none|frame|Shuffling channels using OSLCode.]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLCodeShuffle.gif&amp;diff=402</id>
		<title>File:OSLCodeShuffle.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLCodeShuffle.gif&amp;diff=402"/>
		<updated>2024-08-11T23:53:11Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of using an OSLCode node and OSLImage to shuffle channels.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLInLayerShuffle.gif&amp;diff=401</id>
		<title>File:OSLInLayerShuffle.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLInLayerShuffle.gif&amp;diff=401"/>
		<updated>2024-08-11T23:35:35Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of using the InLayer OSL shader with an OSLImage to shuffle channels from one layer to another.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLInChannelShuffle.gif&amp;diff=400</id>
		<title>File:OSLInChannelShuffle.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLInChannelShuffle.gif&amp;diff=400"/>
		<updated>2024-08-11T23:28:21Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GIF demonstrating basic usage of OSLInChannel and OSLImage by combining them to create a channel shuffle.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLImageDeleteChannel.gif&amp;diff=399</id>
		<title>File:OSLImageDeleteChannel.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLImageDeleteChannel.gif&amp;diff=399"/>
		<updated>2024-08-11T23:15:39Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GIF of deleting plugs from an OSLImage node.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLImageDisableChannel.gif&amp;diff=398</id>
		<title>File:OSLImageDisableChannel.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLImageDisableChannel.gif&amp;diff=398"/>
		<updated>2024-08-11T23:15:03Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GIF of disabling plugs on an OSLImage node.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLImageAddCustomChannelsGraphEditor.gif&amp;diff=397</id>
		<title>File:OSLImageAddCustomChannelsGraphEditor.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLImageAddCustomChannelsGraphEditor.gif&amp;diff=397"/>
		<updated>2024-08-11T23:11:29Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GIF of adding custom channels to an OSLImage node from the Graph Editor and then editing the plug names in the Node Editor.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OSLImageAddChannelsNodeEditor.gif&amp;diff=396</id>
		<title>File:OSLImageAddChannelsNodeEditor.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OSLImageAddChannelsNodeEditor.gif&amp;diff=396"/>
		<updated>2024-08-11T23:08:50Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A GIF showing creation of RGB and A channels on an OSLImage node from the Node Editor&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:PlugAdderIcon.png&amp;diff=395</id>
		<title>File:PlugAdderIcon.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:PlugAdderIcon.png&amp;diff=395"/>
		<updated>2024-08-11T22:59:06Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Gaffer plug adder icon&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageAddInputGraphEditor.png&amp;diff=394</id>
		<title>File:OslImageAddInputGraphEditor.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageAddInputGraphEditor.png&amp;diff=394"/>
		<updated>2024-08-11T22:56:43Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screenshot of adding an input to an OSLImage node from the Graph Editor.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageAddInputFromNodeEditor.png&amp;diff=393</id>
		<title>File:OslImageAddInputFromNodeEditor.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageAddInputFromNodeEditor.png&amp;diff=393"/>
		<updated>2024-08-11T22:55:41Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screenshot of adding an input to an OSLImage node from the Node Editor&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageLuminance.png&amp;diff=392</id>
		<title>File:OslImageLuminance.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageLuminance.png&amp;diff=392"/>
		<updated>2024-08-11T22:51:08Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of using an OSL shader network to compute the luminance of an input image and output the values to a new channel.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:OslImageFractal.png&amp;diff=391</id>
		<title>File:OslImageFractal.png</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:OslImageFractal.png&amp;diff=391"/>
		<updated>2024-08-11T22:48:06Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Using an OSLImage and OSLCode node to generate a fractal image pattern.&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=ContactSheet&amp;diff=390</id>
		<title>ContactSheet</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=ContactSheet&amp;diff=390"/>
		<updated>2024-07-26T19:32:00Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferImage&lt;br /&gt;
|description=Assembles multiple input images into a tiled grid, with customisable layout, labels and borders.&lt;br /&gt;
|categories=Image, ImageTransform&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=389</id>
		<title>Template:LatestGafferVersion</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=389"/>
		<updated>2024-07-26T19:30:28Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;1.4.10.0&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=LocalDispatcher&amp;diff=388</id>
		<title>LocalDispatcher</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=LocalDispatcher&amp;diff=388"/>
		<updated>2024-07-26T19:29:29Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferDispatch |description=Schedules execution of task graphs on the local machine. Tasks may be dispatched in the background to keep the UI responsive. |categories=Dispatch }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferDispatch&lt;br /&gt;
|description=Schedules execution of task graphs on the local machine. Tasks may be dispatched in the background to keep the UI responsive.&lt;br /&gt;
|categories=Dispatch&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=MeshTessellate&amp;diff=387</id>
		<title>MeshTessellate</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=MeshTessellate&amp;diff=387"/>
		<updated>2024-07-26T19:27:45Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferScene |description=Tessellates meshes according to their subdivision scheme, converting them into higher polygon meshes which follow the limit surface - usually the smooth regular quads of a Catmull-Clark scheme. |categories=Mesh }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferScene&lt;br /&gt;
|description=Tessellates meshes according to their subdivision scheme, converting them into higher polygon meshes which follow the limit surface - usually the smooth regular quads of a Catmull-Clark scheme.&lt;br /&gt;
|categories=Mesh&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=DeepSlice&amp;diff=386</id>
		<title>DeepSlice</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=DeepSlice&amp;diff=386"/>
		<updated>2024-07-26T19:26:32Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferImage |description=Takes a slice out of an image with depth defined by Z (and optionally ZBack) channels by discarding everything outside of a clipping range. The range is half open, including point samples exactly at the near clip, but excluding point samples exactly at the far clip. This means that if you split an image into a front and back with two DeepSlices, they will composite back together to match the original. |categories=Deep }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferImage&lt;br /&gt;
|description=Takes a slice out of an image with depth defined by Z (and optionally ZBack) channels by discarding everything outside of a clipping range. The range is half open, including point samples exactly at the near clip, but excluding point samples exactly at the far clip. This means that if you split an image into a front and back with two DeepSlices, they will composite back together to match the original.&lt;br /&gt;
|categories=Deep&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=ContactSheetCore&amp;diff=385</id>
		<title>ContactSheetCore</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=ContactSheetCore&amp;diff=385"/>
		<updated>2024-07-26T19:24:55Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferImage |description=Collects multiple input images, transforming them into tiles within the output image. Provides the core functionality of the ContactSheet node, and may be reused for making similar nodes. |categories=ImageTransform }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferImage&lt;br /&gt;
|description=Collects multiple input images, transforming them into tiles within the output image. Provides the core functionality of the ContactSheet node, and may be reused for making similar nodes.&lt;br /&gt;
|categories=ImageTransform&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=ContactSheet&amp;diff=384</id>
		<title>ContactSheet</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=ContactSheet&amp;diff=384"/>
		<updated>2024-07-26T19:24:05Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferImage |description=Assembles multiple input images into a tiled grid, with customisable layout, labels and borders. |categories=Image }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferImage&lt;br /&gt;
|description=Assembles multiple input images into a tiled grid, with customisable layout, labels and borders.&lt;br /&gt;
|categories=Image&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=RenderPassShader&amp;diff=383</id>
		<title>RenderPassShader</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=RenderPassShader&amp;diff=383"/>
		<updated>2024-07-26T19:22:58Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Created page with &amp;quot;{{Node |namespace=GafferScene |description=Sets up a global shader in the options to replace a shader used by a render pass type. |categories=RenderPass, Shader }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Node&lt;br /&gt;
|namespace=GafferScene&lt;br /&gt;
|description=Sets up a global shader in the options to replace a shader used by a render pass type.&lt;br /&gt;
|categories=RenderPass, Shader&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Scripting_Box_Nodes&amp;diff=382</id>
		<title>Scripting Box Nodes</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Scripting_Box_Nodes&amp;diff=382"/>
		<updated>2024-07-26T19:19:36Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: Undo revision 381 by Murraystevenson (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Gaffer’s [[Box]] node is a staple building block sharing chunks of node graph (via the Reference system), or quickly building tools via expressions and promoted plugs.&lt;br /&gt;
&lt;br /&gt;
[[File:ScriptingBoxNodes1.png]]&lt;br /&gt;
&lt;br /&gt;
The Gaffer docs have a page covering [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html Box nodes and how to use them]. In this guide we take a quick look at how to re-create a simple GroundPlane tool made in the UI, using only Gaffer’s python API.&lt;br /&gt;
&lt;br /&gt;
{{ warning | This guide assumes you are familiar with working with Box nodes in the UI, including [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#promoting-and-demoting-a-plug promoting plugs] and how [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#connecting-a-box-to-the-main-graph in/out nodes work]. You also need to be comfortable with using [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithThePythonScriptingAPI/TutorialNodeGraphEditingInPython/index.html Gaffer’s Python API to work with the node graph].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
[[File:ScriptingBoxNodes2.png|thumb|The internal network we&#039;ll create within the GroundPlane box.]]&lt;br /&gt;
For those reading who have used the Plane node. Its ‘z-up’ orientation is less than helpful if you just want a bit of ground for some reason or another. It is also tiny.&lt;br /&gt;
&lt;br /&gt;
Here we’re going to make a simple box that encapsulates the network shown to the right so that you can add a plane with the right orientation to your scene. Exposing a few useful plugs to make it easy to tweak.&lt;br /&gt;
&lt;br /&gt;
== The script ==&lt;br /&gt;
All of the code below can be run directly in the Python Editor. Let start first by importing a few pre-requisites we’ll need later:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import Gaffer&lt;br /&gt;
import GafferScene&lt;br /&gt;
import imath&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Making the Box ===&lt;br /&gt;
The box node, as with all Gaffer nodes, is made by literally constructing an instance of the Box class and adding it to the graph. In this script, we’ll just put it at the root. In a real tool, you’d probably wan’t to use [https://github.com/GafferHQ/gaffer/blob/main/python/GafferUI/EditMenu.py#L91-L97 GafferUI.EditMenu.scope()] to get the root visible in the user’s Graph Editor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
box = Gaffer.Box( &amp;quot;GroundPlane&amp;quot; )&lt;br /&gt;
root.addChild( box )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{ tip | As this is a script and we don’t know what nodes may already be in the graph, when creating new nodes, we work with the node instance directly rather than adding it to it’s parent and using the parent[&amp;quot;NodeName&amp;quot;] syntax. The latter works in simple cases, but if there is already a node with that name, the new one will be renamed, so the sub-script syntax would give the existing node, not your new one! }}&lt;br /&gt;
&lt;br /&gt;
=== The internal network ===&lt;br /&gt;
We need a Plane, to set some non-default values so it’s more useful, freeze it’s transform and use a Parent node to insert it into the incoming scene. Make these nodes, connect them up and add them to the Box:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
plane = GafferScene.Plane( &amp;quot;Plane&amp;quot; )&lt;br /&gt;
plane[&amp;quot;transform&amp;quot;][&amp;quot;rotate&amp;quot;].setValue( imath.V3f( -90, 0, 0 ) )&lt;br /&gt;
plane[&amp;quot;dimensions&amp;quot;].setValue( imath.V2f( 100, 100 ) )&lt;br /&gt;
plane[&amp;quot;name&amp;quot;].setValue( &amp;quot;ground&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze = GafferScene.FreezeTransform( &amp;quot;FreezeTransform&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
# Note, don&#039;t confuse &#039;parent&#039; here with the built-in variable&lt;br /&gt;
# available in Python expressions. This is just a local holding&lt;br /&gt;
# our &#039;Parent&#039; node (I should have used a less confusing example!)&lt;br /&gt;
 &lt;br /&gt;
parent = GafferScene.Parent( &amp;quot;Parent&amp;quot; )&lt;br /&gt;
parent[&amp;quot;parent&amp;quot;].setValue(&amp;quot;/&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
freeze[&amp;quot;in&amp;quot;].setInput( plane[&amp;quot;out&amp;quot;] )&lt;br /&gt;
parent[&amp;quot;child&amp;quot;].setInput( freeze[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
box.addChild( plane )&lt;br /&gt;
box.addChild( freeze )&lt;br /&gt;
box.addChild( parent )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Promoting plugs ===&lt;br /&gt;
We now have our network inside the box, but the box has no input or output. We can use the Gaffer.BoxIO node’s static promote method to promote the in/out plugs on the Parent node, as a user might do using the right-click menus in the UI:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can use exactly the same function to promote plugs that appear in the Node Editor instead of in the Graph Editor (they’re all just plugs after all). BoxIO.promote preserves the plugs existing metadata, which is what controls where they’re presented, so it knows whether they should be on the top or bottom or sides of the box.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;name&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;parent&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;divisions&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;dimensions&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The promote() method takes care of creating, connecting and configuring BoxIn/ BoxOut nodes and plugs on the Box itself. You don’t need to manually manage these.&lt;br /&gt;
&lt;br /&gt;
You should now have a functional Box just like the one made in the UI.&lt;br /&gt;
&lt;br /&gt;
=== Disabling the node (a.k.a finding inputs and outputs). ===&lt;br /&gt;
In order to allow a user to disable/bypass the Box, we need to provide a pass-through connection ([http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#setting-up-a-box-for-pass-through explained here]) that defines what to output when the Box is disabled. To do this, we’ll need to get the BoxIn and BoxOut nodes so we can connect the boxOutNode[&amp;quot;passThrough&amp;quot;] plug to the boxInNode[&amp;quot;out&amp;quot;] plug.&lt;br /&gt;
&lt;br /&gt;
{{ info | Hang on, you said &amp;quot;out&amp;quot; on the BoxIn node? Don’t you mean &amp;quot;in&amp;quot;?&lt;br /&gt;
&lt;br /&gt;
To understand this, we’ll have to look at a few details of how Box nodes work. A box node has in/out plugs. When you look at the box node from the outside you are looking at plugs on the box node itself. But you also want to see them when you’re looking at the graph inside the box so you can hook things up.&lt;br /&gt;
&lt;br /&gt;
As its Gaffer, and we try to keep the fundamentals simple and consistent. Rather than invent something new and esoteric, the BoxIO code you used earlier creates child nodes that are either BoxIn or BoxOut to represent the box’s input/output plugs when viewing the graph inside the box in the Graph Editor and connects these from/to the box’s actual inputs and outputs so data can flow from outside, to inside and back again, ie:&lt;br /&gt;
&lt;br /&gt;
[[File:ScriptingBoxNodes3.png]]&lt;br /&gt;
&lt;br /&gt;
As they’re __ prefixed (ie: private plugs), you don’t see these ‘bridging’ plugs and connections in the UI (it’d just be confusing too).&lt;br /&gt;
&lt;br /&gt;
The name of these internal nodes also determines the name of the plug on the outside of the box – which works well as these plug names need to be unique.&lt;br /&gt;
&lt;br /&gt;
So, you need the out plug of the BoxIn node as thats the plug that’s visible when looking at the inside of the box, that carries the data from the in plug on the outside of the box. }}&lt;br /&gt;
&lt;br /&gt;
Back, to connecting up the passthrough. There are a couple of ways to find the plugs to do this.&lt;br /&gt;
&lt;br /&gt;
=== Getting the I/O nodes for an existing box ===&lt;br /&gt;
In this simple case, the Box node in plug is connected to its child BoxIn node, and it’s out plug is connected from it’s child BoxOut node. A such, you can use the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxInNode = box[&amp;quot;in&amp;quot;].outputs()[0].node()&lt;br /&gt;
boxOutNode = box[&amp;quot;out&amp;quot;].getInput().node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A safer route ===&lt;br /&gt;
The above works, but relies on knowing the correct names for the input/output plugs on the box. If you’ve promoted several inputs and outputs, they have to have unique names so they might not be what you think.&lt;br /&gt;
&lt;br /&gt;
When you promote a plug using Gaffer.BoxIO.promote() it returns the corresponding plug on the outside of the box. So we could amend our promotion code to the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxInPlug = Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
boxOutPlug = Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Add a passthrough&lt;br /&gt;
boxOutNode = boxOutPlug.getInput().node()&lt;br /&gt;
boxInNode = boxInPlug.outputs()[0].node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You could also go backwards, from the &#039;&#039;&#039;parent&#039;&#039;&#039; node:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxOutNode = parent[&amp;quot;out&amp;quot;].outputs()[0].node()&lt;br /&gt;
boxInNode = parent[&amp;quot;in&amp;quot;].getInput().node()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Box Icons ==&lt;br /&gt;
By default, Box nodes are drawn with a ‘box’ icon. When you change this in the UI Editor, it simply edits the node’s metadata. You can do the same yourself if you want to remove (or change) the icon:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# remove&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, None )&lt;br /&gt;
# change&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, &#039;grid.png&#039; )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{ info | NOTE: Images specified by a relative path need to be on $GAFFER_IMAGE_PATHS. }}&lt;br /&gt;
&lt;br /&gt;
== Useful links ==&lt;br /&gt;
[http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html# Box node introduction]&lt;br /&gt;
&lt;br /&gt;
[http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithThePythonScriptingAPI/TutorialNodeGraphEditingInPython/index.html Working with the Node Graph scripting tutorial]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/GafferHQ/gaffer/blob/main/include/Gaffer/BoxIO.h The BoxIO module]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/GafferHQ/gaffer/blob/main/python/GafferUI/EditMenu.py#L91-L97 Finding the ‘current’ graph root with GafferUI.EditMenu.scope()]&lt;br /&gt;
&lt;br /&gt;
== The final script ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
import Gaffer&lt;br /&gt;
import GafferScene&lt;br /&gt;
import imath&lt;br /&gt;
 &lt;br /&gt;
# The box&lt;br /&gt;
 &lt;br /&gt;
box = Gaffer.Box( &amp;quot;GroundPlane&amp;quot; )&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, None )&lt;br /&gt;
root.addChild( box )&lt;br /&gt;
 &lt;br /&gt;
# Internal network&lt;br /&gt;
 &lt;br /&gt;
plane = GafferScene.Plane( &amp;quot;Plane&amp;quot; )&lt;br /&gt;
plane[&amp;quot;transform&amp;quot;][&amp;quot;rotate&amp;quot;].setValue( imath.V3f( -90, 0, 0 ) )&lt;br /&gt;
plane[&amp;quot;dimensions&amp;quot;].setValue( imath.V2f( 100, 100 ) )&lt;br /&gt;
plane[&amp;quot;name&amp;quot;].setValue( &amp;quot;ground&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze = GafferScene.FreezeTransform( &amp;quot;FreezeTransform&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
parent = GafferScene.Parent( &amp;quot;Parent&amp;quot; )&lt;br /&gt;
parent[&amp;quot;parent&amp;quot;].setValue(&amp;quot;/&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
freeze[&amp;quot;in&amp;quot;].setInput( plane[&amp;quot;out&amp;quot;] )&lt;br /&gt;
parent[&amp;quot;child&amp;quot;].setInput( freeze[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
box.addChild( plane )&lt;br /&gt;
box.addChild( freeze )&lt;br /&gt;
box.addChild( parent )&lt;br /&gt;
 &lt;br /&gt;
# Promote i/o&lt;br /&gt;
boxInPlug = Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
boxOutPlug = Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Promote useful Node Editor plugs&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;name&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;parent&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;divisions&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;dimensions&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Add a passthrough&lt;br /&gt;
boxOutNode = boxOutPlug.getInput().node()&lt;br /&gt;
boxInNode = boxInPlug.outputs()[0].node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Scripting_Box_Nodes&amp;diff=381</id>
		<title>Scripting Box Nodes</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Scripting_Box_Nodes&amp;diff=381"/>
		<updated>2024-07-26T19:18:34Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Gaffer’s [[Box]] node is a staple building block sharing chunks of node graph (via the Reference system), or quickly building tools via expressions and promoted plugs.&lt;br /&gt;
&lt;br /&gt;
[[File:ScriptingBoxNodes1.png]]&lt;br /&gt;
&lt;br /&gt;
The Gaffer docs have a page covering [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html Box nodes and how to use them]. In this guide we take a quick look at how to re-create a simple GroundPlane tool made in the UI, using only Gaffer’s python API.&lt;br /&gt;
&lt;br /&gt;
{{ warning | This guide assumes you are familiar with working with Box nodes in the UI, including [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#promoting-and-demoting-a-plug promoting plugs] and how [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#connecting-a-box-to-the-main-graph in/out nodes work]. You also need to be comfortable with using [http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithThePythonScriptingAPI/TutorialNodeGraphEditingInPython/index.html Gaffer’s Python API to work with the node graph].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
[[File:ScriptingBoxNodes2.png|thumb|The internal network we&#039;ll create within the GroundPlane box.]]&lt;br /&gt;
For those reading who have used the Plane node. Its ‘z-up’ orientation is less than helpful if you just want a bit of ground for some reason or another. It is also tiny.&lt;br /&gt;
&lt;br /&gt;
Here we’re going to make a simple box that encapsulates the network shown to the right so that you can add a plane with the right orientation to your scene. Exposing a few useful plugs to make it easy to tweak.&lt;br /&gt;
&lt;br /&gt;
== The script ==&lt;br /&gt;
All of the code below can be run directly in the Python Editor. Let start first by importing a few pre-requisites we’ll need later:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import Gaffer&lt;br /&gt;
import GafferScene&lt;br /&gt;
import imath&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Making the Box ===&lt;br /&gt;
The box node, as with all Gaffer nodes, is made by literally constructing an instance of the Box class and adding it to the graph. In this script, we’ll just put it at the root. In a real tool, you’d probably wan’t to use [https://github.com/GafferHQ/gaffer/blob/main/python/GafferUI/EditMenu.py#L91-L97 GafferUI.EditMenu.scope()] to get the root visible in the user’s Graph Editor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
box = Gaffer.Box( &amp;quot;GroundPlane&amp;quot; )&lt;br /&gt;
root.addChild( box )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{ tip | As this is a script and we don’t know what nodes may already be in the graph, when creating new nodes, we work with the node instance directly rather than adding it to it’s parent and using the parent[&amp;quot;NodeName&amp;quot;] syntax. The latter works in simple cases, but if there is already a node with that name, the new one will be renamed, so the sub-script syntax would give the existing node, not your new one! }}&lt;br /&gt;
&lt;br /&gt;
=== The internal network ===&lt;br /&gt;
We need a Plane, to set some non-default values so it’s more useful, freeze it’s transform and use a Parent node to insert it into the incoming scene. Make these nodes, connect them up and add them to the Box:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
plane = GafferScene.Plane( &amp;quot;Plane&amp;quot; )&lt;br /&gt;
plane[&amp;quot;transform&amp;quot;][&amp;quot;rotate&amp;quot;].setValue( imath.V3f( -90, 0, 0 ) )&lt;br /&gt;
plane[&amp;quot;dimensions&amp;quot;].setValue( imath.V2f( 100, 100 ) )&lt;br /&gt;
plane[&amp;quot;name&amp;quot;].setValue( &amp;quot;ground&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze = GafferScene.FreezeTransform( &amp;quot;FreezeTransform&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
# Note, don&#039;t confuse &#039;parent&#039; here with the built-in variable&lt;br /&gt;
# available in Python expressions. This is just a local holding&lt;br /&gt;
# our &#039;Parent&#039; node (I should have used a less confusing example!)&lt;br /&gt;
 &lt;br /&gt;
parent = GafferScene.Parent( &amp;quot;Parent&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze[&amp;quot;in&amp;quot;].setInput( plane[&amp;quot;out&amp;quot;] )&lt;br /&gt;
parent[&amp;quot;child&amp;quot;].setInput( freeze[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
box.addChild( plane )&lt;br /&gt;
box.addChild( freeze )&lt;br /&gt;
box.addChild( parent )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Promoting plugs ===&lt;br /&gt;
We now have our network inside the box, but the box has no input or output. We can use the Gaffer.BoxIO node’s static promote method to promote the in/out plugs on the Parent node, as a user might do using the right-click menus in the UI:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can use exactly the same function to promote plugs that appear in the Node Editor instead of in the Graph Editor (they’re all just plugs after all). BoxIO.promote preserves the plugs existing metadata, which is what controls where they’re presented, so it knows whether they should be on the top or bottom or sides of the box.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;name&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;parent&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;divisions&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;dimensions&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The promote() method takes care of creating, connecting and configuring BoxIn/ BoxOut nodes and plugs on the Box itself. You don’t need to manually manage these.&lt;br /&gt;
&lt;br /&gt;
You should now have a functional Box just like the one made in the UI.&lt;br /&gt;
&lt;br /&gt;
=== Disabling the node (a.k.a finding inputs and outputs). ===&lt;br /&gt;
In order to allow a user to disable/bypass the Box, we need to provide a pass-through connection ([http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html#setting-up-a-box-for-pass-through explained here]) that defines what to output when the Box is disabled. To do this, we’ll need to get the BoxIn and BoxOut nodes so we can connect the boxOutNode[&amp;quot;passThrough&amp;quot;] plug to the boxInNode[&amp;quot;out&amp;quot;] plug.&lt;br /&gt;
&lt;br /&gt;
{{ info | Hang on, you said &amp;quot;out&amp;quot; on the BoxIn node? Don’t you mean &amp;quot;in&amp;quot;?&lt;br /&gt;
&lt;br /&gt;
To understand this, we’ll have to look at a few details of how Box nodes work. A box node has in/out plugs. When you look at the box node from the outside you are looking at plugs on the box node itself. But you also want to see them when you’re looking at the graph inside the box so you can hook things up.&lt;br /&gt;
&lt;br /&gt;
As its Gaffer, and we try to keep the fundamentals simple and consistent. Rather than invent something new and esoteric, the BoxIO code you used earlier creates child nodes that are either BoxIn or BoxOut to represent the box’s input/output plugs when viewing the graph inside the box in the Graph Editor and connects these from/to the box’s actual inputs and outputs so data can flow from outside, to inside and back again, ie:&lt;br /&gt;
&lt;br /&gt;
[[File:ScriptingBoxNodes3.png]]&lt;br /&gt;
&lt;br /&gt;
As they’re __ prefixed (ie: private plugs), you don’t see these ‘bridging’ plugs and connections in the UI (it’d just be confusing too).&lt;br /&gt;
&lt;br /&gt;
The name of these internal nodes also determines the name of the plug on the outside of the box – which works well as these plug names need to be unique.&lt;br /&gt;
&lt;br /&gt;
So, you need the out plug of the BoxIn node as thats the plug that’s visible when looking at the inside of the box, that carries the data from the in plug on the outside of the box. }}&lt;br /&gt;
&lt;br /&gt;
Back, to connecting up the passthrough. There are a couple of ways to find the plugs to do this.&lt;br /&gt;
&lt;br /&gt;
=== Getting the I/O nodes for an existing box ===&lt;br /&gt;
In this simple case, the Box node in plug is connected to its child BoxIn node, and it’s out plug is connected from it’s child BoxOut node. A such, you can use the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxInNode = box[&amp;quot;in&amp;quot;].outputs()[0].node()&lt;br /&gt;
boxOutNode = box[&amp;quot;out&amp;quot;].getInput().node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A safer route ===&lt;br /&gt;
The above works, but relies on knowing the correct names for the input/output plugs on the box. If you’ve promoted several inputs and outputs, they have to have unique names so they might not be what you think.&lt;br /&gt;
&lt;br /&gt;
When you promote a plug using Gaffer.BoxIO.promote() it returns the corresponding plug on the outside of the box. So we could amend our promotion code to the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxInPlug = Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
boxOutPlug = Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Add a passthrough&lt;br /&gt;
boxOutNode = boxOutPlug.getInput().node()&lt;br /&gt;
boxInNode = boxInPlug.outputs()[0].node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You could also go backwards, from the &#039;&#039;&#039;parent&#039;&#039;&#039; node:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
boxOutNode = parent[&amp;quot;out&amp;quot;].outputs()[0].node()&lt;br /&gt;
boxInNode = parent[&amp;quot;in&amp;quot;].getInput().node()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Box Icons ==&lt;br /&gt;
By default, Box nodes are drawn with a ‘box’ icon. When you change this in the UI Editor, it simply edits the node’s metadata. You can do the same yourself if you want to remove (or change) the icon:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# remove&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, None )&lt;br /&gt;
# change&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, &#039;grid.png&#039; )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{ info | NOTE: Images specified by a relative path need to be on $GAFFER_IMAGE_PATHS. }}&lt;br /&gt;
&lt;br /&gt;
== Useful links ==&lt;br /&gt;
[http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithTheNodeGraph/BoxNode/index.html# Box node introduction]&lt;br /&gt;
&lt;br /&gt;
[http://www.gafferhq.org/documentation/{{latestGafferVersion}}/WorkingWithThePythonScriptingAPI/TutorialNodeGraphEditingInPython/index.html Working with the Node Graph scripting tutorial]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/GafferHQ/gaffer/blob/main/include/Gaffer/BoxIO.h The BoxIO module]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/GafferHQ/gaffer/blob/main/python/GafferUI/EditMenu.py#L91-L97 Finding the ‘current’ graph root with GafferUI.EditMenu.scope()]&lt;br /&gt;
&lt;br /&gt;
== The final script ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot; line&amp;gt;&lt;br /&gt;
import Gaffer&lt;br /&gt;
import GafferScene&lt;br /&gt;
import imath&lt;br /&gt;
 &lt;br /&gt;
# The box&lt;br /&gt;
 &lt;br /&gt;
box = Gaffer.Box( &amp;quot;GroundPlane&amp;quot; )&lt;br /&gt;
Gaffer.Metadata.registerValue( box, &#039;icon&#039;, None )&lt;br /&gt;
root.addChild( box )&lt;br /&gt;
 &lt;br /&gt;
# Internal network&lt;br /&gt;
 &lt;br /&gt;
plane = GafferScene.Plane( &amp;quot;Plane&amp;quot; )&lt;br /&gt;
plane[&amp;quot;transform&amp;quot;][&amp;quot;rotate&amp;quot;].setValue( imath.V3f( -90, 0, 0 ) )&lt;br /&gt;
plane[&amp;quot;dimensions&amp;quot;].setValue( imath.V2f( 100, 100 ) )&lt;br /&gt;
plane[&amp;quot;name&amp;quot;].setValue( &amp;quot;ground&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze = GafferScene.FreezeTransform( &amp;quot;FreezeTransform&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
parent = GafferScene.Parent( &amp;quot;Parent&amp;quot; )&lt;br /&gt;
 &lt;br /&gt;
freeze[&amp;quot;in&amp;quot;].setInput( plane[&amp;quot;out&amp;quot;] )&lt;br /&gt;
parent[&amp;quot;child&amp;quot;].setInput( freeze[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
box.addChild( plane )&lt;br /&gt;
box.addChild( freeze )&lt;br /&gt;
box.addChild( parent )&lt;br /&gt;
 &lt;br /&gt;
# Promote i/o&lt;br /&gt;
boxInPlug = Gaffer.BoxIO.promote( parent[&amp;quot;in&amp;quot;] )&lt;br /&gt;
boxOutPlug = Gaffer.BoxIO.promote( parent[&amp;quot;out&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Promote useful Node Editor plugs&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;name&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( parent[&amp;quot;parent&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;divisions&amp;quot;] )&lt;br /&gt;
Gaffer.BoxIO.promote( plane[&amp;quot;dimensions&amp;quot;] )&lt;br /&gt;
 &lt;br /&gt;
# Add a passthrough&lt;br /&gt;
boxOutNode = boxOutPlug.getInput().node()&lt;br /&gt;
boxInNode = boxInPlug.outputs()[0].node()&lt;br /&gt;
boxOutNode[&amp;quot;passThrough&amp;quot;].setInput( boxInNode[&amp;quot;out&amp;quot;] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Installing_Gaffer_on_AWS_with_EC2_Image_Builder&amp;diff=373</id>
		<title>Installing Gaffer on AWS with EC2 Image Builder</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Installing_Gaffer_on_AWS_with_EC2_Image_Builder&amp;diff=373"/>
		<updated>2024-07-22T17:25:15Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a guide to setting up Amazon Web Services EC2 Image Builder with Gaffer. The EC2 Image Builder is a service from AWS that allows you to create an &#039;&#039;&#039;AMI&#039;&#039;&#039; (Amazon Machine Image) for use with EC2 with all the software and configuration needed to run jobs on AWS. An &#039;&#039;&#039;EC2 instance&#039;&#039;&#039; is a preset configuration of processor, memory and network resources made available by AWS - the virtual equivalent of hardware. An EC2 instance needs to be launched with an AMI that defines the virtual machine that will be running - the operating system and installed software.&lt;br /&gt;
&lt;br /&gt;
This guide is intended for users with some AWS knowledge but not expert level. After completing this guide, you will have a Linux-based EC2 AMI available to your AWS users that can be run on any EC2 instance type for running Gaffer tasks, optionally including Arnold rendering. These instances can be launched manually through the AWS management console, via Python scripts or the AWS terminal, or from applications like Thinkbox&#039;s Deadline.&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
The EC2 Image Builder is split into a number of different parts. We need to work with three of those to create our Gaffer image. [https://docs.aws.amazon.com/imagebuilder/latest/userguide/start-build-image-pipeline.html &#039;&#039;&#039;Image Pipelines&#039;&#039;&#039;] are run to create the AMI. With Image Pipelines, you determine the schedule with which it will be run (likely Manual for our purposes) and the Image Recipe that will be used to create the AMI.&lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;[https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-recipes.html Image Recipe]&#039;&#039;&#039; consists the the base image AMI that will be the starting point for the new AMI, optional additional storage to attach to the instance and a set of components that define the software to be installed.&lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;[https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-components.html Component]&#039;&#039;&#039; is a YAML script describing the set of steps to be performed to install and configure some logical unit of software.&lt;br /&gt;
&lt;br /&gt;
Image Recipes and Components are both versioned. When you save, that version becomes immutable and you will create a new version when making changes or fixes.&lt;br /&gt;
&lt;br /&gt;
This guide will show you how to create Components that can be used to install any version of Gaffer and Arnold through the use of Component parameters. This means you can only have one version of Gaffer and Arnold installed on your AMI. Alternatively, you could create a new Component for each version of Gaffer / Arnold then choose multiple components to install on the same AMI.&lt;br /&gt;
&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
To start, log into the AWS Console. Pay attention to the region you have set - this setting is in the top-right of the console. The EC2 Image Builder components as well as the generated AMIs (and therefore the EC2 instances you launch with them) are unique per-region.&lt;br /&gt;
&lt;br /&gt;
Choose EC2 Image Builder from the Services menu in the top-left. You can navigate between Image Pipelines, Image Recipes and Components from the left-side menu.&lt;br /&gt;
&lt;br /&gt;
=== Gaffer Component ===&lt;br /&gt;
From the navigation menu, choose Components to see a list of your customized components, which is empty if this is your first time using it.&lt;br /&gt;
&lt;br /&gt;
# Choose &amp;quot;Create component&amp;quot;.&lt;br /&gt;
# Keep &amp;quot;Component type&amp;quot; set to &amp;lt;code&amp;gt;Build&amp;lt;/code&amp;gt;&lt;br /&gt;
# Set the Component Details&lt;br /&gt;
## Choose &amp;lt;code&amp;gt;Linux&amp;lt;/code&amp;gt; as the &amp;quot;Image operating system&amp;quot;. This indicates this recipe is compatible with Linux-based Image Pipelines. You determine the operating system for your AMI in the Image Recipe configuration.&lt;br /&gt;
## From &amp;quot;Compatible OS Versions&amp;quot;, choose the Linux versions you intend for this recipe to be compatible with.&lt;br /&gt;
## Enter a Component name and optionally a description.&lt;br /&gt;
## Enter a Component version, such as &amp;lt;code&amp;gt;1.0.0&amp;lt;/code&amp;gt;&lt;br /&gt;
# Set the Content. This is the YAML script that will be run to install Gaffer. The YAML script below will setup Gaffer.&lt;br /&gt;
# Finally, choose &amp;quot;Save Component&amp;quot; at the bottom of the screen to save it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;YAML&amp;quot;&amp;gt;&lt;br /&gt;
name: Gaffer Component&lt;br /&gt;
description: Installs software needed for running Gaffer on AWS.&lt;br /&gt;
schemaVersion: 1.0&lt;br /&gt;
&lt;br /&gt;
parameters:&lt;br /&gt;
  - gafferVersion:&lt;br /&gt;
      type: string&lt;br /&gt;
      default: &#039;1.4.7.0&#039;&lt;br /&gt;
      description: &#039;The Gaffer version to install.&#039;&lt;br /&gt;
  - gccVersion:&lt;br /&gt;
      type: string&lt;br /&gt;
      default: &#039;9&#039;&lt;br /&gt;
      description: &#039;The gcc version to target for the Gaffer install.&#039;&lt;br /&gt;
&lt;br /&gt;
phases:&lt;br /&gt;
  - name: build&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: DownloadGaffer&lt;br /&gt;
        action: WebDownload&lt;br /&gt;
        inputs:&lt;br /&gt;
          - source: https://github.com/GafferHQ/gaffer/releases/download/{{gafferVersion}}/gaffer-{{gafferVersion}}-linux-gcc{{gccVersion}}.tar.gz&lt;br /&gt;
            destination: /opt/gaffer-{{gafferVersion}}/gaffer-{{gafferVersion}}-gcc{{gccVersion}}.tar.gz&lt;br /&gt;
&lt;br /&gt;
      - name: ExtractGaffer&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - tar xvzf {{build.DownloadGaffer.inputs[0].destination}} --strip-components=1 -C /opt/gaffer-{{gafferVersion}}&lt;br /&gt;
            - rm {{build.DownloadGaffer.inputs[0].destination}}&lt;br /&gt;
            - echo &#039;GAFFER_ROOT=/opt/gaffer-{{gafferVersion}}&#039; &amp;gt;&amp;gt; .env&lt;br /&gt;
            - echo &#039;GAFFER_VERSION={{gafferVersion}}&#039; &amp;gt;&amp;gt; .env&lt;br /&gt;
&lt;br /&gt;
  - name: validate&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: ValidateGaffer&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - source .env&lt;br /&gt;
            - $GAFFER_ROOT/bin/gaffer env python -c &amp;quot;print(&#039;Gaffer validated&#039;)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;schemaVersion&amp;lt;/code&amp;gt; and the two items under &amp;lt;code&amp;gt;phases&amp;lt;/code&amp;gt; are required. The &amp;lt;code&amp;gt;parameters&amp;lt;/code&amp;gt; list is a helpful way of specifying variables. They can be referenced in multiple places, so they make your Components less error-prone. Their value can also be set from within an Image Recipe which makes for much fewer versions of your Component. Without &amp;lt;code&amp;gt;parameter&amp;lt;/code&amp;gt; lists, you would need to version up your Component with each Gaffer version.&lt;br /&gt;
&lt;br /&gt;
==== Build Phase ====&lt;br /&gt;
The first phase of a component build installs Gaffer. It first downloads Gaffer from the Github release URL. Then it extracts the archive to the &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt; directory. Finally, an environment variable is set for other components to locate Gaffer&#039;s root directory.&lt;br /&gt;
&lt;br /&gt;
The extraction step makes use of another helpful feature in Component scripts - referencing variables from a previous step. The &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{build.DownloadGaffer.inputs[0].destination}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; text indicates that the file path to extract is taken from the &amp;lt;code&amp;gt;DownloadGaffer&amp;lt;/code&amp;gt; step of the &amp;lt;code&amp;gt;build&amp;lt;/code&amp;gt; phase. This is another way to reduce errors from typos in your script.&lt;br /&gt;
&lt;br /&gt;
Note that starting with Gaffer 1.4, a gcc11 and gcc9 build is available. Which of these archives you use depends on the Linux distribution you use as your base image (see &amp;quot;Choosing a base image below&amp;quot; for more information). If you&#039;re running on Amazon Linux 2, you should use the gcc9 build as the sample above does. If you&#039;re using the newer Amazon Linux 2023, you should use the gcc11 build. &lt;br /&gt;
&lt;br /&gt;
==== Validate Phase ====&lt;br /&gt;
The second phase is for verifying that your installation succeeded. For our purposes, running a short Python script from within Gaffer will prove that the installation succeeded and Gaffer is operable. Implementing this phase helps identify problems before the AMI is created. If any of the validation steps fail, the Image Pipeline will be stopped and an error raised.&lt;br /&gt;
&lt;br /&gt;
=== Arnold Component ===&lt;br /&gt;
Creating the Arnold Component is much like the Gaffer Component, with one significant difference. Arnold&#039;s installer is not available publicly so you will need to upload it to a location that EC2 Image Builder can access. It should be kept private to your organization, which makes AWS S3 a good solution. &#039;&#039;&#039;S3&#039;&#039;&#039; is a storage repository you can manually or programmatically upload to. EC2 Image Builder Components have an action for downloading from S3, which makes it easy to incorporate into Components.&lt;br /&gt;
&lt;br /&gt;
# Upload the Arnold Linux archive to a bucket in your S3 storage and note it&#039;s URL. It will start with &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Choose &amp;quot;Create Component&amp;quot; to start a new Component.&lt;br /&gt;
# Set the Component details as you did with the Gaffer Component.&lt;br /&gt;
# An example Component Content script is below.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;YAML&amp;quot;&amp;gt;&lt;br /&gt;
name: Arnold Component&lt;br /&gt;
description: Installs Arnold for AWS.&lt;br /&gt;
schemaVersion: 1.0&lt;br /&gt;
&lt;br /&gt;
parameters:&lt;br /&gt;
  - arnoldVersion:&lt;br /&gt;
      type: string&lt;br /&gt;
      default: &#039;7.2.5.3&#039;&lt;br /&gt;
      description: &#039;The Arnold version to install.&#039;&lt;br /&gt;
&lt;br /&gt;
phases:&lt;br /&gt;
  - name: build&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: DownloadArnold&lt;br /&gt;
        action: S3Download&lt;br /&gt;
        inputs:&lt;br /&gt;
          - source: s3://hypotheticalinc-image-sources/Arnold-{{arnoldVersion}}-linux.tgz&lt;br /&gt;
            destination: /opt/arnoldRoot/arnold-{{arnoldVersion}}.tgz&lt;br /&gt;
&lt;br /&gt;
      - name: ExtractArnold&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - mkdir /opt/arnoldRoot/{{arnoldVersion}}&lt;br /&gt;
            - tar xvzf {{build.DownloadArnold.inputs[0].destination}} -C /opt/arnoldRoot/{{arnoldVersion}}&lt;br /&gt;
            - rm {{build.DownloadArnold.inputs[0].destination}}&lt;br /&gt;
            - echo &#039;export ARNOLD_ROOT=/opt/arnoldRoot/{{arnoldVersion}}&#039; &amp;gt;&amp;gt; .env&lt;br /&gt;
&lt;br /&gt;
  - name: validate&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: ValidateGafferArnold&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - source .env&lt;br /&gt;
            - $GAFFER_ROOT/bin/gaffer env python -c &amp;quot;import GafferArnold;GafferArnold.ArnoldLight()&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;The Arnold component is similar to the Gaffer Component. Instead of downloading from a publicly available Github release, we get the Arnold archive from our S3 upload. This method can be used for other private archives you may need to install, such as non-public Gaffer extensions.&lt;br /&gt;
&lt;br /&gt;
The validation phase once again runs a Python script in Gaffer. Here we simply import &amp;lt;code&amp;gt;GafferArnold&amp;lt;/code&amp;gt; and attempt to create an &amp;lt;code&amp;gt;ArnoldLight&amp;lt;/code&amp;gt; to verify it is working.&lt;br /&gt;
&lt;br /&gt;
=== Gaffer Prerequisites Component ===&lt;br /&gt;
Depending on the which Linux distribution you are using as the base image for your Image Pipeline, you may need some additional packages installed to run Gaffer. The Amazon Linux 2 base image, for example, needs &amp;lt;code&amp;gt;mesa-libGL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;mesa-libGLU&amp;lt;/code&amp;gt; to run Gaffer tasks. These can be installed with the following Component.&amp;lt;syntaxhighlight lang=&amp;quot;YAML&amp;quot;&amp;gt;&lt;br /&gt;
name: Gaffer Prerequisites&lt;br /&gt;
description: Required components to run Gaffer.&lt;br /&gt;
schemaVersion: 1.0&lt;br /&gt;
&lt;br /&gt;
phases:&lt;br /&gt;
  - name: build&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: installPackages&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - yum install -y mesa-libGL mesa-libGLU&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Image Recipe ===&lt;br /&gt;
With your Components defined, you can now create the Image Recipe for generating your AMI. Choose &amp;quot;Image Recipes&amp;quot;. The Recipe list will be empty if you haven&#039;t created on yet.&lt;br /&gt;
&lt;br /&gt;
# Choose &amp;quot;Create image recipe&amp;quot;.&lt;br /&gt;
# Enter a name for your Recipe and optionally a description.&lt;br /&gt;
# Enter a version number, such as &amp;lt;code&amp;gt;1.0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Select an AMI to use as the base image for the Recipe. See the section below for more information about choosing a base image.&lt;br /&gt;
# Enable your &amp;lt;code&amp;gt;Gaffer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Arnold&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;GafferPrerequisites&amp;lt;/code&amp;gt; Components you created above. You may need to change the search filter to &amp;lt;code&amp;gt;Owned by me&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As you enable each Component, it will be added to the &amp;quot;Selected Components&amp;quot; section.&lt;br /&gt;
# The Components will be added in the order they are listed. Make sure the &amp;lt;code&amp;gt;GafferPrerequisites&amp;lt;/code&amp;gt; Component is first, followed by &amp;lt;code&amp;gt;Gaffer&amp;lt;/code&amp;gt; and then &amp;lt;code&amp;gt;Arnold&amp;lt;/code&amp;gt;. If they are not in the correct order, validation will fail.&lt;br /&gt;
# You can modify the version number to install in the &amp;quot;Selected Components&amp;quot; section. This is also where you can change the installed versions when making a new version of this Image Recipe for future releases.&lt;br /&gt;
# It is also a good idea to change the &amp;quot;Versioning Options&amp;quot; of your Components to &amp;lt;code&amp;gt;Use latest available component version&amp;lt;/code&amp;gt;. This will ensure the latest component is used without needing to create new Image Recipe versions each time a Component is modified. Using the version variables in your Component allows you to update new software versions without needing to change the Component, only the Recipe.&lt;br /&gt;
# If you need to attach additional storage, you can do that in the &amp;quot;Storage (volumes)&amp;quot; section. This can be useful for storing assets on the instance.&lt;br /&gt;
# Choose &amp;quot;Create Recipe&amp;quot; to complete the Recipe.&lt;br /&gt;
&lt;br /&gt;
==== Choosing a base image ====&lt;br /&gt;
The base image is the AMI that will be used as the starting point for your customized AMI. It is booted first, your components are added to it, and a snapshot is taken to be used as your custom AMI.&lt;br /&gt;
&lt;br /&gt;
The Image Recipe creation wizard allows you to choose a variety of standard Linux distributions, images you&#039;ve subscribed to from the AWS Marketplace, or a manually entered AMI ID.&lt;br /&gt;
&lt;br /&gt;
If you are using Deadline, entering an AMI ID can be helpful for identifying the AMI provided by AWS / Thinkbox that has the needed Deadline components preinstalled. One way of finding this ID is to launch a Deadline worker from the AWS Portal in the Deadline Monitor. There are various options for AMIs with DCC software preinstalled. You can choose the generic Linux worker for a clean base AMI. Once the machine has started, go to your EC2 Instances in the AWS Web Console, locate the running instance and note its AMI ID.&lt;br /&gt;
&lt;br /&gt;
=== Image Pipeline ===&lt;br /&gt;
The final step is to create an Image Pipeline. Choose Image Pipelines from the navigation menu to see the list of Pipelines, which is empty if this is your first time using it.&lt;br /&gt;
&lt;br /&gt;
# Choose &amp;quot;Create Image Pipeline&amp;quot; to get started.&lt;br /&gt;
# Enter a name for the pipeline and optionally a description.&lt;br /&gt;
# Change the schedule option to &amp;quot;Manual&amp;quot; so it will only run when you trigger it.&lt;br /&gt;
# Proceed to the next step, to configure the Recipe.&lt;br /&gt;
# Choose the Recipe you created above.&lt;br /&gt;
# Proceed through the remaining wizard screens, accepting the default values.&lt;br /&gt;
When you are finished, refresh the Image Pipeline list. Select your new Pipeline and choose &amp;quot;Run Pipeline&amp;quot; from the &amp;quot;Actions&amp;quot; menu. Choosing the Pipeline in the list will take you to a list of Pipeline runs, your new submission should be in the &amp;lt;code&amp;gt;Building&amp;lt;/code&amp;gt; status.&lt;br /&gt;
&lt;br /&gt;
Once the image has been built and validated, it will be available in your EC2 AMI list for use!&lt;br /&gt;
&lt;br /&gt;
=== Debugging ===&lt;br /&gt;
If your AMI is not successfully built, you can view the log stream for a detailed description of the steps EC2 Image Builder performed, including the results of your Component validation steps. This can be useful for finding what went wrong and making corrections to your Components.&lt;br /&gt;
&lt;br /&gt;
=== Updating Installed Versions ===&lt;br /&gt;
&lt;br /&gt;
# Select your Image Recipe.&lt;br /&gt;
# Choose &amp;quot;Create new version&amp;quot;.&lt;br /&gt;
# Enter the new version numbers in the Component settings.&lt;br /&gt;
# Either create a new Image Pipeline for your new Image Recipe, or modify the existing Image Pipeline to use it in place.&lt;br /&gt;
&lt;br /&gt;
=== GafferDeadline Component ===&lt;br /&gt;
Though it isn&#039;t necessary for using Gaffer on AWS, if you are using Deadline to queue jobs to AWS, you will also need to have GafferDeadline installed. The following script can be used as a Component for GafferDeadline.&amp;lt;syntaxhighlight lang=&amp;quot;YAML&amp;quot;&amp;gt;&lt;br /&gt;
name: GafferDeadline Component&lt;br /&gt;
description: Installs GafferDeadline for AWS.&lt;br /&gt;
schemaVersion: 1.0&lt;br /&gt;
&lt;br /&gt;
parameters:&lt;br /&gt;
  - gafferDeadlineVersion:&lt;br /&gt;
      type: string&lt;br /&gt;
      default: &#039;0.58.0.0b2&#039;&lt;br /&gt;
      description: &#039;The GafferDeadline version to install.&#039;&lt;br /&gt;
&lt;br /&gt;
phases:&lt;br /&gt;
  - name: build&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: DownloadGafferDeadline&lt;br /&gt;
        action: WebDownload&lt;br /&gt;
        inputs:&lt;br /&gt;
          - source: https://github.com/hypothetical-inc/GafferDeadline/archive/refs/tags/{{gafferDeadlineVersion}}.tar.gz&lt;br /&gt;
            destination: /opt/GafferDeadline/gafferDeadline{{gafferDeadlineVersion}}.tar.gz&lt;br /&gt;
&lt;br /&gt;
      - name: ExtractGafferDeadline&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - tar -xvzf {{build.DownloadGafferDeadline.inputs[0].destination}} --strip-components=1 -C /opt/GafferDeadline&lt;br /&gt;
            - rm {{build.DownloadGafferDeadline.inputs[0].destination}}&lt;br /&gt;
&lt;br /&gt;
  - name: validate&lt;br /&gt;
    steps:&lt;br /&gt;
      - name: ValidateGafferDeadline&lt;br /&gt;
        action: ExecuteBash&lt;br /&gt;
        inputs:&lt;br /&gt;
          commands:&lt;br /&gt;
            - source .env&lt;br /&gt;
            - export GAFFER_EXTENSION_PATHS=/opt/GafferDeadline&lt;br /&gt;
            - $GAFFER_ROOT/bin/gaffer env python -c &amp;quot;import GafferDeadline;print(help(GafferDeadline))&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Adding_Presets_to_Global_Context_Variables&amp;diff=372</id>
		<title>Adding Presets to Global Context Variables</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Adding_Presets_to_Global_Context_Variables&amp;diff=372"/>
		<updated>2024-07-22T17:23:14Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Global context variables can be useful for ensuring a context variable is always accessible and set for the entire script. Unless it is overridden by a [https://www.gafferhq.org/documentation/1.4.8.0/Reference/NodeReference/Gaffer/ContextVariables.html ContextVariables] or  [https://www.gafferhq.org/documentation/1.4.8.0/Reference/NodeReference/Gaffer/ContextVariableTweaks.html ContextVariablesTweaks] node in the node graph, all nodes will have access to the same global context variable. There are some global context variables added by Gaffer by default when creating a new script. You can see these, and add your own, from the &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt; tab in the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; dialog accessed from the &amp;lt;code&amp;gt;File.Settings&amp;lt;/code&amp;gt;menu item.&lt;br /&gt;
&lt;br /&gt;
The default user interface for a global context variable can be useful for many situations, but like all plugs in Gaffer, it is possible to customize how the plugs for global context variables are presented to the user. One such customization is adding presets for the user to choose from. This ensures only valid values can be assigned to the context variable. This guide will describe how to add presets to the widget controlling a global context variable.&lt;br /&gt;
&lt;br /&gt;
=== Adding Presets to the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; Dialog ===&lt;br /&gt;
Presets can be added to plugs in the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; dialog by assigning metadata to the value plug for a global context variable. There are three steps to adding the needed metadata.&lt;br /&gt;
&lt;br /&gt;
==== Identify the target plug ====&lt;br /&gt;
Before we assign metadata, we need to identify which plug represents the context variable you want to add presets to. Global context variables are stored in the &amp;lt;code&amp;gt;variables&amp;lt;/code&amp;gt; child of the script node. In the Python editor, these are accessed by &amp;lt;code&amp;gt;root[&amp;quot;variables&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The entries in &amp;lt;code&amp;gt;root[&amp;quot;variables&amp;quot;]&amp;lt;/code&amp;gt; are of the type &amp;lt;code&amp;gt;NameValuePlug&amp;lt;/code&amp;gt;. These plugs themselves have three child plugs: &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; plug sets the name of the context variable and the &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; sets the value of the variable. The &amp;lt;code&amp;gt;enabled&amp;lt;/code&amp;gt; plug is used to enable or disable the variable. Note that the value of &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; may be different from the name of the actual &amp;lt;code&amp;gt;NameValuePlug&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To determine which child of &amp;lt;code&amp;gt;root[&amp;quot;variables&amp;quot;]&amp;lt;/code&amp;gt; you want to add metadata to, loop through all the children of &amp;lt;code&amp;gt;root[&amp;quot;variables&amp;quot;]&amp;lt;/code&amp;gt; checking the value of the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; plug until you find the variable. The corresponding &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; child plug will be the plug we add metadata to.&lt;br /&gt;
&lt;br /&gt;
==== Set the widget type ====&lt;br /&gt;
Next we need to tell Gaffer to use a presets dropdown widget for our value plug. That is done by setting &amp;lt;code&amp;gt;plugValueWidget:type&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;quot;GafferUI.PresetsPlugValueWidget&amp;quot;&amp;lt;/code&amp;gt; : &amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.Metadata.registerValue( variablePlug, &amp;quot;plugValueWidget:type&amp;quot;, &amp;quot;GafferUI.PresetsPlugValueWidget&amp;quot; )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set the preset names and values ====&lt;br /&gt;
Now we add the presets themselves. This can be done in one of two ways. You can add a metadata entry starting with &amp;lt;code&amp;gt;preset:&amp;lt;/code&amp;gt; followed by the display string of your preset set to the preset value. For example, &amp;lt;code&amp;gt;Gaffer.Metadata.registerValue( variablePlug, &amp;quot;preset:Shot 1004&amp;quot;, &amp;quot;shot1004&amp;quot;)&amp;lt;/code&amp;gt;will create a preset showing in the UI as &amp;lt;code&amp;gt;Shot 1004&amp;lt;/code&amp;gt;. When the user chooses that preset, the actual value of the context variable will be &amp;lt;code&amp;gt;shot1004&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The second method sets all of the preset names and all of the values in one step each. This can be done by setting the &amp;lt;code&amp;gt;presetNames&amp;lt;/code&amp;gt; metadata entry to a Cortex string vector (&amp;lt;code&amp;gt;IECore.StringVectorData&amp;lt;/code&amp;gt;)holding all of the preset names. Setting &amp;lt;code&amp;gt;presetValues&amp;lt;/code&amp;gt; to a Cortex vector of some type, such as &amp;lt;code&amp;gt;IECoreStringVectorData&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;IECoreFloatVectorData&amp;lt;/code&amp;gt;.&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
Gaffer.Metadata.registerValue( variablePlug, &amp;quot;presetNames&amp;quot;, IECore.StringVectorData( presetNames ) )&lt;br /&gt;
Gaffer.Metadata.registerValue( variablePlug, &amp;quot;presetValues&amp;quot;, IECore.StringVectorData( presetValues ) )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Resetting the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; dialog ====&lt;br /&gt;
&lt;br /&gt;
Note that the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; UI is built once and then reused for subsequent showings. This means after adding presets, you need to force Gaffer to refresh the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; dialog. You can force Gaffer to rebuild the &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; dialog by running the following code in the Python editor:&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
sw = GafferUI.ScriptWindow.acquire(root)&lt;br /&gt;
for window in sw.childWindows():&lt;br /&gt;
	if hasattr( window, &amp;quot;_settingsEditor&amp;quot; ) :&lt;br /&gt;
		sw.removeChild(window)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have registered the metadata for a plug in this way, it will be saved with the script so there is no need to register the metadata again.&lt;br /&gt;
&lt;br /&gt;
==== Example Script ====&lt;br /&gt;
&lt;br /&gt;
The following script is an example of all of the above steps condensed into a reusable function for adding presets for a global context variable.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# Sets the preset names and values for a global context variable.&lt;br /&gt;
# `scriptNode` is the script node which you want to set global context variables for.&lt;br /&gt;
# `variableName` is the name of the variable to set presets for.&lt;br /&gt;
# `presetNames` is a list of strings to set the preset names to.&lt;br /&gt;
# `presetValues` is an `IECore.*VectorData` holding a list of preset values&lt;br /&gt;
# for each corresponding item in `presetNames`&lt;br /&gt;
def globalContextPresets( scriptNode, variableName, presetNames, presetValues ) :&lt;br /&gt;
	if len( presetNames ) != len( presetValues ) :&lt;br /&gt;
		raise ValueError( &amp;quot;`presetNames` and `presetValues` must be the same length&amp;quot; )&lt;br /&gt;
	&lt;br /&gt;
	# Find the value plug for `variableName` global context variable.&lt;br /&gt;
	variablePlug = None&lt;br /&gt;
	for p in scriptNode[&amp;quot;variables&amp;quot;] :&lt;br /&gt;
		if p[&amp;quot;name&amp;quot;].getValue() == variableName :&lt;br /&gt;
			variablePlug = p[&amp;quot;value&amp;quot;]&lt;br /&gt;
			break&lt;br /&gt;
&lt;br /&gt;
	if variablePlug is None :&lt;br /&gt;
		raise RuntimeError( &amp;quot;\&amp;quot;{}\&amp;quot; not found in global context variables&amp;quot;.format( variableName ) )&lt;br /&gt;
	&lt;br /&gt;
	# Set the widget to a presets dropdown.&lt;br /&gt;
	Gaffer.Metadata.registerValue( variablePlug, &amp;quot;plugValueWidget:type&amp;quot;, &amp;quot;GafferUI.PresetsPlugValueWidget&amp;quot; )&lt;br /&gt;
&lt;br /&gt;
	# Register the names and values.&lt;br /&gt;
	Gaffer.Metadata.registerValue( variablePlug, &amp;quot;presetNames&amp;quot;, IECore.StringVectorData( presetNames ) )&lt;br /&gt;
	Gaffer.Metadata.registerValue( variablePlug, &amp;quot;presetValues&amp;quot;, presetValues )&lt;br /&gt;
&lt;br /&gt;
	# Set the value to the first preset.&lt;br /&gt;
	variablePlug.setValue( presetValues[0] )&lt;br /&gt;
&lt;br /&gt;
	# Tell Gaffer to rebuild the `Settings` dialog.&lt;br /&gt;
	sw = GafferUI.ScriptWindow.acquire( scriptNode )&lt;br /&gt;
	for window in sw.childWindows():&lt;br /&gt;
		if hasattr( window, &amp;quot;_settingsEditor&amp;quot; ) :&lt;br /&gt;
			sw.removeChild(window)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;For example, to set a few presets for a global context variable named &amp;lt;code&amp;gt;shot&amp;lt;/code&amp;gt; run the script above in the Python editor, then run the line below.&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
globalContextPresets( root, &amp;quot;shot&amp;quot;, [&amp;quot;shot A&amp;quot;, &amp;quot;shot B&amp;quot;, &amp;quot;shot C&amp;quot;], IECore.StringVectorData( [ &amp;quot;shotA&amp;quot;, &amp;quot;shotB&amp;quot;, &amp;quot;shotC&amp;quot; ] ) )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=357</id>
		<title>Template:LatestGafferVersion</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Template:LatestGafferVersion&amp;diff=357"/>
		<updated>2024-07-18T00:05:01Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;1.4.9.0&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=UV_camera_projection&amp;diff=355</id>
		<title>UV camera projection</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=UV_camera_projection&amp;diff=355"/>
		<updated>2024-07-09T23:03:01Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Example&lt;br /&gt;
|description=Example of using a MapProjection node to project a new UV set from camera.&lt;br /&gt;
|nodes=Camera, MapProjection&lt;br /&gt;
|file=MapProjectionExample.gfr&lt;br /&gt;
|screenshots=MapProjectionScreenshot.png&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=349</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=349"/>
		<updated>2024-07-05T00:38:13Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
[[File:SelectionTool.mp4|description=Selection tool modes allowing selection based on attributes such as usd:kind.]]&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[File:HighlightPlacement.mp4|description=Place highlights with the Light Tool&#039;s Highlight mode.]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.|frame]]&lt;br /&gt;
[[File:SlashTool.gif|none|frame|Hold &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; to cut connections between nodes.]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=348</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=348"/>
		<updated>2024-07-05T00:36:27Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
[[File:SelectionTool.mp4|description=Selection tool modes allowing selection based on attributes such as usd:kind.]]&lt;br /&gt;
[[File:HighlightPlacement.mp4|description=Place highlights with the Light Tool&#039;s Highlight mode.]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.|frame]]&lt;br /&gt;
[[File:SlashTool.gif|none|frame|Hold &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; to cut connections between nodes.]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:HighlightPlacement.mp4&amp;diff=347</id>
		<title>File:HighlightPlacement.mp4</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:HighlightPlacement.mp4&amp;diff=347"/>
		<updated>2024-07-05T00:35:13Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Demonstration of the highlight placement mode of the Light Tool&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=346</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=346"/>
		<updated>2024-07-05T00:32:22Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Viewer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
[[File:SelectionTool.mp4|description=Selection tool modes allowing selection based on attributes such as usd:kind.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.|frame]]&lt;br /&gt;
[[File:SlashTool.gif|none|frame|Hold &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; to cut connections between nodes.]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:SelectionTool.mp4&amp;diff=345</id>
		<title>File:SelectionTool.mp4</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:SelectionTool.mp4&amp;diff=345"/>
		<updated>2024-07-05T00:14:14Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Demonstrating using the SelectionTool&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=344</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=344"/>
		<updated>2024-07-04T00:46:33Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Graph Editor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.|frame]]&lt;br /&gt;
[[File:SlashTool.gif|none|frame|Hold &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; to cut connections between nodes.]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:SlashTool.gif&amp;diff=343</id>
		<title>File:SlashTool.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:SlashTool.gif&amp;diff=343"/>
		<updated>2024-07-04T00:45:20Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Demonstration of using the X shortcut to cut connections in the Graph Editor&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=342</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=342"/>
		<updated>2024-07-04T00:44:02Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Graph Editor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.|frame]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=341</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=341"/>
		<updated>2024-07-04T00:43:03Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: /* Graph Editor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:ConnectToBookmark.gif|none|thumb|650x650px|Using &amp;lt;code&amp;gt;Connect Bookmark&amp;lt;/code&amp;gt; to quickly make connections from bookmarked nodes.]]&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=File:ConnectToBookmark.gif&amp;diff=340</id>
		<title>File:ConnectToBookmark.gif</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=File:ConnectToBookmark.gif&amp;diff=340"/>
		<updated>2024-07-04T00:41:22Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Demonstration of connecting to bookmarked nodes&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
	<entry>
		<id>https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=339</id>
		<title>Shortcuts and UI Interactions</title>
		<link rel="alternate" type="text/html" href="https://www.gaffer.wiki/w/index.php?title=Shortcuts_and_UI_Interactions&amp;diff=339"/>
		<updated>2024-07-04T00:38:43Z</updated>

		<summary type="html">&lt;p&gt;Murraystevenson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page provides examples of shortcut usage and UI interactions. A comprehensive list of shortcuts can be found in the [http://www.gafferhq.org/documentation/{{LatestGafferVersion}}/Interface/ControlsAndShortcuts/index.html Controls And Shortcuts] page of the Gaffer documentation.&lt;br /&gt;
&lt;br /&gt;
=== Manipulators ===&lt;br /&gt;
[[File:GafferManipulatorScale.gif|frame|Manipulator size can be decreased with &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; and increased with &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:RotateManipHotkeys.gif|frame|Hold &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; while rotating to make precise adjustments. Hold &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; to step in 30 degree increments.|none]]&lt;br /&gt;
[[File:SnapToObjectInViewer.gif|frame|Hold &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; with the translate or rotate tools enabled to snap or aim at the point clicked.|none]]&lt;br /&gt;
[[File:MultiSelectSnapTo.gif|frame|Holding &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; to snap and aim also works with multiple selected objects.|none]]&lt;br /&gt;
=== Expressions and the Python Editor ===&lt;br /&gt;
[[File:PlugDragDropPythonEditor.gif|frame|Drag a plug from the Node Editor into the Python Editor for easy access.|none]]&lt;br /&gt;
[[File:ExpressionPlugDragDrop.gif|frame|Drag a plug into the expression editor to access the plug’s value.|none]]&lt;br /&gt;
=== Editing Plug Values ===&lt;br /&gt;
[[File:GangPlugs.gif|frame|Gang plug values together with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:GafferPlugMath.gif|frame|Math operators can be used to edit plug values.|none]]&lt;br /&gt;
[[File:VirtualSliders.gif|frame|Edit plug values by holding &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt; and click-dragging left or right.|none]]&lt;br /&gt;
[[File:CursorValueIncrement.gif|frame|Edit plug values with the &amp;lt;code&amp;gt;Up&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Down&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Right&amp;lt;/code&amp;gt; cursor keys.|none]]&lt;br /&gt;
=== Viewer ===&lt;br /&gt;
[[File:GafferVisualisersMenu.gif|none|frame|Adjusting visualiser scale in the Viewer.]]&lt;br /&gt;
[[File:CameraCopyFromViewer.gif|frame|Copy camera position from the Viewer.|none]]&lt;br /&gt;
[[File:SelectionMasking.gif|frame|Use selection masking to limit what is selectable in a Viewer.|none]]&lt;br /&gt;
=== Graph Editor ===&lt;br /&gt;
[[File:SelectAffectedObjects.gif|frame|Use &amp;lt;code&amp;gt;Select Affected Objects&amp;lt;/code&amp;gt; to quickly select all objects affected by a filtered node.|none]]&lt;br /&gt;
[[File:SelectUpstreamDownstreamNodes.gif|frame|Select upstream nodes with &amp;lt;code&amp;gt;Shift&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;. Select downstream nodes with &amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Alt&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;Click&amp;lt;/code&amp;gt;.|none]]&lt;br /&gt;
[[File:MiddleDragToPin.gif|frame|Middle-drag a node from the Graph Editor to pin it to a Viewer or Editor. &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; quickly unpins a Viewer or Editor|none]]&lt;/div&gt;</summary>
		<author><name>Murraystevenson</name></author>
	</entry>
</feed>