Skip to content

14. PBR

Bram Stout edited this page Mar 10, 2026 · 2 revisions

Using PBR resource packs

MiEx creates materials using material templates. Because of this, it can support PBR resource packs by using material templates that use the PBR textures. The repository contains a few example PBR resource packs that add PBR support to the existing example material templates.

To use these, you enable the normal material template resource pack and then also the PBR variant. The PBR variant must be on top of the normal variant. For example when exporting for Blender, you'd enable the Blender resource pack and the PBR_Blender resource pack with the PBR_Blender resource pack above the Blender resource pack.

These example PBR resource packs support the following format:

Suffix Description
_roughness The roughness value.
_metalness The metalness / metallic value.
_emission The emission colour.
_normal Tangent space normal map with +Y facing up.

LabPBR support

The example PBR resource packs support a PBR format common in CG animation, but most PBR Minecraft resource packs use a format called LabPBR. The repository contains an example resource pack containing a PBR Generator node graph that converts LabPBR into the standard PBR format supported by the example PBR resource packs.

To use this, open up the PBR Generator tool via the Tools dropdown in the bottom-right corner of the main window. Select the resource packs that you want to convert from LabPBR to standard PBR and also the LabPBR_To_StandardPBR resource pack. Specify whether you want the generated textures to be saved next to the original textures or into their own resource pack. Lastly, hit the Create PBR Textures button to generate the standard PBR textures. Once this is done, you can use the generated textures with the example PBR resource packs.

PBR Generator

MiEx contains a PBR Generator tool which allows you to create PBR textures from the base colour textures. The PBR Generator works via a node graph, where each node performs some operation on an image. These node graphs can be created by the user to generate the PBR textures exactly how they want.

Using the PBR Generator

The PBR Generator Tool can be opened via the Tools button. In the PBR Generator dialog, you can specify a few things. First you can specify the suffixes of utility textures. Any file that has the given suffix (ignoring file extensions) will be ignored, so that it doesn't try to generate PBR textures for the PBR textures.

On the right of the dialog, you can specify which resource packs you want to convert. It'll search these selected resource packs for textures. The node graphs that generate the PBR textures are specified in resource packs as well, and in the resource pack selector you also specify the resource packs whose PBR node graphs it should use.

At the bottom left you can specify how the PBR textures should be saved. You can either have it save next to the original textures or have it save the PBR textures into their own resource pack. With the second option, you can specify the name of the resource pack to save the PBR textures to. If a resource pack with that name doesn't yet exist, it'll make one.

Once you've specified all of the settings, you can run the PBR Generator.

Node Graphs

Node graphs are specified in JSON files located in resource packs, in the following folder: pbr/<namespace>/nodegraphs (namespace defaults to minecraft). When MiEx generates the PBR textures, it iterates over all base colour textures. For each base colour texture, it finds a single node graph to execute on that texture. That node graph should then generate the PBR textures. Node graphs can also include other node graphs, to build on top of other node graphs or modify them, and execute other node graphs.

The basic structure of a node graph file is as follows:

{
    "priority": <priority value>,
    "selection": [ <selection patterns> ],
    "include": [ <node graph names> ],
    "executeFirst": [ <node graph names> ],
    "executeFirst.add": [ <node graph names> ],
    "outputs": [ <node names> ],
    "outputs.add": [ <node names> ],
    "nodes": {
        "<node name>": {
            "type": "node type",
            "attributes": {
                "<attribute name>": <attribute value>,
                "<attribute name>": { "conn": "<node name>.<attribute name>" },
                ...
            }
        },
        ...
    }
}

selection is a list of strings which are matched against the texture's resource identifier. They may use wildcards (*) in the strings to match any texture matching the pattern. When MiEx figures out which node graph to execute for a texture, it uses the selection strings. If the texture's resource identifier matches one of the strings, it'll use that node graph. If multiple node graphs match with that texture, then it'll use the node graph with the highest priority. If multiple node graphs match and have the same priority, it will be undefined behaviour. selection and priority are optional. If no selection list is provided or it's an empty list, then it won't be used, but can still be included by other node graphs.

executeFirst allows you to specify the resource identifiers of other node graphs which should be executed on the texture first. This helps build more modular node graph systems. executeFirst is optional.

outputs is a list of node names of the nodes that should be executed. At this moment, the only nodes possible to specify here are Write nodes. This allows you to specify which parts of the node graphs should be executed. outputs is optional.

