Paint finishes¶
A paint finish draws a car's paint with a shader instead of the game's plain paint. It uses the same sun, street lights, fog and reflections as the rest of the car, and every player sees it.
vehicle.paintFinish = { type: "metallic" };
vehicle.paintFinish = { type: "metallic", flakes: 1, flakeSize: 2, gloss: 1 }; // coarse, glittery, wet look
vehicle.paintFinish = null; // the game's paint again
Only the paint changes: the parts the game paints in the car's colours. Glass, lights, trim and tyres stay as they are. The mod shops sell finishes too, in their Paint Finish area.
Built in¶
Metallic¶
Flakes in the paint glint as they catch the light, mostly around the highlights. They stay on the car as it moves, and blur into a soft sheen further away. The colour deepens towards the car's edges, as real metallic paint does, under a glossy clear coat.
| Option | Type | Default | Description |
|---|---|---|---|
flakes |
number |
0.7 |
How much the flakes show, 0 to 1. |
flakeSize |
number |
1 |
Their size: 2 is coarser (glittery), 0.5 finer. |
flakeColor |
number |
0xFFFFFF |
The colour they glint (0xRRGGBB), mixed with the paint's: dark paint shows more of it (silver flakes on black). |
gloss |
number |
0.85 |
The clear coat's shine, 0 (dull) to 1 (wet look). |
on |
"both" \| "primary" \| "secondary" \| "all" |
"both" |
Which of the car's colours. |
Pearl¶
Colour shift's pearl without the shift: the paint keeps its own colour, with a soft pearly glow (faint pastel tints running through it with the angle) and fine pearl flakes under a clear coat.
| Option | Type | Default | Description |
|---|---|---|---|
pearlColor |
number |
0xFFFFFF |
What the glow and flakes are tinted towards (0xRRGGBB): a gold or blue pearl on white paint, say. |
flakes |
number |
0.35 |
How much the pearl flakes show. |
flakeSize |
number |
1 |
Their size, as metallic's. |
gloss |
number |
0.9 |
The clear coat's shine. |
on |
as metallic | "both" |
Satin and matte¶
A rough finish. The light spreads into a soft, wide glow instead of a sharp highlight, with a faint scatter at the edges and hardly any reflection. Matte is the same with almost no sheen.
| Option | Type | Default | Description |
|---|---|---|---|
sheen |
number |
0.5 (satin), 0.1 (matte) |
0 is flat and chalky; 1 is a soft glow. |
on |
as metallic | "both" |
Colour shift¶
Chameleon paint. Each part's hue turns as the surface turns away from you: one way where it turns to your right (or up), the other way where it turns to your left (or down), with its own colour where it faces you. Colour 1 and colour 2 parts each shift from their own colour, and greys, black and white stay as they are. Fine pearl flakes in the shifting colour sit under a clear coat.
| Option | Type | Default | Description |
|---|---|---|---|
hueShift |
number |
30 |
How far the hue turns at the edges, in degrees, each way: red runs to orange on one side and to pink on the other. Larger is wilder (120 runs red through green and purple). Negative swaps the sides. |
shift |
number |
1 |
How much of that it reaches, 0 (none) to 1. |
flakes |
number |
0.35 |
How much the pearl flakes show. |
flakeSize |
number |
1 |
Their size, as metallic's. |
gloss |
number |
0.9 |
The clear coat's shine. |
on |
as metallic | "both" |
---|---|---|---|
| shiftColor | number | 0x6A2BD9 (purple) | The colour it turns to (0xRRGGBB). |
| edgeColor | number | shiftColor | The colour at the edges, for a three-colour shift. |
| shift | number | 1 | How far it turns, 0 (not at all) to 1. |
| flakes | number | 0.35 | How much the pearl flakes show. |
| flakeSize | number | 1 | Their size, as metallic's. |
| gloss | number | 0.9 | The clear coat's shine. |
| on | as metallic | "both" | |
vehicle.paintFinish = { type: "colorShift", shiftColor: 0x1e7f3a, edgeColor: 0x2050ff }; // to green, then blue
Liveries¶
A livery is an image over a car's paint, such as a sticker bomb wrap or a paint job of your own. It shows under any finish, so a car can have a metallic sticker bomb.
vehicle.livery = { texture: "liveries/stickerbomb.jpg" }; // any car
vehicle.livery = { texture: "game:savanna1/savanna92body256a", mapping: "uv", blend: "multiply" }; // the game's Savanna flames
vehicle.livery = null;
| Option | Type | Default | Description |
|---|---|---|---|
texture |
string |
An image client file (PNG or JPG, up to 2048 square), or one of the game's textures, "game:<txd>/<texture>". |
|
mapping |
"projected" \| "uv" |
"projected" |
"projected" lays it onto any car from the sides, top and front, tiled scale metres across. "uv" uses the paint's own layout, for an image drawn for one model (as the game's own paint jobs are); only the parts the game's paint jobs cover get it. |
scale |
number |
4 |
Projected: how many metres one copy of the image covers. |
opacity |
number |
1 |
0 to 1. |
blend |
"over" \| "multiply" |
"over" |
"over" puts the image on top as it is. "multiply" tints it by the car's colour, as the game's paint jobs are (mid grey keeps the paint as it is). |
on |
as metallic | "both" |
Which of the car's colours. |
The game's own paint jobs are in its TXDs, named after the car with 1, 2 or 3
on the end: game:savanna1/savanna92body256a, game:savanna2/savanna92body256b,
and so on. Freeroam has a sticker bomb texture to start from
(examples/freeroam/client-files/liveries/stickerbomb.jpg).
Your own¶
shader is an HLSL client file with a shade function, which gives the
paint's colour at each point. params holds up to 8 values by name, each a
number or 2 to 4 numbers, which the shader reads by those names.
// client-files/shaders/paint_chrome.hlsl
float3 shade(Surface s) {
float3 n = s.normal;
float3 mirror = reflection(s, n) * 1.6 * tint;
return mirror * s.texel.rgb + specularLight(s, n, 250) * 1.5;
}
Surface
| Field | Type | Description |
|---|---|---|
color |
float3 |
The paint colour. |
alpha |
float |
Its alpha. |
normal |
float3 |
The surface's direction, in world space. |
view |
float3 |
The direction to the camera. |
position |
float3 |
Where the point is, in world space. |
local |
float3 |
Where it is on the car, in metres (model space). Use this for patterns that stay on the car, such as flakes or stripes. |
localNormal |
float3 |
The surface's direction on the car. |
uv |
float2 |
Its texture coordinates. |
texel |
float4 |
Its texture (dirt and details), or white if it has none. Multiply by it to keep the car's dirt. |
distance |
float |
Its distance from the camera. |
ambient |
float3 |
The ambient light. |
Helpers
| Function | Description |
|---|---|
diffuseLight(s, n) |
The ambient light plus every light shining on a surface facing n. Multiply your colour by it. |
specularLight(s, n, power) |
The lights' highlights off a surface facing n; a higher power gives a tighter highlight. |
reflection(s, n) |
The surroundings reflected off a surface facing n (the game's car reflection map), dimmed at night. |
fresnel(nDotV, f0) |
How much more a surface reflects towards its edges; f0 is the amount head on. |
hueRotate(c, angle) |
A colour with its hue turned by angle (radians); greys stay grey. |
iridescence(t) |
A soft rainbow colour that runs with t, for pearly sheens. |
hash3(p) |
Three random numbers, 0 to 1, for a cell (floor of a position). |
time |
Seconds, for animation. |
Fog is added for you afterwards. A shader that doesn't compile is reported
as a clientScriptError, with where set to
"paint <file>". Up to three lights reach a point: the sun first, then the
two nearest lamps.
Keep shaders modest: shader model 3 has 32 temporary registers, and Wine's shader compiler (Linux players, Proton, Lutris) needs more than Windows' for the same code. A shader that needs more than 32 is reported as an error (and the paint drawn plainly) rather than drawn as garbage. Fewer values held at once, one pass over the lights instead of several, and fewer texture reads all help.
examples/freeroam/client-files/shaders/paint_chrome.hlsl is a starting
point.