The Universal Render Pipeline Cookbook: Recipes for Shaders and Visual Effects

The Universal Render Pipeline Cookbook cover

The latest cookbook, I’ve written for Unity is now live. It is all about Universal Render Pipeline (URP) effects and is now available to download for free. The e-book provides 12 recipes for popular visual effects that can be applied to a wide range of games, art styles, and platforms. Get ready to cook up Renderer Features, GPU-instantiated meshes, decals, volumetric materials, and more. You can use it alongside my other Unity e-book, Introduction to the Universal Render Pipeline for advanced Unity creators , which offers a wealth of information about how to use URP for creators that have developed projects with the Built-In Render Pipeline.

To celebrate the launch of the new e-book my Udemy course “The Complete Guide to Unity’s Universal Render Pipeline (URP)”, is available for less than $10 until 9th July 2023.

Here’s a handy overview of the recipes you’ll find in the book.

1. Stencils

Renderer features provide you with ample opportunities to experiment with lighting and effects. This recipe focuses on Stencils, using only the bare minimum of required code. If you work alongside the sample project, open the sample scene via Scenes > Renderer Features > SmallRoom – Stencil in the Editor.

The sample project uses the magnifying glass over desk example, and the aim is to convert the lens of the magnifying glass so that it allows you to see through the desk like an x-ray image. The approach uses a combination of Layer Masks, shaders, and Renderer features.

Renderer Features are a great way to achieve dramatic custom effects or gameplay possibilities.

GitHubDownload the sample

Stencils in action: As the magnifying glass moves over the desk, it can see through the drawers to reveal what’s inside.
Stencils in action: As the magnifying glass moves over the desk, it can see through the drawers to reveal what’s inside.

2. Instancing

Exchanging data between CPU and GPU is a major bottleneck in the rendering pipeline. If you have a model that needs to be rendered many times using the same geometry and material, then Unity provides some great tools to do so, which are covered in the cookbook’s instancing chapter.

This recipe uses a field full of grass to illustrate the concept of instancing. It uses the SRP Batcher, GPU instancing, RenderMeshPrimitives, and ComputeBuffers.

A field of grass rendered using an SRP Batcher-compatible material
A field of grass rendered using an SRP Batcher-compatible material

3. Toon and outline shading

Often used together, toon and outline shaders present two distinct challenges. The toon shader takes the cooler that would be created using a URP-compatible Lit shader, and ramps the output rather than allowing continuous gradients, thereby requiring a custom lighting model.

The example in this recipe uses Shader Graph. However, Shader Graph doesn’t support custom lighting, so there’s no node available to directly access the Main and Additional Lights. Instead, you can leverage a custom node to access those.

Check out the Toon and outline shading recipe to get the full details.

One scene, three different looks: Standard shading (left), with post-processing (center), and per-material shading (right)
One scene, three different looks: Standard shading (left), with post-processing (center), and per-material shading (right)

4. Ambient Occlusion

Ambient Occlusion is a post-processing technique available from Unity 2020.2. This effect darkens creases, holes, intersections, and surfaces that are close to one another. In the real world, such areas tend to block out or occlude ambient light, thereby appearing darker.

See how you can implement real-time Screen Space Ambient Occlusion (SSAO) effect as a Renderer Feature using URP.

Screen Space Ambient Occlusion
Screen Space Ambient Occlusion

5. Decals

Decals are a great way to insert overlays onto a surface. They’re often used to add visuals such as bullet holes or tire treads to the game environment as the player interacts with the scene.

If you want to follow along this recipe, you’ll work with URP Decal Projection properties, creating the material, and even adding a decal with code.

A new Decal Projector in action
A new Decal Projector in action

6. Water

The water recipe is created in Shader Graph to make the steps more accessible. It’s built in three stages:

  • Creating the water color
  • Moving tiled normal maps to add wavelets to the surface
  • Adding moving displacement to the vertex positions to create a swell effect

While this recipe forms the basis of a simple water shader, you can enhance it using Caustic Reflections, Refraction, and Foam.

Simple water shader in motion

7. LUT for color grading

Using LUT Textures is an efficient way to create dramatic color grading, and this approach can be useful in many games. It involves using one filter, but the steps employed apply to all of them.

Using Color Lookup to create grading effects
Using Color Lookup to create grading effects

8. Lighting

Lighting with URP is similar to using the Built-in Render Pipeline. The main difference is where to find the settings.

This chapter in the cookbook covers related recipes for real-time lighting and shadows, including baked and mixed lighting using the GPU Progressive Lightmapper, Light Probes, and Reflection Probes. You’ll pick up enough instruction for a five-course meal!