nodes is a list of nodes. The key in the JSON object is the name of the node. Each node must have a type. If you are overriding an existing node, then type is optional. You can't change the type of an node once it's already been defined. Each node then has an optional list of attributes. The key in the attributes JSON object is the name of the attribute, which must match one of the predefined attributes of the node type. The value is the value to set the attribute to. Attributes can also be connected to other attributes, this is done by having the value be a JSON object with a key of conn and whose value specified the attribute to connect into the current attribute. It's specified via the syntax <node name>.<attribute name>. If only a node name is provided, it will default to the first output attribute on that node that it finds.

include is a list of node graph resource identifiers that should be imported in. The node graph data is first read from the included node graphs, in the order specified, and then the data in the current file is applied. It effectively works the same as copy and pasting in nodes. This allows you to build on top of other node graphs and to be able to override specific parts of them. If a resource pack has a node graph with a specific name, then it'll override all other node graphs with the same name from resource packs that are lower on the active resource packs list. This allows you to fully override the bahaviour of node graphs specified in other resource packs. If instead of fully overriding, you want to modify other node graphs, then you can add in the include list the name of the current node graph. It'll then include the node graph that you are overriding, allowing you to modify it.

Included node graphs also set the main properties of node graphs (selection, executeFirst, priority, outputs). If the current node graph file doesn't specify these properties, it'll use them from the included node graphs. If it does specify these properties, then it'll override it. executeFirst and outputs have the optional variant of executeFirst.add and outputs.add. These variants don't override the values coming from the included node graphs, but instead will add to those values.

Nodes

MiEx provides a set of node types that can be used to build up the node graphs. Each node has attributes on it. An attribute can be an input or an output. Output attributes have their values calculated by the node. Input attributes affect how the node calculates the output attributes. The node graph JSON files can either specify the value of an attribute directly or connect attributes to other attributes. If an attribute is connected to another attribute, then its value will be the same as the value it's connected to.

Attribute types

Attributes can be of a few different types. MiEx will automatically convert types from one to another, so they can easily be connected to each other. Here are the different attribute types:

Type JSON Syntax Conversion
boolean true | false Numbers convert to true if the integer value is larger than 0. Strings convert to true if they start with t or y, to false if they start with f or n, or to true if the parsed integer value is larger than 0. RGBA and images convert to true if the red channel of the top-left pixel is larger or equal to 0.5.
enum "option" The value is stored as the index in the options list. Numbers convert to the index directly. Booleans convert to an index of 1 if true and 0 if false. Strings convert to the index by finding the index of the option matching the string or by parsing them as an integer. RGBA and images convert to the index by taking the integer value of the red channel of the top-left pixel.
float 0.0 Numbers are directly converted. Booleans convert to 1.0 if true and 0.0 if false. Strings convert to the value by parsing them as a float. RGBA and images convert to the value by taking the red channel of the top-left pixel.
image [0.0, 0.0, 0.0, 0.0] Values, other than images, are converted into RGBA first and then a constant image entire filled with that RGBA value is then created.
int 0 Numbers are directly converted. Booleans convert to 1 if true and 0 if false. Strings convert to the value by parsing them as an integer. RGBA and images convert to the value by taking the integer value of the red channel of the top-left pixel.
rgba [0.0, 0.0, 0.0, 0.0] Numbers are converted into RGBA by setting every component to that number. Booleans convert to all components being 1.0 if true and 0.0 if false. Strings convert into RGBA by parsing the string as a float and setting every component to that number. Images are converted into RGBA by sampling the top-left pixel.
string "value" Numbers are converted into strings directly. Booleans convert to "true" if true and "false" is false. RGBA and images are converted by taking the red channel of the top-left pixel and converting it into a string directly.

Images can be one of two kinds: constant images and raster images. Constant images represent a single constant value over the entire image. Raster images are normal images with pixels. Constant images are used as an optimisation strategy, especially when converting non-image values into images. Images use un-associated alpha, just like the PNG file format. This means that the RGB channels represent the colour of the object and the A channel represents the opacity of the object. Associated alpha would mean that the RGB channels represent the amount of light emitted and the A channel represents the transmittance of light behind the object, through the object (in other words, how much light passes through). Given the context of the PBR Generator, all images internally use un-associated alpha. When sampling an image with interpolation, it will assume the image is using un-associated alpha and will use the corresponding math. All nodes will similarly use the appropriate maths for un-associated alpha images.

Enums have a list of values that it can be, called its options. Under the hood, which option is selected is stored as an integer indicating the index of the selected option in the list of options. So, you can both specify the string value of the option or the index of the option.

The JSON syntax provides shows how MiEx will guess the value type when specified in the JSON file. This means that you can provide a value with a different type than the type of the attribute and MiEx will automatically convert it to the type of the attribute. So an rgba typed attribute can be given a value with [0.0, 0.0, 0.0, 0.0] but also with '0', 0.0, 'false', and '"0.0"', which are all equivalent.

