//recursively lerp the influences of the neighboring grid points to the pixel //in the first iteration, the last index is split between 0 and 1 // (0, 0, ..., 0) (0, 1, ..., 0) vs. (0, 0, ..., 1) (0, 1, ..., 1) // the weight by which these two groups will be lerped is the faded value // weight = last coordinate of ((the map coordinate of v) - (the basis node)) //then for the second iteration, the second last index is split between 0 and 1 // weight = 2nd last of ... //and so on private float RecurLerpTree( PerlinNode[] influencers, VecN[] deviations, VecN w, int left, int right, int iteration ) { if (right - left == 1) { //only two elements to be processed //by def. the iteration number will be d-1 at this point //calculate left and right influences with dist dot grad float lInf = w.Subtract(deviations[left]).Dot(influencers[left].gradient); float rInf = w.Subtract(deviations[right]).Dot(influencers[right].gradient); //lerp the two influences using the weight of the first digit //this is because the 01 difference of the deviations are at the first digit float lerped = Lerp( lInf, rInf, Fade(w.coord[d - iteration - 1]) ); return(lerped); } //for more than one element, by def the length of influencers should always be even //lerp the left and right influences using the last iteration'th value of w\ return(Lerp( RecurLerpTree(influencers, deviations, w, left, left + (right - left) / 2, iteration + 1), RecurLerpTree(influencers, deviations, w, left + (right - left) / 2 + 1, right, iteration + 1), Fade(w.coord[d - iteration - 1]) )); }
//return a new VecN instance representing the result of a VecN instance multiplied //by a scalar value public static VecN Scale(VecN a, float b) { float[] c = new float[a.d]; for (int i = 0; i < a.d; i++) { c[i] = a.coord[i] * b; } return(new VecN(c)); }
//return the radian angle of a VecN instance public static float Angle(VecN a) { if (a.d != 2) { throw new System.ArgumentException("angle only applies to 2D vectors, not " + a.d + "D"); } return(Mathf.Atan2(a.coord[1], a.coord[0])); }
//returns a new VecN instance representing the modulo b version of a VecN instance //basically applies mod b to every coordinate of a public static VecN Mod(VecN a, float b) { float[] coord = new float[a.d]; for (int i = 0; i < a.d; i++) { coord[i] = a.coord[i] % b; } return(new VecN(coord)); }
public PerlinNode(VecN coord) { this.coord = coord; //generate random d-dimensional unit vector gradient = Gaussian.UnitVecN(coord.d); //gradient = new VecN(new float[] { //1, 0 //}); }
//return a new VecN instance representing the normalized result of //a VecN instance public static VecN Normalize(VecN a) { float l = a.Norm(); if (l != 0.0f) { return(a.Scale(1 / l)); } else { return(a.Scale(1.0f)); //not returning a for the sake of cloning } }
//return a new VecN instance (for N = 3) representing the cross product //of two 3D vectors public static VecN Cross(VecN a, VecN b) { if (a.d != 3 || b.d != 3) { throw new System.ArgumentException("cross product only takes 3D vectors, not " + a.d + " and " + b.d + "D vectors"); } else { return(new VecN(new float[] { a.coord[1] * b.coord[2] - a.coord[2] * b.coord[1], a.coord[2] * b.coord[0] - a.coord[0] * b.coord[2], a.coord[0] * b.coord[1] - a.coord[1] * b.coord[0], })); } }
//find the base adjacent grid-coordinate for a given d-dimensional vector //the first element returned is the basis node //the second element is the deviation vector that goes from the pixel to the basis node public ArrayList FindBasis(VecN a) { if (a.d != d) { throw new System.ArgumentException( "dimension of pixel (" + a.d + ") does not match the dimension of this noise map (" + d + ")"); } else { int[] coord = new int[a.d]; float[] deviation = new float[a.d]; for (int i = 0; i < a.d; i++) { //fit the i'th item of the coordinate into the range [0, w] float temp = a.coord[i]; if (temp < 0) //for negative values of a coordinate { if (temp % w == 0.0f) { temp = 0; } else { temp += (int)(-1 * temp / w + 1) * w; } } else if (temp > w) //for positive overflow (repeat) { temp = temp % w; } deviation[i] = temp % 1.0f; //round the coordinate value down to get the basis value coord[i] = (int)temp; } int l = Unwrap(coord); if (l < 0 || l > nodes.Length) { throw new ArgumentException("length is out of bounds, " + l); } ArrayList result = new ArrayList(); result.Add(nodes[l]); result.Add(new VecN(deviation)); return(result); } }
//convert a Vec-d instance to the corresponding //length coordinate on the space filling curve public int Unwrap(VecN a) { if (a.d != d) { throw new System.ArgumentException( "the coordinate's dimension " + a.d + " does not match with the noise map's dimension " + d); } int result = 0; for (int i = 0; i < d; i++) { result += (int)a.coord[i] * (int)Mathf.Pow(w, i); } return(result); }
//return a new VecN instance representing the difference of two //VecN instances with the same dimensions (a - b), not (b - a) public static VecN Subtract(VecN a, VecN b) { if (a.d != b.d) { throw new System.ArgumentException("dimensions do not agree when calculating difference: " + a.d + " and " + b.d); } else { float[] c = a.coord.Clone() as float[]; for (int i = 0; i < a.d; i++) { c[i] = a.coord[i] - b.coord[i]; } return(new VecN(c)); } }
//notice that none of the functions below modify the original VecN instance //return the dot product of two vectors with the same dimensions public static float Dot(VecN a, VecN b) { if (a.d != b.d) { throw new System.ArgumentException("dimensions do not agree when calculating dot product: " + a.d + " and " + b.d); } else { float result = 0.0f; //Debug.Log("\t\tdot called with returning:"); for (int i = 0; i < a.d; i++) { result += a.coord[i] * b.coord[i]; //Debug.Log("\t\t\t" + i + "th value is" + (a.coord[i] * b.coord[i])); } return(result); } }
/* * The noise map is noted by a grid of d-dimensional vectors going from: * (0, 0, ..., 0), (1, 0, ..., 0), ..., (w-1, 0, ..., 0) * (0, 1, ..., 0), (1, 1, ..., 0), ..., (w-1, 1, ..., 0) * ...(w-1, w-1, ..., w-1) * and each vector corresponds to a measurement of length for a space filling curve * which works similar to a w-based number: * digit 0 * 1 + digit 1 * w + ... + digit d-1 * w ^ d-1 */ //beware that this method is not very efficient as it uses a rectangular grid instead of triangular //initialize with flavor specification public Perlin(int w, int d, int f) { this.w = w; this.d = d; this.f = f; //initiate the relative positions of adjacent grids to the basis grid //use a cursor going from 0 to 2^n-1 to iterate across all the //possible deviations associated with adjacent coordinates grid_deviations = new VecN[(int)Mathf.Pow(2, d)]; for (int i = 0; i < grid_deviations.Length; i++) { float[] c = new float[d]; for (int j = 0; j < d; j++) { c[j] = (i >> j) & 1; //gets the bit value of the j'th digit of i } grid_deviations[i] = new VecN(c); } //there are w * ... * w pivots in total, w^d nodes = new PerlinNode[(int)Mathf.Pow(w, d)]; //iterate through the space filling curve by length //populate the noise map and configure the adjacency relationships for (int i = 0; i < nodes.Length; i++) { //populate the map and set the gradient vector (see constructor for pn) PerlinNode pn = new PerlinNode(Wrap(i)); nodes[i] = pn; } for (int i = 0; i < nodes.Length; i++) { //setup adjacency relationships nodes[i].neighbors = FindAdjacency(nodes[i]); } //Debug.Log(nodes[0].PrintNeighbors()); }
//find the noise in the noisespace corresponding to v public float Noise(VecN v) { if (v.d != d) { throw new System.ArgumentException( "dimension of pixel (" + v.d + ") does not match the dimension of this noise map (" + d + ")"); } ArrayList found = FindBasis(v); //the basis node PerlinNode[] influencers = (found[0] as PerlinNode).neighbors; //influences[0] is basically the basis grid node //w is how much it deviates from that node, it's values will be //for lerping purposes VecN w = found[1] as VecN; float n = RecurLerpTree(influencers, grid_deviations, w, 0, grid_deviations.Length - 1, 0); return(Mathf.Min(Mathf.Max(0, n + 0.5f), 1.0f)); }
// update tex's content private void FillTexture() { for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { //calculate noise VecN marker = new VecN( new float[] { i / (float)pixelPerGrid, j / (float)pixelPerGrid, t } ); float n = map.Noise(marker); //convert to color format byte col = (byte)(n * 255); tex.SetPixel(i, j, new Color32(col, col, col, 255)); } } tex.Apply(); }
//return a new VecN instance representing the sum of //this VecN instance with another vector public VecN Add(VecN a) { return(Add(this, a)); }
//return dot product of the current VecN instance with another vector public float Dot(VecN a) { return(Dot(this, a)); }
//return a new VecN instance (for N = 3) representing the cross product //of this VecN instance with another 3D vector public VecN Cross(VecN a) { return(Cross(this, a)); }
//return a new VecN instance representing this VecN instance minus //another VecN instance public VecN Subtract(VecN a) { return(Subtract(this, a)); }
//return the length of a VecN instance public static float Norm(VecN a) { return(Mathf.Sqrt(Dot(a, a))); }