A few things to keep in mind about shaders and color space: When using lighting in URP, you have a choice between a Lit Shader and Simple Lit Shader, which is largely an artistic decision. If you want a realistic render, you can use the Lit Shader, but if you want a more stylized render, you can use Simple Lit for stellar results.

The diorama scene mixing baked and real-time lighting
The diorama scene mixing baked and real-time lighting

9. Shadows

Shadow settings are set using a Renderer Data object and a URP Asset using URP. You can use these assets to define the fidelity of your shadows.

The URP Asset
The URP Asset

This recipe includes tips for: Main Light and Shadow Resolution, Shadow Cascades, baking lights, and more.

Texel size by scale setting: In the top-left image, texel size is set to 0.5; in the top-right image, 0.2; in the bottom-left image, 0.1, and in the bottom-right image, 0.05.
Texel size by scale setting: In the top-left image, texel size is set to 0.5; in the top-right image, 0.2; in the bottom-left image, 0.1, and in the bottom-right image, 0.05.

10. Light Probes

Light Probes save the light data at a particular position within an environment when you bake the lighting by clicking Generate Lighting via Window > Rendering > Lighting panel. This ensures that the illumination of a dynamic object moving through an environment reflects the lighting levels used by the baked objects. It will be dark in a dark area, and in a lighter area it will be brighter.

Follow this recipe to find out how to position Light Probes with a code-based approach in order to speed up your editing, how to use Reflection Probes in your scene, and how to blend them.

The robot inside and outside of the cave, with lighting level affected by Light Probes
The robot inside and outside of the cave, with lighting level affected by Light Probes

11. Screen Space Refraction

Screen Space Refraction uses the current opaque texture created by the render pipeline as the source texture to map pixels to the model being rendered. This method and recipe is about deforming the UV used to sample the image.

Learn how to use a normal map to create refraction effects as well as tint a refraction effect.

An example of Screen Space Refraction
An example of Screen Space Refraction

12. Volumetrics

This is a recipe for using ray marching to render a 3D texture. Unity supports 3D textures, which are an array of images placed in a grid on a single texture, rather like a Texture Atlas. The difference is that each image is the same size. Using a 3D UV value, you can source a texel from the grid of images with UV.Z defining the row and column of the individual image to use.

You can also use Houdini when creating the 3D texture. Alternatives to a 3D texture include using multilayered Perlin noise, or prebaking a tileable noise texture using Unity.

A cloud with ray marching
A cloud with ray marching

More resources

This cover image shown here is from PRINCIPLES, an adventure game from COLOPL Creators, the technology brand of COLOPL, Inc., who developed the series of Shironeko Project and Quiz RPG: The World of Mystic Wiz.
This cover image shown here is from PRINCIPLES, an adventure game from COLOPL Creators, the technology brand of COLOPL, Inc., who developed the series of Shironeko Project and Quiz RPG: The World of Mystic Wiz.

There are many advanced resources available for free from Unity. As mentioned at the beginning of the blog post, the e-book Introduction to the Universal Render Pipeline for advanced Unity creators is a valuable resource for helping experienced Unity developers and technical artists migrate their projects from the Built-in Render Pipeline to the URP.

All of the advanced e-books and articles are available from the Unity best practices hub. E-books can also be found on the advanced best practices documentation page.

The Complete Guide to Unity’s Universal Render Pipeline (URP)

And don’t forget my Udemy URP course is available for less than 10 bucks until 9th July

Converting older projects to the new Wonderland Engine framework

When the Wonderland editor moved from 0.9.5 to 1.0.0 there was a significant change to the code framework. In this article we’ll look at how you can easily migrate an old project to work in the latest editor. If you plan to code-along then download my Wonderland course resources.

With the switch by default Wonderland now uses npm, NodeJS Package Manager, and esbuild bundler. The WL.registerComponent method no longer exists. For each of your custom components you need to do some editing. If you want to code-along then open the code-along Migrate project. As you can see, Wonderland creates an npm project.

node modules

After running you’ll find a node_modules folder, two new json files; package.json and package-lock.json and index.js. The node_modules folder by default contains the wonderlandengine, glMatrix and howler and a few other modules. Index.js is auto generated by the editor and this file handles registering your components. 

Console errors

Looking at the project console window we can see there are errors when packaging. WL not defined. Notice it refers to the blockHandler component let’s fix this file.

