Skip to content

Instantly share code, notes, and snippets.

@avrahamy
Last active January 2, 2024 09:52
Show Gist options
  • Save avrahamy/bed45af9d09046a02fa8ce9b3b5b4099 to your computer and use it in GitHub Desktop.
Save avrahamy/bed45af9d09046a02fa8ce9b3b5b4099 to your computer and use it in GitHub Desktop.
Logarithmic Binning shader code to be used in a Custom Function Node in Shader Graph (in Unity). LogBinNoise_float uses Logarithmic Binning to calculate the weights of gradient noise functions with different scales. Implemented according to the description in this GDC talk by Sean Feeley on his work on God of War: https://www.youtube.com/watch?v…
// Taken from GradientNoiseNode.cs from the Shader Graph package.
float2 GradientNoise_Dir_float(float2 p) {
// Permutation and hashing used in webgl-nosie goo.gl/pX7HtC
p = p % 289;
// need full precision, otherwise half overflows when p > 1
float x = float(34 * p.x + 1) * p.x % 289 + p.y;
x = (34 * x + 1) * x % 289;
x = frac(x / 41) * 2 - 1;
return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
}
// Taken from GradientNoiseNode.cs from the Shader Graph package.
float GradientNoise_float(float2 UV, float Scale) {
float2 p = UV * Scale;
float2 ip = floor(p);
float2 fp = frac(p);
float d00 = dot(GradientNoise_Dir_float(ip), fp);
float d01 = dot(GradientNoise_Dir_float(ip + float2(0, 1)), fp - float2(0, 1));
float d10 = dot(GradientNoise_Dir_float(ip + float2(1, 0)), fp - float2(1, 0));
float d11 = dot(GradientNoise_Dir_float(ip + float2(1, 1)), fp - float2(1, 1));
fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
return lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x) + 0.5;
}
void LogBinNoise_float(float2 uv, float scale, out float res) {
const float scaleIndex = log2(max(0.001, scale));
const int index = floor(scaleIndex);
float firstScale = pow(2, index - 1);
res = GradientNoise_float(uv, firstScale) * (1 - scaleIndex + index)
+ GradientNoise_float(uv, firstScale * 2) * (2 - scaleIndex + index)
+ GradientNoise_float(uv, firstScale * 4) * (1 + scaleIndex - index)
+ GradientNoise_float(uv, firstScale * 8) * (scaleIndex - index);
res *= 0.25;
}
// This can be used to get the weights for a custom noise function.
// The out variables match the octaves of the noise scales:
// 1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16
// val should be between 1/8 and 4
void LogBin_float(
float scale,
out float _1_16,
out float _1_8,
out float _1_4,
out float _1_2,
out float _1,
out float _2,
out float _4,
out float _8,
out float _16) {
const float scaleIndex = clamp(log2(scale), -3, 2);
const int index = floor(scaleIndex);
float res[9] = {0,0,0,0,0,0,0,0,0};
res[index + 3] = 1 - scaleIndex + index;
res[index + 4] = 2 - scaleIndex + index;
res[index + 5] = 1 + scaleIndex - index;
res[index + 6] = scaleIndex - index;
_1_16 = res[0];
_1_8 = res[1];
_1_4 = res[2];
_1_2 = res[3];
_1 = res[4];
_2 = res[5];
_4 = res[6];
_8 = res[7];
_16 = res[8];
}
@avrahamy
Copy link
Author

avrahamy commented Feb 16, 2022

@sevelee
Copy link

sevelee commented May 30, 2022

YOU'RE BREATHTAKING!

@Pflugshaupt
Copy link

Pflugshaupt commented Oct 16, 2022

If the weights are based on triangle area, shouldn't the weight formulas all be parabolas?
Oh I realize now you calculate the height of the triangle at 4 spots, not the sub-areas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment