Painted materials

Creating custom shaders to support painted materials

Objects like landscapes normally have variation in the ground like some parts are grassy, some might be sand and maybe a dirt road. This requires two things

  1. Painted weights on an object such as a landscape

  2. A material using a custom shader that can use these painted weights

What are painted weights?

When you are using the paint tool in the editor, you are not necessarily painting a color. Instead, as your brush goes over a vertex (a corner of a triangle) on the geometry of the selected object, it will store a "weight" on that vertex as a fractional value between 0 and 1 for the specific painting layer used when painting. This is similar to a feature that exists in 3D editors called vertex painting. These weights are then available to shaders to create materials that can use the weights to determine the color of each pixel on the surface.

Using painted weights in a shader

In order to use the weights, you need to create a custom shader. The easiest way to accomplish this in a custom shader is to use the function mixColorsByLayer as shown below.


import { LayerMixMode, mixColorsByLayer, rgb, standardMaterial } from "@hology/core/shader-nodes";
import { NodeShader, NodeShaderOutput } from "@hology/core/shader/shader";

export class ExampleShader extends NodeShader {

  output(): NodeShaderOutput {

    const color = mixColorsByLayer({
      layerColors: [
        rgb("#CCCCCC"), // Default color
        rgb("#FF0000"), // Painted layer 1
        rgb("#00FF00"), // Painted layer 2
        rgb("#0000FF"), // Painted layer 3 
      ], 
      enableNoise: true,
      noiseScale: .5,
      noiseAmount: .1,
      mode: LayerMixMode.soft,
      decay: .5,
    })

    return {
      color: standardMaterial({color})
    }
  }
}

export default ExampleShader

For each layer in your material, including the bottom (default) layer, you need to assign a color. Colors could be defined as in the example above, or they could be the result of sampling textures and anything else you otherwise can do in shaders.

Currently, a maximum of 4 colors are supported, including the default color. This will change in a future release to support many more.

In the example above, the color created from mixing the painted layers with their assigned colors is passed to the standardMaterial function which adds support for lights and shadows on top of your material.

Transitions

You also need to decide how to transition between the layers. As the painted weights are stored in the corners of triangles, you don't have a especially high precision for exactly where a color should appear. If your object's geometry has a high triangle density, meaning it has a lot of tiny triangles as opposed to fewer large ones, it will require more processing power to render the object but you could get more control over where the paint goes. Also, uniformly sized and positioned triangles in a grid as on a landscape object will easily make it look unnatural. To handle this, there are several parameters you can use to control how to transition between the layers. This requires knowing what kind of art style you are going for and some experimentation to find values that makes it look the way you want.

  • enableNoise (boolean) - By adding noise to the transition between layers, you can add some more natural looking edges as opposed to the straight and jagged edges you would otherwise get when the geometry is just triangles.

  • noiseScale (number) - Control the detail level of the noise.

  • noiseAmount (number) - A value between 0 and 1 for how much the noise should affect the transition.

  • mode (enum) - The transition can either be soft or hard.

    • A soft transition will create a gradient between the colors of the layers. For something to look realisitc like in nature, you likely want this.

    • A hard transition on the other hand could be favourable for an art style that is more stylized.

  • decay (number) - This is only relevant when the mode is soft. It controls the length of the gradient between the two layers.

Last updated