WL.registerComponent('blockHandler', {
vrCamera: {type: WL.Type.Object, default: null},
speed: {type: WL.Type.Float, default: 5.0 }
}, {

Where we have WL.registerComponent this becomes export class, then the class name, BlockHandler here and then extends Component

export class BlockHandler extends Component {

If you’re familiar with JavaScript classes then you will instantly say, hang on where does Component come from. If so go to the top of the class. We need to add an import. It comes from the wonderlandengine api. From the module we saw in the node_modules folder. 

import {Component} from '@wonderlandengine/api';

Each component needs a TypeName, Wonderland uses the type name in the editor not the class name. 

static TypeName = "blockHandler";

Parameters are defined using a static Properties object. The Wonderland api has a Property class and we use this to specify property types and default values. If we’re using something from the api we need to import it so we add Property as a second import from wonderlandengine/api. 

import {Component, Property} from '@wonderlandengine/api';

We specify vrCamera as a Property.object. This means the editor will include a scene objects dropdown for this component property. Speed is a Property.float, for floats we can pass a default value to the constructor. 

static Properties = {
   vrCamera: Property.object(),
   speed: Property.float( 5.0 )
}

 Now we are dealing with classes we need to remove ‘: function’.

init() {

And we remove all the commas linking the functions in what was previously an object. 

This component uses the glMatrix library and now we are using the new framework we need to add each class we use from the library as an import. For this component we need vec3 and quat. They come from gl-matrix which you can find in the node_modules folder.

import { vec3, quat } from "gl-matrix";

The easiest way to remove glMatrix. Is to use find and replace all. 

this.rotation = quat.create();
quat.fromEuler(this.rotation, 1, 1, 0);

This component also uses HowlerAudioSource. When using this we need to add it as an import.

import {HowlerAudioSource} from '@wonderlandengine/components';

This is a Wonderland component. Because it is not directly attached to a scene object. We need to make sure the component is registered. Any dynamically created component needs to do this. We add a static onRegister method, this method will receive the engine, which was previously the WL global, as a parameter. We use the registerComponent method of the engine and pass the class name. Without this linking would fail and you would get an error in the browser.

static onRegister(engine){
   engine.registerComponent( HowlerAudioSource );
}

We add the HowlerAudioSource in the start method. What was previously a string now takes the class name. 

this.sfxSwish = this.object.addComponent(HowlerAudioAource, {src: 'sfx/swish.mp3', spatial: false});

At this point the project should package correctly. But you will then notice that Wonderland has added several useful warnings about legacy methods. You’ll see several methods have a line through them and if you hover over them a panel will appear explaining what method you should switch to. Position, for example, no longer uses Translation, now it is Position instead.

this.vrCamera.getPositionWorld( this.tmpVec1 );

Instead of translate you choose between translateLocal and translateWorld.

this.object.translateWorld( this.tmpVec );

And getForward is now getForwardWorld.

this.vrCamera.getForwardWorld( this.tmpVec1 );

Some other changes you’ll come across. 

WL.onXRSessionStart is now an instance of the Emitter class. We replace WL with this.engine. Engine is always available to a class that extends Component. And replace push with add.  

this.engine.onXRSessionStart.add(this.setupVREvents.bind(this));

And finally getters and setters have been replaced.

  • this.object.translationLocal use this.object.getPositionLocal(out) and this.object.setPositionLocal(v)
  • this.object.translationWorld use this.object.getPositionWorldl(out) and this.object.setPositionWorld(v)
  • this.object.rotationLocal use this.object.getRotationLocal(out) and this.object.setRotationLocal(q)
  • this.object.rotationWorld use this.object.getRotationWorld(out) and this.object.setRotationWorld(q)
  • this.object.scalingLocal use this.object.getScalingLocal(out) and this.object.setScalingLocal(v)
  • this.object.scalingWorld use this.object.getScalingWorld(out) and this.object.setScalingWorld(v)
  • this.object.transformLocal use this.object.getTransformLocal(out) and this.object.setTransformLocal(q2)
  • this.object.transformWorld  use this.object.getTransformWorld(out) and this.object.setTransformWorld(q2)

Follow the steps in this article and you’ll soon have your projects running smoothly using the latest Wonderland editor.
You’ll find more information in the ‘Migrate Your JavaScript to 1.0.0’ article.

Unity URP Course FREE for 4 days

The Complete Guide to Unity’s Universal Render Pipeline (URP). Is FREE for the next 4 days.

https://www.udemy.com/course/unity-urp/?couponCode=JUNE23_FREE

If you use Unity then you really should get up to speed with this pipeline which will replace the Built-in Render Pipeline. This course by the author of Unity’s e-book will show you how.

Grab it here https://www.udemy.com/course/unity-urp/?couponCode=JUNE23_FREE

Pre-summer SALE!



The days are long, but that doesn’t mean you should neglect your studies. To encourage you I’m having a pre-summer SALE. Grab yourself a bargain and kick back and learn a new skill!!

The Complete Guide to Unity’s Universal Render PipelineWith URP to become Unity’s default pipeline learn how to use it in this course by the author of Unity’s URP e-books
https://www.udemy.com/course/unity-urp/?couponCode=SUMMERSALE23

Create WebXR, VR and AR app, using the Wonderland EngineWonderland is a high performance engine for WebXR apps. The editor makes creating VR experiences super-easy. In this course I show you how.
https://www.udemy.com/course/webxr-wle/?couponCode=SUMMERSALE23

Model viewer: Web 3D made easyModel-viewer is a web component created by Google. It makes displaying user interact-able 3D models on a web page a walk in the park. In this course I show you how.
https://www.udemy.com/course/model-viewer/?couponCode=SUMMERSALE23

The Beginners Guide to 3D Web Game Development with ThreeJSLearn to write JavaScript code while having fun making 3D web games using the most popular Open Source WebGL library ThreeJS
https://www.udemy.com/course/beginners-3d-web-game-development-with-threejs/?couponCode=SUMMERSALE23

Learn to write Unity Compute ShadersLearn to harness the power of the GPU for processing intensive jobs.
https://www.udemy.com/course/compute-shaders/?couponCode=SUMMERSALE23

Learn to Create WebXR, VR and AR, experiences with ThreeJSLearn how to create VR and AR experiences that work directly from the browser, using the latest API from Google and Amazon and our favourite Open Source WebGL library, ThreeJS
https://www.udemy.com/course/learn-webxr/?couponCode=SUMMERSALE23

Learn Unity Shaders from ScratchLearn the black-art of Unity shaders in this comprehensive course on HLSL. Shaders for both the Built-in Render Pipeline and URP are included.
https://www.udemy.com/course/learn-unity-shaders-from-scratch/?couponCode=SUMMERSALE23

Learn GLSL Shaders from ScratchLearn how to harness the power of the GPU in your web pages by learning to code GLSL shaders.
https://www.udemy.com/course/learn-glsl-shaders-from-scratch/?couponCode=SUMMERSALE23

Create a 3D Multi-Player Game using ThreeJS and SocketIOLearn how to use nodeJS, socketIO and ThreeJS to create a 3d multi-player game
https://www.udemy.com/course/create-a-3d-multi-player-game-using-threejs-and-socketio/?couponCode=SUMMERSALE23

Create HTML5 Games using Adobe AnimateAdobe Animate used to be Flash. Learn how you can use your Flash skills to create HTML5 games that use no plugins.
https://www.udemy.com/course/create-html5-games-using-adobe-animate/?couponCode=SUMMERSALE23

Create a 3D Car Racing Game with ThreeJS and CannonJS
Learn to combine the physics engine CannonJS and ThreeJS to create a fun car racing game
https://www.udemy.com/course/create-a-3d-car-racing-game-with-threejs-and-cannonjs/?couponCode=SUMMERSALE23

Create a 3D RPG Game with ThreeJSLearn how to harness the ThreeJS library to create a 3D RPG game
https://www.udemy.com/course/create-a-3d-rpg-game-with-threejs/?couponCode=SUMMERSALE23

HTML5 Game Development: Beginner to ProLearn how to create 2D games that work in the browser. From card games to puzzle games to action games.
https://www.udemy.com/course/html5-game-development-beginner-to-pro/?couponCode=SUMMERSALE23

JavaScript in 12 Easy LessonsNew to JavaScript or coding then this FREE course is for you
https://www.udemy.com/course/javascript-in-12-easy-lessons/?referralCode=086EC53154E29AD37EA1

The ThreeJS PrimerNew to ThreeJS then this FREE course is for you
https://www.udemy.com/course/the-threejs-primer/?referralCode=ABB270C1AE32EF9E7174

Adding Variants to a GLB model and displaying them using ThreeJS

(This article is taken from my Udemy course ‘The ThreeJS Cookbook’ coming Summer 2023. Get the course assets from GitHub. I recommend using VSCode and the Live Server extension when working along with the recipe )

If you’re creating a ThreeJS app for a store. You may be selling an item that comes in a variety of finishes. The GLB format has an extension that allows you to embed multiple materials for an object, then with code you can allow the user to switch materials to view the item with different finishes.

Before we get into coding this recipe take a look at complete > Materials > variants.html . Using the dropdown selector you can view the chair in one of four different finishes. The code reads the options directly from the model file. You can use the same technique in your own GUI.

Before we look at the code let’s look at how we create a glb file that includes material variants. We’re going to use Blender, I’m using Blender 3.5 but anything from version 3.3 will be fine. You can download Blender for free at blender.org. Blender is a complex application but there are loads of tutorials available both free and paid.

In this recipe I assume you have some knowledge of Blender and I focus exclusively on variants. To keep things simple we’ll just create materials that change the colour of the default cube. 

First open Blender and choose the General new project. You’ll get a 2m grey cube centre screen. Right-click on the light and camera and choose Delete. Lights and cameras will be added using code when we create the ThreeJS app to display our model.

Using the buttons at the top right set the display to Material Preview mode.

Select Edit > Preferences…. Select Add-ons and type ‘gltf’ in the Search field. Make sure that for the Import-Export: glTF 2.0 format add-on that the Material Variants checkbox is checked. You can close the preferences panel now.

Select the Cube. Select the Materials tool, the chequered sphere. Click the white on black Material word and rename it Grey. 

Expand the Sidebar and select glTF Variants. In the new panel press the Add Material Variant button.

Rename the variant Grey.

In the materials panel slide down to find glTF Material Variants. Press the Add a new Variant Slot button.

The new variant should take the name you’ve renamed Grey.

Click the + button to add a new material. Rename it red and set the Base Color. 

Now select Grey and press the – button. You can now use the glTF Variants Sidebar tool to add a new variant. Rename it Red and then use the Material glTF Material Variants panel to Add a new Variant Slot.

Repeat from adding a new material for each variant. 

To test whether the variants are working, use the glTF Variants Sidebar panel. Select the variant you’re interested in and press the Display Variant button. You should see the cube change to your chosen material. Now choose File > Export > glTF 2.0 (.glb/.gltf), select a file location and name.

Time to focus on code. The assets include the chair we’ve seen that includes variants with different textures and roughness. We’ll use that in the coding. But feel free to use your model if you prefer.

Before we start coding take a look at a glTF formatted file assets > variant-cube.gltf. This is the text formatted version. Usually you save a file in binary format, a glB file. But it is easy to read a glTF file in any text editor. First notice the extensions object, and see this includes KHR_materials_variants. This object contains a variants array. Each entry in the array is an object with a single property, name. 

{
   "asset":{
      "generator":"Khronos glTF Blender I/O v3.5.30",
      "version":"2.0"
   },
   "extensionsUsed":[
      "KHR_materials_variants"
   ],
   "extensionsRequired":[
      "KHR_materials_variants"
   ],
   "extensions":{
      "KHR_materials_variants":{
         "variants":[
            {
               "name":"Red"
            },
            {
               "name":"Yellow"
            },
            {
               "name":"Default"
            }
         ]
      }
   },

The glTF structure includes, a scene and scenes array. Scene is simply and index into the scenes array. A scene includes a nodes array, which in this case has a single index. 

   "scene":0,
   "scenes":[
      {
         "name":"Scene",
         "nodes":[
            0
         ]
      }
   ],

The nodes array includes a series of objects. Here we have a properties mesh, an index into the meshes array and a name.

   "nodes":[
      {
         "mesh":0,
         "name":"Cube"
      }
   ],

Now the meat of our variants data. The materials array includes a series of objects. Here simply setting the pbrMetallicRoughness.baseColorFactor, metallicFactor and roughness

   "materials":[
      {
         "doubleSided":true,
         "name":"Red",
         "pbrMetallicRoughness":{
            "baseColorFactor":[
               0.8000000715255737,
               0.011036576703190804,
               0.00618034927174449,
               1
            ],
            "metallicFactor":0,
            "roughnessFactor":0.5
         }
      },
      {
         "doubleSided":true,
         "name":"Yellow",
         "pbrMetallicRoughness":{
            "baseColorFactor":[
               0.8000000715255737,
               0.6312862634658813,
               0,
               1
            ],
            "metallicFactor":0,
            "roughnessFactor":0.5
         }
      },
      {
         "doubleSided":true,
         "name":"Material",
         "pbrMetallicRoughness":{
            "baseColorFactor":[
               0.800000011920929,
               0.800000011920929,
               0.800000011920929,
               1
            ],
            "metallicFactor":0,
            "roughnessFactor":0.5
         }
      }
   ],

The meshes array contains each mesh in every scene. But in this simple example there is only the single Cube. Notice that the Cube has an extensions object, which contains a KHR_materials_variants object. This in turn contains a mappings array. Each entry in the mappings array contains a material index and a variants array containing a single index in this instance. 

   "meshes":[
      {
         "name":"Cube",
         "primitives":[
            {
               "attributes":{
                  "POSITION":0,
                  "TEXCOORD_0":1,
                  "NORMAL":2
               },
               "extensions":{
                  "KHR_materials_variants":{
                     "mappings":[
                        {
                           "material":0,
                           "variants":[
                              0
                           ]
                        },
                        {
                           "material":1,
                           "variants":[
                              1
                           ]
                        },
                        {
                           "material":2,
                           "variants":[
                              2
                           ]
                        }
                     ]
                  }
               },
               "indices":3,
               "material":2
            }
         ]
      }
   ],

The code we need to generate should read root level extensions object, look for KHR_materials_variants and read the names of the variants. Using this to populate a GUI. Then we need a function that given a variant name, finds the index of this variant. Then iterates over each mesh in the scene, checks if the mesh has a KHR_materials_variants extension. If it has then it checks the mappings object to see if its variants array includes the variant index found for the name given. If it does then we get the material using the index and apply it to the mesh. If that sounds complex it might be clearer once we’ve added the code. 

We’ll source the variant names from the root level extension first. First we create a new GUI using the lil-gui library. Then we grab a reference to the gltf.parser object. More about that later. After parsing, extensions are stored in the userData object for each Object3D instance in the hierarchy. We check if gltfExtensions is a gltf.userData property. If it is we attempt to assign the extension KHR_materials_variants. At this stage variantsExtension is either null if missing or a reference to the data. 

gui = new GUI();

const parser = gltf.parser;

let variantsExtension;

if ( 'gltfExtensions' in gltf.userData ){
  variantsExtension = gltf.userData.gltfExtensions[ 'KHR_materials_variants' ];
}

If variantsExtension is not null then we use the JavaScript array method map to return a new array containing just an array of names. Then we add a select control. The state object is defined at the start of the script with a single property variant.

if (variantsExtension != null){
   const variants = variantsExtension.variants.map( ( variant ) => variant.name );
   const variantsCtrl = gui.add( state, 'variant', variants ).name( 'Variant' );
}

If you try the app now you should get a list of variant names in the GUI. 

Now we need a function to call when a name is selected in the GUI. It needs a reference to the scene, the parser we stored, the variants extension and the variant name. We need to convert the variant name into an index, so we use the findIndex method of a JavaScript array. 

function selectVariant( scene, parser, extension, variantName ) {

   const variantIndex = extension.variants.findIndex( ( v ) => v.name.includes( variantName ) );

Now we can traverse the scene. We do this asynchronously since a scene may contain a lot of Object3D instances and we don’t want to hold up processing. The callback retrieves each object in the gltf scene. We’re only interested in meshes, so we check if the isMesh flag is set. And we need userData to contain a gltfExtensions object. If either of these checks fail we return from the callback for the current object and move on to the next. Then we assign meshVariantDef as the gltfExtensions object KHR_materials_variants. That’s the extension stored in the object definition. The one we saw that includes mappings.

scene.traverse( async ( object ) => {

if ( ! object.isMesh || ! object.userData.gltfExtensions ) return;

const meshVariantDef = object.userData.gltfExtensions[ 'KHR_materials_variants' ];

If meshVariantDef is null then we can also return. Now we store the current material as userData.originalMaterial, assuming this hasn’t already been set.

if ( ! meshVariantDef ) return;

if ( ! object.userData.originalMaterial ) {

   object.userData.originalMaterial = object.material;

}

To find the mapping we’re interested in we use the find method of a JavaScript array, searching through the variants array for one that includes the variantIndex.

const mapping = meshVariantDef.mappings
                .find( ( mapping ) => mapping.variants.includes( variantIndex ) );

If we find a mapping, we use the parser method getDependency, use material as the property and the mapping.material index. This returns the material in the materials array with the given index. The parser object also contains a method, assignFinalMaterial that ensures the material is correctly assigned and compiled. If mapping is not found then we restore the original material from the one we stored in userData. We also call render because this app doesn’t use a render loop. Instead we call render on each change.

if ( mapping ) {

   object.material = await parser.getDependency( 'material', mapping.material );
   parser.assignFinalMaterial( object );

} else {

   object.material = object.userData.originalMaterial;

}

render();

It just remains to update our GUI code to call this function on a change event. 

variantsCtrl.onChange( ( value ) => selectVariant( scene, parser, variantsExtension, value ) );

Now the GUI is populated and on a change event the variant name is passed to the selectVariant method. 

Variants are a great solution for allowing users to view an object using a variety of finishes.

May the 4th Be With You!

May the 4th be with you. Starry sky poster, star force and hand drawn stars. Wars movie slogan banner, futuristic stars glow poster or space star fantasy vector illustration

It’s Star Wars Day. My courses are at Udemy’s Best Price for the next few days. Grab yourself a bargain and May the Fourth be with you!!

Or use the menu to check-out my tutorial links.

The Complete Guide to Unity’s Universal Render Pipeline

With URP to become Unity’s default pipeline with Unity 6. Learn how to use it in this course by the author of Unity’s URP e-books

https://www.udemy.com/course/unity-urp/?couponCode=MAY24_FOURTH

Create WebXR, VR and AR app, using the Wonderland Engine

Wonderland is a high performance engine for WebXR apps. The editor makes creating VR experiences super-easy. In this course I show you how.

https://www.udemy.com/course/webxr-wle/?couponCode=MAY24_FOURTH

Model viewer: Web 3D made easy

Model-viewer is a web component created by Google. It makes displaying user interact-able 3D models on a web page a walk in the park. In this course I show you how.

https://www.udemy.com/course/model-viewer/?couponCode=MAY24_FOURTH

The Beginners Guide to 3D Web Game Development with ThreeJS

Learn to write JavaScript code while having fun making 3D web games using the most popular Open Source WebGL library ThreeJS

https://www.udemy.com/course/beginners-3d-web-game-development-with-threejs/?couponCode=MAY24_FOURTH

Learn to write Unity Compute Shaders

Learn to harness the power of the GPU for processing intensive jobs.

https://www.udemy.com/course/compute-shaders/?couponCode=MAY24_FOURTH

Learn to Create WebXR, VR and AR, experiences with ThreeJS

Learn how to create VR and AR experiences that work directly from the browser, using the latest API from Google and Amazon and our favourite Open Source WebGL library, ThreeJS

https://www.udemy.com/course/learn-webxr/?couponCode=MAY24_FOURTH

Learn Unity Shaders from Scratch

Learn the black-art of Unity shaders in this comprehensive course on HLSL. Shaders for both the Built-in Render Pipeline and URP are included.

https://www.udemy.com/course/learn-unity-shaders-from-scratch/?couponCode=MAY24_FOURTH

Learn GLSL Shaders from Scratch

Learn how to harness the power of the GPU in your web pages by learning to code GLSL shaders.

https://www.udemy.com/course/learn-glsl-shaders-from-scratch/?couponCode=MAY24_FOURTH

Create a 3D Multi-Player Game using ThreeJS and SocketIO

Learn how to use nodeJS, socketIO and ThreeJS to create a 3d multi-player game

https://www.udemy.com/course/create-a-3d-multi-player-game-using-threejs-and-socketio/?couponCode=MAY24_FOURTH

Create HTML5 Games using Adobe Animate

Adobe Animate used to be Flash. Learn how you can use your Flash skills to create HTML5 games that use no plugins.

https://www.udemy.com/course/create-html5-games-using-adobe-animate/?couponCode=MAY24_FOURTH

Create a 3D Car Racing Game with ThreeJS and CannonJS

Learn to combine the physics engine CannonJS and ThreeJS to create a fun car racing game

https://www.udemy.com/course/create-a-3d-car-racing-game-with-threejs-and-cannonjs/?couponCode=MAY24_FOURTH

Create a 3D RPG Game with ThreeJS

Learn how to harness the ThreeJS library to create a 3D RPG game

https://www.udemy.com/course/create-a-3d-rpg-game-with-threejs/?couponCode=MAY24_FOURTH

HTML5 Game Development: Beginner to Pro

Learn how to create 2D games that work in the browser. From card games to puzzle games to action games.

https://www.udemy.com/course/html5-game-development-beginner-to-pro/?couponCode=MAY24_FOURTH

JavaScript in 12 Easy Lessons

New to JavaScript or coding then this FREE course is for you

https://www.udemy.com/course/javascript-in-12-easy-lessons/?referralCode=086EC53154E29AD37EA1

The ThreeJS Primer

New to ThreeJS then this FREE course is for you

https://www.udemy.com/course/the-threejs-primer/?referralCode=ABB270C1AE32EF9E7174

Happy Lunar New Year!

Just an update to my 36,000 students. I’m currently developing a course teaching how to get the best from Unity’s Universal Render Pipeline (URP). It’s based on the e-book I wrote for Unity.

Introduction to the Universal Render Pipeline for Advanced Unity Creators.

The course, aimed more at intermediate level, is going well and should be live in March.

If you plan to do the course then take a look at these two tutorial videos on YouTube as a taster to what to expect.

Converting custom shaders to URP

Make your shaders Scriptable Render Pipeline Batcher compatible. This step by step video tutorial will show you how to convert a custom unlit Built-in shader to the Universal Render Pipeline (URP).

Three ways to use URP Renderer Features

A Renderer Feature is a C# script that can be used at any stage in the Render Pipeline to affect the final render. Renderer Features provide a great way of adding custom rendering effects to your scene, often with no code required, making them artist-friendly.

Nik Lever 22 Jan 2023

Nik Lever’s New Year Sale

Hope you’re all enjoying the holidays. To celebrate the New Year. All my courses are available at the lowest price Udemy will allow this week with the coupon code NEWYEAR23 . Read through to find two FREE courses

Create WebXR, VR and AR app, using the Wonderland Engine

Wonderland is a high performance engine for WebXR apps. The editor makes creating VR experiences super-easy. In this course I show you how.

https://www.udemy.com/course/webxr-wle/?couponCode=NEWYEAR23

Model viewer: Web 3D made easy

Model-viewer is a web component created by Google. It makes displaying user interact-able 3D models on a web page a walk in the park. In this course I show you how.

https://www.udemy.com/course/model-viewer/?couponCode=NEWYEAR23

The Beginners Guide to 3D Web Game Development with ThreeJS

Learn to write JavaScript code while having fun making 3D web games using the most popular Open Source WebGL library ThreeJS

https://www.udemy.com/course/beginners-3d-web-game-development-with-threejs/?couponCode=NEWYEAR23

Learn to write Unity Compute Shaders

Learn to harness the power of the GPU for processing intensive jobs.

https://www.udemy.com/course/compute-shaders/?couponCode=NEWYEAR23

Learn to Create WebXR, VR and AR, experiences with ThreeJS

Learn how to create VR and AR experiences that work directly from the browser, using the latest API from Google and Amazon and our favourite Open Source WebGL library, ThreeJS

https://www.udemy.com/course/learn-webxr/?couponCode=NEWYEAR23

Learn Unity Shaders from Scratch

Learn the black-art of Unity shaders in this comprehensive course on HLSL. Shaders for both the Built-in Render Pipeline and URP are included.

https://www.udemy.com/course/learn-unity-shaders-from-scratch/?couponCode=NEWYEAR23

Learn GLSL Shaders from Scratch

Learn how to harness the power of the GPU in your web pages by learning to code GLSL shaders.

https://www.udemy.com/course/learn-glsl-shaders-from-scratch/?couponCode=NEWYEAR23

Create a 3D Multi-Player Game using ThreeJS and SocketIO

Learn how to use nodeJS, socketIO and ThreeJS to create a 3d multi-player game

https://www.udemy.com/course/create-a-3d-multi-player-game-using-threejs-and-socketio/?couponCode=NEWYEAR23

Create HTML5 Games using Adobe Animate

Adobe Animate used to be Flash. Learn how you can use your Flash skills to create HTML5 games that use no plugins.

https://www.udemy.com/course/create-html5-games-using-adobe-animate/?couponCode=NEWYEAR23

Create a 3D Car Racing Game with ThreeJS and CannonJS

Learn to combine the physics engine CannonJS and ThreeJS to create a fun car racing game

https://www.udemy.com/course/create-a-3d-car-racing-game-with-threejs-and-cannonjs/?couponCode=NEWYEAR23

Create a 3D RPG Game with ThreeJS

Learn how to harness the ThreeJS library to create a 3D RPG game

https://www.udemy.com/course/create-a-3d-rpg-game-with-threejs/?couponCode=NEWYEAR23

HTML5 Game Development: Beginner to Pro

Learn how to create 2D games that work in the browser. From card games to puzzle games to action games.

https://www.udemy.com/course/html5-game-development-beginner-to-pro/?couponCode=NEWYEAR23

JavaScript in 12 Easy Lessons

New to JavaScript or coding then this FREE course is for you

https://www.udemy.com/course/javascript-in-12-easy-lessons/?referralCode=086EC53154E29AD37EA1

The ThreeJS Primer

New to ThreeJS then this FREE course is for you

https://www.udemy.com/course/the-threejs-primer/?referralCode=ABB270C1AE32EF9E7174

My Unity URP e-book is published today!

URP for Advanced Unity Creators

Download this valuable guide to fully leverage the benefits of URP for professional game development. I hope that you find the e-book helpful.

To celebrate the publication of my Unity URP e-book my Udemy Unity courses are available for just $10.99 for the next few days.

Unity Compute Shaders
Learn to Write Unity Compute Shaders

Learn to Write Unity Compute Shaders

Unity Shaders
Learn to Write Unity Shaders from Scratch

Learn to Write Unity Shaders from Scratch

Includes URP shaders and ShaderGraph