Variable SCATTER_TEXTURE_FRAG_BODYConst
SCATTER_TEXTURE_FRAG_BODY: "\n {\n vec3 stQ = vScatterTexPos * uScatterTexScale;\n // Taken BEFORE the branch below. fwidth is undefined under non-uniform\n // control flow, and the strength test is uniform today but the cost of\n // being careless here is garbage derivatives exactly along a silhouette.\n float stPx = max(max(fwidth(stQ.x), fwidth(stQ.y)), fwidth(stQ.z));\n if (uScatterTexEnabled > 0.5 && uScatterTexStrength > 0.0) {\n // Two octaves: one alone is blobby at this scale, and the second is what\n // makes it read as surface rather than as a stain.\n float stN = stNoise(stQ) * 0.65 + stNoise(stQ * 2.7) * 0.35;\n // Fade out once a feature is down to about a pixel. Left in, a\n // sub-pixel pattern does not average to a tint — it crawls as the camera\n // moves, and a forest of crawling trees is far worse than a flat one.\n float stFade = 1.0 - smoothstep(0.5, 1.5, stPx);\n diffuseColor.rgb *= 1.0 + (stN - 0.5) * uScatterTexStrength * stFade;\n }\n }\n" = ...
Fragment half: two octaves of noise, multiplied into
diffuseColor.Multiplicative, so it scales whatever the surface already is — it keeps the hue, survives the seasonal tint recolouring underneath it, and is exactly a no-op at strength 0. Additive would wash a dark trunk and a bright canopy by the same absolute amount and grey both.