Node types

AlphaMode

Allows you to convert an image between associated alpha and un-associated alpha.

Attribute Type Default Value Description
input image [0,0,0,1] The input image
conversion enum "un-associated to associated" Which way to convert. Possible options are: "un-associated to associated" and "associated to un-associated"
output image [0,0,0,1] The output image

Blend

Blends two images together based on a factor. Each channel is blended independently.

Attribute Type Default Value Description
bottom image [0,0,0,1] The image on the bottom layer.
top image [0,0,0,1] The image on the top layer.
factor image [0,0,0,1] The image specifying the blending factor. It uses the red channel for the blending factor. A value of 0.0 is 100% the bottom layer and a value of 1.0 is 100% the top layer.
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the two input images.

BlendNormals

Blends two normal map images together based on a factor. The RGB images are interpreted as tangent-space normal maps with all values in the range of 0.0 - 1.0. The resulted normal is also normalised.

Attribute Type Default Value Description
bottom image [0,0,0,1] The image on the bottom layer.
top image [0,0,0,1] The image on the top layer.
factor image [0,0,0,1] The image specifying the blending factor. It uses the red channel for the blending factor. A value of 0.0 is 100% the bottom layer and a value of 1.0 is 100% the top layer.
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the two input images.

Blur

Applies some kind of blur over the image.

Attribute Type Default Value Description
input image [0,0,0,1] The input image to blur.
radiusX float 1.0 The radius in the horizontal direction.
radiusY float 1.0 The radius in the vertical direction.
radiusMode enum "pixels" How the radius is specified. Possible options are: "pixels" (The value is in pixels), "0-1" (The value is a resolution-independent value where a value of 1.0 means the width or height of the image), and "everything" (Resolution is ignored and every pixel is blurred together into a single pixel image).
radiusShape enum "round" The shape of the radius. Possible options are: "round" and "square".
kernel enum box How the pixels are blurred together. Possible options are: "box" (Every pixel within the radius contributes equally), "gaussian" (Every pixel is weighted based on the gaussian distribution), "min" (The minimum value of the pixels within the radius, calculated for each component individually. Fully transparent pixels are ignored for the RGB channels.), "max" (The maximum value of the pixels within the radius, calculated for each component individually. Fully transparent pixels are ignored for the RGB channels.), "median" (The median value of the pixels within the radius, calculated for each component individually. Fully transparent pixels are ignored for the RGB channels.), and standardDeviation (The standard deviation of the pixels within the radius, calculated for each component individually. Fully transparent pixels are ignored for the RGB channels.).
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image.

Composite

Composites two images together using a specific blend mode.

Attribute Type Default Value Description
blendMode enum "over" The blend mode used to composite the two images. Possible options are: "over", "add", "subtract", "multiply", "divide", and "screen".
bottom image [0,0,0,1] The image on the bottom / background layer.
top image [0,0,0,1] The image on the top / foreground layer.
factor image [1,1,1,1] The image specifying the compositing strength. It uses the red channel for the blending factor. A value of 0.0 is 100% the bottom layer and a value of 1.0 is 100% the composited image.
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the two input images.

Condition

Returns one of two values based on a condition. The condition is checked per pixel.

Attribute Type Default Value Description
a image [0,0,0,1] The a value.
b image [0,0,0,1] The b value.
ifFalse image [0,0,0,1] The image to sample if the condition returns false.
ifTrue image [0,0,0,1] The image to sample if the condition returns true.
condition enum "==" The condition used to compare a and b. Possible options are: "==", "!=", "<", "<=", ">", and ">=".
mode enum "r" Which channel from a and b to compare. Possible options are: "r" (Red channels are compared), "g" (Green channels are compared), "b" (Blue channels are compared), "a" (Alpha channels are compared), and "individually" (Each channel is compared individually and controls whether ifFalse or ifTrue is sampled for each of their respective channels.)
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the four input images.

GamutMap

Makes sure that all colours are within the gamut, so no colours have a negative component (a.k.a. a saturation of over 100%). The luminance of the colour is preserved. The alpha channel remains unchanged.

Attribute Type Default Value Description
input image [0,0,0,1] The input image to gamut map.
luminance RGBA [0.25, 0.625, 0.125, 0.0] The luminance coefficients used to calculate the luminance.
output image [0,0,0,1] The output image.

LUT

Applies a Look-Up-Table to the RGB values of the image. The alpha channel remains unchanged.

Attribute Type Default Value Description
input image [0,0,0,1] The input image to run through the Look-Up-Table.
lutPath string "" The name of the LUT file. It can either be a file path, or a resource identifier. For resource identifiers it looks at the path pbr/<namespace>/luts/<name>. It supports .cube, .sp1d, and .sp3d files.
inverse boolean false Whether to invert the LUT or not.
output image [0,0,0,1] The output image.

Math

Applies some math operation. The math operation is applied to each component independently.

Attribute Type Default Value Description
operator enum "add" The math operation to perform. Possible options are: "add" (a + b), "subtract" (a - b), "multiply" (a * b), "divide" (a / b), "pow" (pow(a, b)), "expn" (pow(E, a)), "exp2" (pow(2, a)), "exp10" (pow(10, a)), "logn" (logn(a)), "log2" (log2(a)), "log10" (log10(a)), "min" (min(a, b)), "max" (max(a, b)), "normalise" (a.rgb / length(a.rgb)), "dot" (dot(a.rgb, b.rgb)), "cross" (cross(a.rgb, b.rgb)), "floor" (floor(a)), ceil (ceil(a)), "fract" (a - floor(a)), and "modulo" ((a/b - floor(a/b)) * b).
a image [0,0,0,1] The a value.
b image [0,0,0,1] The b value.
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the two input images.

Matrix

Multiplies each pixel value by some matrix.

Attribute Type Default Value Description
input image [0,0,0,1] The input image
outR rgba [1,0,0,0] The coefficients used to calculate the red channel.
outG rgba [0,1,0,0] The coefficients used to calculate the green channel.
outB rgba [0,0,1,0] The coefficients used to calculate the blue channel.
outA rgba [0,0,0,1] The coefficients used to calculate the alpha channel.
output image [0,0,0,1] The output image

NormalFromBump

Calculates a tangent-space normal map from a bump map. It only looks at the red channel of the image. The red channel points to the right, the green channel points up, and the blue channel points towards you.

Attribute Type Default Value Description
input image [0,0,0,1] The input bump image.
imageSizeInUnits float 16.0 How many units big is the image.
bumpHeightInUnits float 1.0 How many units is a value of 1.0.
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
derivativeMode enum both How the derivative is calculated from the bump map. Possible options are: "left" (Derivative between current pixel and pixel to the left/top), "right" (Derivative between current pixel and pixel to the right/bottom), and "both" (Average derivative between the current pixel and the pixels on either side).
output image [0,0,0,1] The output normal map image. Each component is already in the range of 0-1.

NormaliseColor

Normalises the image so that all values sit in the specified range. It first analyses the entire image to find the lowest, highest, and middle values. It then remaps all values from the calculated range to the specified output range. Each component is remapped individually.

Attribute Type Default Value Description
input image [0,0,0,1] The input image.
outMin rgba [0,0,0,0] The output minimum value.
outMax rgba [1,1,1,1] The output maximum value.
outMidMode enum none How the middle value is remapped. Possible options are: "none" (Just do a linear remapping using the outMin and outMax), "average" (Remap the average value to be halfway between outMin and outMax), and "median" (Remap the median value to be halfway between outMin and outMax).
output image [0,0,0,1] The output image.

Read

Reads in an image.

Attribute Type Default Value Description
imageName string "@texture@" The resource identifier for the image to read. @texture@ will automatically be replaced with the resource identifier of the current texture being processed.
linearise boolean false Should the image be linearised by applying an inverse 2.2 gamma.
colorIfMissing rgba [-1,-1,-1,-1] The colour to output if the image could not be loaded.
output image [0,0,0,1] The output image.

Remap

Remaps the colours based on the ranges provided.

Attribute Type Default Value Description
input image [0,0,0,1] The input image.
inMin rgba [0,0,0,0] The input value to map to outMin
inMax rgba [1,1,1,1] The input value to map to outMax
outMin rgba [0,0,0,0] The output minimum value.
outMax rgba [1,1,1,1] The output maximum value.
gamma rgba [1,1,1,1] The gamma to apply to the values after the input range has been mapped to 0-1 and before it has been mapped to the output range. It's applied as pow(colour, gamma).
clamp boolean false Whether or not to clamp the values to always be within the output range. The clamp happens before the gamma.
output image [0,0,0,1] The output image.

Resize

Resizes the image to be a different resolution.

Attribute Type Default Value Description
input image [0,0,0,1] The input image.
scaleWidth float 1.0 The amount to scale the horizontal resolution by.
scaleHeight float 1.0 The amount to scale the vertical resolution by.
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image.

Shuffle

Combines two images into one image by shuffling the individual components.

Attribute Type Default Value Description
a image [0,0,0,1] The a image.
b image [0,0,0,1] The b image.
outR enum a.r Which component to shuffle into the red channel of the output image. Possible options are: "a.r", "a.g", "a.b", "a.a", "b.r", "b.g", "b.b", "b.a", "0", "1", "a.luminance", "a.maxComponent", "a.minComponent", "a.saturation", "b.luminance", "b.maxComponent", "b.minComponent", "b.saturation".
outG enum a.g Which component to shuffle into the green channel of the output image. Possible options are: "a.r", "a.g", "a.b", "a.a", "b.r", "b.g", "b.b", "b.a", "0", "1", "a.luminance", "a.maxComponent", "a.minComponent", "a.saturation", "b.luminance", "b.maxComponent", "b.minComponent", "b.saturation".
outB enum a.b Which component to shuffle into the blue channel of the output image. Possible options are: "a.r", "a.g", "a.b", "a.a", "b.r", "b.g", "b.b", "b.a", "0", "1", "a.luminance", "a.maxComponent", "a.minComponent", "a.saturation", "b.luminance", "b.maxComponent", "b.minComponent", "b.saturation".
outA enum a.a Which component to shuffle into the alpha channel of the output image. Possible options are: "a.r", "a.g", "a.b", "a.a", "b.r", "b.g", "b.b", "b.a", "0", "1", "a.luminance", "a.maxComponent", "a.minComponent", "a.saturation", "b.luminance", "b.maxComponent", "b.minComponent", "b.saturation".
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image. The resolution of the image is the larger of the two input images.

SubGraph

Embeds a node graph into this node graph. This node effectively copy and pastes the node in the sub graph into this graph, but prefixes all nodes with <nodeName>:. Connecting to nodes in the sub graph can be done by using the prefix. Setting attribute values of nodes in the sub graph is done without the prefix. If you are overriding a SubGraph node that was included and specify the graphName attribute, even if it still points to the same id, then it'll re-import the sub graph fully, getting rid of any changes made so far on the sub graph by the included node.

Attribute Type Default Value Description
graphName string "" The resource id of the node graph to use as a sub graph.

Transform

Applies some transformation to the image. It first scales the image, then rotates, and lastly translates.

Attribute Type Default Value Description
input image [0,0,0,1] The input image.
translateX float 0.0 The translation on the horizontal axis. Positive moves the image to the right.
translateY float 0.0 The translation on the vertical axis. Positive moves the image down.
rotate float 0.0 The amount to rotate the image clockwise in degrees.
scaleX float 1.0 The amount to scale the image in the horizontal axis.
scaleY float 1.0 The amount to scale the image in the vertical axis.
pivotX float 0.5 The pivot used for rotating and scaling on the horizontal axis.
pivotY float 0.5 The pivot used for rotating and scaling on the vertical axis.
coordinateSpace enum "0-1" The coordinate space used for the transformation values. Possible options are: "pixels" (The values are specified in pixel units) and "0-1" (The values are specified in a resolution-independent way where 1.0 is the width/height of the image).
interpolation enum "nearest" The kind of interpolation used to sample the images. Possible option are: "nearest", "linear", and "bicubic"
boundary enum "repeat" The boundary condition used when sampling the images. Possible options are: "empty" (a value of [0,0,0,0] outside of the image), "clip" (the sample coordinates are clamped to the border), and "repeat" (the image repeats).
output image [0,0,0,1] The output image.

Write

Writes in an image.

Attribute Type Default Value Description
input image [0,0,0,1] The image to write.
imageName string "@texture@_pbr" The resource identifier for the image to write to. @texture@ will automatically be replaced with the resource identifier of the current texture being processed. Extensions can optionally be provided to force a specific file format. Valid extensions are .png and .exr. This node cannot override the current texture being processed.
applyGamma boolean false Should the image have a 2.2 gamma applied.
isTemporary boolean false If this texture is a temporary texture that should be deleted after the PBR generator is done processing.

Node Graph Examples

The following example simply blurs the texture a little bit and then writes it out again.

{
    "priority": 0,
    "selection": [ "*" ],
    "outputs": [ "WRITE" ],
    "nodes": {
        "READ": {
            "type": "Read",
            "attributes": {
                "imageName": "@texture@"
            }
        },
        "BLUR": {
            "type": "Blur",
            "attributes": {
                "input": { "conn": "READ.output" },
                "radiusX": 2,
                "radiusY": 2,
                "radiusShape": "square"
            }
        },
        "WRITE": {
            "type": "Write",
            "attributes": {
                "input": { "conn": "BLUR" },
                "imageName": "@texture@_blurred"
            }
        }
    }
}

Clone this wiki locally