Advertisement Jump to content
  • Advertisement
  • entries
  • comments
  • views

Derivative Noise

Sign in to follow this  


The other day I was skimming through the forums and I stumbled across this post concerning reading material about noise functions. That subject being near and dear to my heart, and one of my projects being a library to modularize the generation of noise, I took a look at some of the posted links. A couple of them seemed interesting, notably:


The second of the above links details a couple noise variants that the author came up with, that he calls Swiss noise and Jordan noise (named for their apparent similarity to terrain found in Switzerland and Jordan, respectively). The variants utilize a technique that I first saw used by Inigo Quilez; that is, using the derivative of the noise of one layer/octave to modify the contributions of successive layers. It's a neat idea, but one that, unfortunately, isn't well-supported by the current versions of the noise generators in my Accidental Noise Library. The existing support for derivative noise uses a specific module that approximates the derivative using multi-sampling, and requires one module (with the attendant overhead of multi-sampling the source) for each axis of the noise function, so constructing a fractal based on derivative noise would be a bit clunky. I'm trying to change that, since including derivative calculations in the noise generation itself, while the noise value is being generated, opens up some pretty interesting effects.

The main reason it isn't currently supported is that the ANL library supports a number of different basis generators: classic value noise, Perlin noise, Perlin simplex noise, cellular noise, white noise, etc... Yet the library was designed such that the various variants can be mixed and matched, combined willy-nilly as the various octaves of a fractal, if so desired, and the various generators were not built to calculate derivative of the function alongside the value. In fact, figuring out how to calculate derivative alongside noise for some of the generators could prove quite tricky indeed.

A second reason lies in the process of calculating the derivative itself, and in how the noise is generated in ANL. You see, the typical Perlin classic noise process involves generating the noise values at the corners of a cube (or N-dimensional hypercube) and interpolating the values across the volume to obtain the final value. In ANL, I represented this as explicit interpolations; ie, 3D interpolated noise looks something like this:

double interp_X_3(double x, double y, double z, double xs, int x0, int x1, int iy, int iz, unsigned int seed, worker_noise_3 noisefunc)
double v1=noisefunc(x,y,z,x0,iy,iz,seed);
double v2=noisefunc(x,y,z,x1,iy,iz,seed);
return lerp(xs,v1,v2);

double interp_XY_3(double x, double y, double z, double xs, double ys, int x0, int x1, int y0, int y1, int iz, unsigned int seed, worker_noise_3 noisefunc)
double v1=interp_X_3(x,y,z,xs,x0,x1,y0,iz,seed,noisefunc);
double v2=interp_X_3(x,y,z,xs,x0,x1,y1,iz,seed,noisefunc);
return lerp(ys,v1,v2);

double interp_XYZ_3(double x, double y, double z, double xs, double ys, double zs, int x0, int x1, int y0, int y1, int z0, int z1, unsigned int seed, worker_noise_3 noisefunc)
double v1=interp_XY_3(x,y,z,xs,ys,x0,x1,y0,y1,z0,seed,noisefunc);
double v2=interp_XY_3(x,y,z,xs,ys,x0,x1,y0,y1,z1,seed,noisefunc);
return lerp(zs,v1,v2);

It's not pretty, what with the exponentially exploding number of function arguments required as axes are added, but it has its own sort of elegance given the problem. The problem, you see, is that of exponential increase of the number of interpolations necessary to generate noise. In 2D noise, 4 points are interpolated using 3 interpolation operations. In 3D noise, 8 points are interpolated using 7 operations. Since ANL supports up to 6D noise (in order to perform distortionless seamless mapping in 3 dimensions as per the technique I detailed at http://www.gamedev.n...seamless-noise/ ), the 6D version requires interpolating 64 different points using 63 different interpolation operations.

Handling the 4D and 6D variants using the chained function call method is a bit less messy and more elegant than the alternatives, and I chose that approach to save myself some work.

However, in order to calculate the derivative of noise in-place, this chained function calling doesn't work that well. In Quilez's method, the interpolation operations are combined into a single large operation which is simplified/expanded to a polynomial from which coefficients are extracted, the coefficients being various combinations of the lattice sample point values. The polynomial has a number of terms equal to the number of sample points being interpolated. The coefficients of the terms are calculated from the sample points. Solving the polynomial for a given set of inputs and the enclosing sample points results in the noise value of the input point; and you can take the derivative of the polynomial with respect to a given input coordinate axis in order to calculate the derivative of the noise in that axis at the same time. Doing it this way is very simple and straightforward.

The problem, of course, is that while it is easy enough to construct the polynomial by hand for the 2D and 3D cases (and not extremely overwhelming even in the 4D case, though a bit of a pain), the 6D case is a mess. If you are going to try to simplify and expand a 64 term polynomial from a series of nested linear interpolation operations, you are going to need a rather large sheet of paper, and you are probably going to make mistakes. And especially considering how far in the past my last calculus course was, and how extremely rusty and shaky my skills are these days, doing that by hand just didn't seem appealing at all.

I don't have any kind of software to help me with these kinds of things. I'm sure such software exists, but if so, it's nothing I'm familiar with. So for a long time now, I've avoided trying to implement in-place derivative noise in ANL. Recently, though, I've had some free time at work to tinker with things, and I decided to try my hand at writing some Lua script to help me construct the 4D and 6D polynomials and extract the terms. It has required me to delve into old college textbooks on expression parsing, converting infix to prefix notation, etc... In other words, it's been fun.

I started with writing a function to convert an expression from infix to prefix. This, of course, is standard algorithm textbook stuff and only took a couple minutes to implement. I didn't want to write a general-case parser, so I imposed the requirement of the tokens in the input string being white-space delineated, so that I could extract the tokens using simple string.gmatch calls in Lua. The function outputs a string in prefix notation, whitespace-delineated.

function priority(token)
if token=="*" or token=="/" then return 2 end
if token=="+" or token=="-" then return 1 end
return 0

function InfixToPrefix(s)
local tokens=s:tokenize()
local stack=LinkedList()
local pf=""
local i

for i=1,#tokens,1 do
local token=tokens
--print("token: "..token)

if token=="(" then
elseif token==")" then
while stack:getFront() ~= "(" do
pf=stack:popFront().." "
elseif token=="+" or token=="-" or token=="*" or token=="/" then
while priority(stack:getFront()) > priority(token) do
pf=stack:popFront().." "
pf=token.." "
while stack:isEmpty()==false do
pf=stack:popFront().." "
return pf

From this string I build an expression tree. Each node of the tree can be one of three basic types: operator, unknown operand, or coefficient operand. The unknown operands correspond to the input coordinates (x, y, z, etc...), and the coefficients are derived from the sample points (A, B, C, etc...). I instituted this division between operand types to facilitate simplifying and expanding the terms, since I want to be able to isolate terms in regards to their unknowns, and group together the coefficients.

Once the expression tree is constructed, I recurse the tree and apply a few simple rules. The rules are as follows:

1) If a node is a "*" operation between two operands of type unknown (x, y, z, etc...), I concatenate the node into a single node of type unknown operand whose value is a string concatenation of the two initial unknowns. IE, if a node is represented as "x*y" then the result of this simplification will be a single token "xy".

2) If a node is a "*" operation between an unknown and another "*", and that right-hand side is a "*" with an unknown on the left, then the unknowns are concatenated. ie, a chain of nodes such as "x*(y*a)" reduces to "xy*a".

3) If a node is a "*" operation with the left operand being a coefficient and the right being an unknown, the two are swapped.

4) If a node is a "*" operation with the left side being an operand and the right side being a "+" or "-" operation, then the distributive property is applied and a new sub-tree constructed. ie, if the tree is equal to "a*(b+c)" the result will be a new tree "a*b + a*c".

The above rules are applied to the expression tree to construct the polynomial. However, the polynomial is still nested, requiring another pass to invert +/- signs of operations for subtraction operations. ie, a tree of the form "a-(b+c)" will be converted to "a-b-c". This involves recursing the tree and if a node is a subtraction operation, the right child tree will be recursed and every + changed to -, every - changed to +. At this point, the tree is actually technically "broken"; if you interpret the tree as-is, using tree traversal, the terms will be slightly wrong, if you assume that a sub-tree represents a sub-expression that can be parenthesized. However, if you traverse the tree and do a simple leftchild-root-rightchild traversal, outputting terms, then the result is our simple polynomial; or, rather, the polynomial before coefficients are grouped.

Here are the functions for the simplification operations:
function ExpandNode(node)
if node.type=="operator" then

-- Now, apply unknown multiplication simplification rule
if node.value=="*" then
if node.left.type=="operand" and node.left.operandtype=="unknown" and node.right.type=="operand" and node.right.operandtype=="unknown" then
-- This simplifies down to a node
local newnode={}
return newnode
elseif node.left.type=="operand" and node.left.operandtype=="unknown" and node.right.type=="operator" and node.right.value=="*" and node.right.left.type=="operand" and node.right.left.operandtype=="unknown" then
local newnode={}
return newnode
elseif node.left.type==operand and node.left.operandtype=="coeff" and node.right.type=="operand" and node.right.operandtype=="unknown" then
-- Switch this one around
local newnode={}
return newnode
if node.left.type=="operator" and node.right.type=="operand" then print("swap") node.left,node.right=node.right,node.left end -- Swap
if node.left.type=="operand" and node.right.type=="operator" and (node.right.value=="+" or node.right.value=="-") then
-- Expand this one using distributive
local newnode={}



return newnode

return node

-- Function to invert the +/- signs of a tree chain
function InvertSigns(node)
if node.type=="operator" then
if node.value=="+" then node.value="-" elseif node.value=="-" then node.value="+" end
if node.value=="+" or node.value=="-" then

-- function to check a node to see if it is a subtraction operation, and if the right child is an addition or subtraction. If so, invert the operation of the right
-- child.

function InvertSubtractions(node)
if node.type=="operator" then
if node.type=="operator" and node.value=="-" then
The expansion/simplification is a bit hackish, but it works.

In order to construct the initial expressions that start the process, I wrote some helper functions which perform the chaining interpolation and generate an expression string:

function lerp(t,a,b)
return " ( "..a.." + "..t.." * ( "..b.." - "..a.." ) ) "

function lerp2(s,t,a,b,c,d)
return lerp(t,lerp(s,a,b),lerp(s,c,d))

function lerp3(s,t,u,a,b,c,d,e,f,g,h)
return lerp(u, lerp2(s,t,a,b,c,d), lerp2(s,t,e,f,g,h))

function lerp4(s,t,u,v,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)
return lerp(v, lerp3(s,t,u,a,b,c,d,e,f,g,h), lerp3(s,t,u,i,j,k,l,m,n,o,p))

function lerp5(s,t,u,v,w,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF)
return lerp(w,lerp4(s,t,u,v,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), lerp4(s,t,u,v,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF))

return lerp(x, lerp5(s,t,u,v,w,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF), lerp5(s,t,u,v,w,GG,HH,II,JJ,KK,LL,MM,NN,OO,PP,QQ,RR,SS,TT,UU,VV,WW,XX,YY,ZZ,AAA,BBB,CCC,DDD,EEE,FFF,GGG,HHH,III,JJJ,KKK,LLL))

For the purposes of illustration, here is a traversal of the expression tree generated by lerp6, before expansion into polynomial form.


Go ahead and try to reduce/expand/simplify that by hand into a polynomial. I dare you.

Expanding the tree results in the creation of terms, although it doesn't result in the final polynomial form. To do that, I had to come up with a way to combine terms that share unknown operand groups. If you are curious, here are the terms generated from lerp6, after expansion and simplification but before grouping coefficients:


If you were wondering, there are 728 separate terms there.

The next step is to combine the groups so that I can extract the coefficients of the terms. The way I elected to do this was to traverse the tree and build a string of the above terms (+/- unknown * coeff) delimited by some spacing character, "#", between the terms and whitespace between the sign, unknown, "*" and coeff portions. This string then is easy to parse into the individual terms.

From each term, the group is extracted and a bucket in a group list is created to hold that term if none exists. Then, a table is made from the sign and the coefficient, and this is added to the group list.

The result of this is a bucket full of groups, each group named for the unknowns (or interpolants) that are used in the term, and the elements of each group are the coefficients (grid samples) that contribute to the term. Here is the result of iterating the group list for lerp3:

y: +c-a

x: +b-a

zx: +f-e-b+a

z: +e-a

yx: +d-c-b+a

zyx: +h-g-f+e-d+c+b-a

zy: +g-e-c+a


From where I sit, those coefficients look correct, and are exactly the same as the coefficients in Quilez's code. I have verified the 2D and 4D cases as well, and they produce the expected results.

Once I have the terms of the polynomial, it is easy enough to build the actual code expressions to calculate the noise value, and construct expressions to calculate the derivative. The derivative, of course, is calculated with respect to a particular unknown, or axis, so the derivative of a noise function will be a vector of dimensionality equal to the dimensionality of the noise function. The final noise function can then be constructed. Here is the final 6D noise function:

double gradientderiv_noise6D(double *derivs, double x, double y, double z, double w, double u, double v, unsigned int seed, interp_func interp, interp_func interpderiv)
int x0=fast_floor(x);
int y0=fast_floor(y);
int z0=fast_floor(z);
int w0=fast_floor(w);
int u0=fast_floor(u);
int v0=fast_floor(v);

double xs=interp((x-(double)x0));
double ys=interp((y-(double)y0));
double zs=interp((z-(double)z0));
double ws=interp(w-(double)w0);
double us=interp(u-(double)u0);
double vs=interp(v-(double)v0);
double xd=interpderiv(x-(double)x0);
double yd=interpderiv(y-(double)y0);
double zd=interpderiv(z-(double)z0);
double wd=interpderiv(w-(double)w0);
double ud=interpderiv(u-(double)u0);
double vd=interpderiv(v-(double)v0);

double A=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0, u0, v0, seed);
double B=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0, u0, v0, seed);
double C=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0, u0, v0, seed);
double D=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0, u0, v0, seed);
double E=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0, u0, v0, seed);
double F=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0, u0, v0, seed);
double G=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0, u0, v0, seed);
double H=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0, u0, v0, seed);
double I=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0+1, u0, v0, seed);
double J=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0+1, u0, v0, seed);
double K=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0+1, u0, v0, seed);
double L=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0+1, u0, v0, seed);
double M=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0+1, u0, v0, seed);
double N=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0+1, u0, v0, seed);
double O=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0+1, u0, v0, seed);
double P=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0+1, u0, v0, seed);
double Q=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0, u0+1, v0, seed);
double R=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0, u0+1, v0, seed);
double S=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0, u0+1, v0, seed);
double T=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0, u0+1, v0, seed);
double U=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0, u0+1, v0, seed);
double V=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0, u0+1, v0, seed);
double W=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0, u0+1, v0, seed);
double X=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0, u0+1, v0, seed);
double Y=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0+1, u0+1, v0, seed);
double Z=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0+1, u0+1, v0, seed);
double AA=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0+1, u0+1, v0, seed);
double BB=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0+1, u0+1, v0, seed);
double CC=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0+1, u0+1, v0, seed);
double DD=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0+1, u0+1, v0, seed);
double EE=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0+1, u0+1, v0, seed);
double FF=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0+1, u0+1, v0, seed);
double GG=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0, u0, v0+1, seed);
double HH=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0, u0, v0+1, seed);
double II=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0, u0, v0+1, seed);
double JJ=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0, u0, v0+1, seed);
double KK=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0, u0, v0+1, seed);
double LL=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0, u0, v0+1, seed);
double MM=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0, u0, v0+1, seed);
double NN=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0, u0, v0+1, seed);
double OO=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0+1, u0, v0+1, seed);
double PP=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0+1, u0, v0+1, seed);
double QQ=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0+1, u0, v0+1, seed);
double RR=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0+1, u0, v0+1, seed);
double SS=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0+1, u0, v0+1, seed);
double TT=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0+1, u0, v0+1, seed);
double UU=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0+1, u0, v0+1, seed);
double VV=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0+1, u0, v0+1, seed);
double WW=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0, u0+1, v0+1, seed);
double XX=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0, u0+1, v0+1, seed);
double YY=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0, u0+1, v0+1, seed);
double ZZ=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0, u0+1, v0+1, seed);
double AAA=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0, u0+1, v0+1, seed);
double BBB=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0, u0+1, v0+1, seed);
double CCC=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0, u0+1, v0+1, seed);
double DDD=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0, u0+1, v0+1, seed);
double EEE=grad_noise_6(x,y,z,w,u,v, x0, y0, z0, w0+1, u0+1, v0+1, seed);
double FFF=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0, w0+1, u0+1, v0+1, seed);
double GGG=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0, w0+1, u0+1, v0+1, seed);
double HHH=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0, w0+1, u0+1, v0+1, seed);
double III=grad_noise_6(x,y,z,w,u,v, x0, y0, z0+1, w0+1, u0+1, v0+1, seed);
double JJJ=grad_noise_6(x,y,z,w,u,v, x0+1, y0, z0+1, w0+1, u0+1, v0+1, seed);
double KKK=grad_noise_6(x,y,z,w,u,v, x0, y0+1, z0+1, w0+1, u0+1, v0+1, seed);
double LLL=grad_noise_6(x,y,z,w,u,v, x0+1, y0+1, z0+1, w0+1, u0+1, v0+1, seed);

double k0=A;
double k1=B-A;
double k2=C-A;
double k3=E-A;
double k4=I-A;
double k5=Q-A;
double k6=GG-A;
double k7=A-B-C+D;
double k8=A-B-E+F;
double k9=A-B-I+J;
double k10=A-B-Q+R;
double k11=A-B-GG+HH;
double k12=A-C-E+G;
double k13=A-C-I+K;
double k14=A-C-Q+S;
double k15=A-C-GG+II;
double k16=A-E-I+M;
double k17=A-E-Q+U;
double k18=A-E-GG+KK;
double k19=A-I-Q+Y;
double k20=A-I-GG+OO;
double k21=A-Q-GG+WW;
double k22=H-G-F+E-D+C+B-A;
double k23=L-K-J+I-D+C+B-A;
double k24=T-S-R+Q-D+C+B-A;
double k25=JJ-II-HH+GG-D+C+B-A;
double k26=N-M-J+I-F+E+B-A;
double k27=V-U-R+Q-F+E+B-A;
double k28=LL-KK-HH+GG-F+E+B-A;
double k29=Z-Y-R+Q-J+I+B-A;
double k30=PP-OO-HH+GG-J+I+B-A;
double k31=XX-WW-HH+GG-R+Q+B-A;
double k32=O-M-K+I-G+E+C-A;
double k33=W-U-S+Q-G+E+C-A;
double k34=MM-KK-II+GG-G+E+C-A;
double k35=AA-Y-S+Q-K+I+C-A;
double k36=QQ-OO-II+GG-K+I+C-A;
double k37=YY-WW-II+GG-S+Q+C-A;
double k38=CC-Y-U+Q-M+I+E-A;
double k39=SS-OO-KK+GG-M+I+E-A;
double k40=AAA-WW-KK+GG-U+Q+E-A;
double k41=EEE-WW-OO+GG-Y+Q+I-A;
double k42=P-O-N+M-L+K+J-I-H+G+F-E+D-C-B+A;
double k43=X-W-V+U-T+S+R-Q-H+G+F-E+D-C-B+A;
double k44=NN-MM-LL+KK-JJ+II+HH-GG-H+G+F-E+D-C-B+A;
double k45=BB-AA-Z+Y-T+S+R-Q-L+K+J-I+D-C-B+A;
double k46=RR-QQ-PP+OO-JJ+II+HH-GG-L+K+J-I+D-C-B+A;
double k47=ZZ-YY-XX+WW-JJ+II+HH-GG-T+S+R-Q+D-C-B+A;
double k48=DD-CC-Z+Y-V+U+R-Q-N+M+J-I+F-E-B+A;
double k49=TT-SS-PP+OO-LL+KK+HH-GG-N+M+J-I+F-E-B+A;
double k50=BBB-AAA-XX+WW-LL+KK+HH-GG-V+U+R-Q+F-E-B+A;
double k51=FFF-EEE-XX+WW-PP+OO+HH-GG-Z+Y+R-Q+J-I-B+A;
double k52=EE-CC-AA+Y-W+U+S-Q-O+M+K-I+G-E-C+A;
double k53=UU-SS-QQ+OO-MM+KK+II-GG-O+M+K-I+G-E-C+A;
double k54=CCC-AAA-YY+WW-MM+KK+II-GG-W+U+S-Q+G-E-C+A;
double k55=GGG-EEE-YY+WW-QQ+OO+II-GG-AA+Y+S-Q+K-I-C+A;
double k57=FF-EE-DD+CC-BB+AA+Z-Y-X+W+V-U+T-S-R+Q-P+O+N-M+L-K-J+I+H-G-F+E-D+C+B-A;

// k0 + xk1 + yk2 + zk3 + wk4 + uk5 + vk6 + xyk7 + xzk8 + xwk9 + xuk10 + xvk11 + yzk12 + ywk13 + yuk14 + yvk15 +
// zwk16 + zuk17 + zvk18 + wuk19 + wvk20 + uvk21 + xyzk22 + xywk23 + xyuk24 + xyvk25 + xzwk26 + xzuk27 + xzvk28 +
// xwuk29 + xwvk30 + xuvk31 + yzwk32 + yzuk33 + yzvk34 + ywuk35 + ywvk36 + yuvk37 + zwuk38 + zwvk39 + zuvk40 +
// wuvk41 + xyzwk42 + xyzuk43 + xyzvk44 + xywuk45 + xywvk46 + xyuvk47 + xzwuk48 + xzwvk49 + xzuvk50 + xwuvk51 + yzwuk52 +
// yzwvk53 + yzuvk54 + ywuvk55 + zwuvk56 + xyzwuk57 + xyzwvk58 + xyzuvk59 + xywuvk60 + xzwuvk61 + yzwuvk62 + xyzwuvk63

double nn=k0 + xs*k1 + ys*k2 + zs*k3 + ws*k4 + us*k5 + vs*k6 + xs*ys*k7 + xs*zs*k8 + xs*ws*k9 + xs*us*k10 + xs*vs*k11 +
ys*zs*k12 + ys*ws*k13 + ys*us*k14 + ys*vs*k15 + zs*ws*k16 + zs*us*k17 + zs*vs*k18 + ws*us*k19 + ws*vs*k20 + us*vs*k21 +
xs*ys*zs*k22 + xs*ys*ws*k23 + xs*ys*us*k24 + xs*ys*vs*k25 + xs*zs*ws*k26 + xs*zs*us*k27 + xs*zs*vs*k28 + xs*ws*us*k29 +
xs*ws*vs*k30 + xs*us*vs*k31 + ys*zs*ws*k32 + ys*zs*us*k33 + ys*zs*vs*k34 + ys*ws*us*k35 + ys*ws*vs*k36 + ys*us*vs*k37 +
zs*ws*us*k38 + zs*ws*vs*k39 + zs*us*vs*k40 + ws*us*vs*k41 + xs*ys*zs*ws*k42 + xs*ys*zs*us*k43 + xs*ys*zs*vs*k44 +
xs*ys*ws*us*k45 + xs*ys*ws*vs*k46 + xs*ys*us*vs*k47 + xs*zs*ws*us*k48 + xs*zs*ws*vs*k49 + xs*zs*us*vs*k50 + xs*ws*us*vs*k51 +
ys*zs*ws*us*k52 + ys*zs*ws*vs*k53 + ys*zs*us*vs*k54 + ys*ws*us*vs*k55 + zs*ws*us*vs*k56 + xs*ys*zs*ws*us*k57 +
xs*ys*zs*ws*vs*k58 + xs*ys*zs*us*vs*k59 + xs*ys*ws*us*vs*k60 + xs*zs*ws*us*vs*k61 + ys*zs*ws*us*vs*k62 + xs*ys*zs*ws*us*vs*k63;

derivs[0] = xd * (k1 + ys*k7 + zs*k8 + ws*k9 + us*k10 + vs*k11 +
ys*zs*k22 + ys*ws*k23 + ys*us*k24 + ys*vs*k25 + zs*ws*k26 + zs*us*k27 + zs*vs*k28 + ws*us*k29 +
ws*vs*k30 + us*vs*k31 + ys*zs*ws*k42 + ys*zs*us*k43 + ys*zs*vs*k44 +
ys*ws*us*k45 + ys*ws*vs*k46 + ys*us*vs*k47 + zs*ws*us*k48 + zs*ws*vs*k49 + zs*us*vs*k50 + ws*us*vs*k51 + ys*zs*ws*us*k57 +
ys*zs*ws*vs*k58 + ys*zs*us*vs*k59 + ys*ws*us*vs*k60 + zs*ws*us*vs*k61 + ys*zs*ws*us*vs*k63);

derivs[1] = yd * (k2 + xs*k7 +
zs*k12 + ws*k13 + us*k14 + vs*k15 +
xs*zs*k22 + xs*ws*k23 + xs*us*k24 + xs*vs*k25 +
zs*ws*k32 + zs*us*k33 + zs*vs*k34 + ws*us*k35 + ws*vs*k36 + us*vs*k37 +
xs*zs*ws*k42 + xs*zs*us*k43 + xs*zs*vs*k44 +
xs*ws*us*k45 + xs*ws*vs*k46 + xs*us*vs*k47 +
zs*ws*us*k52 + zs*ws*vs*k53 + zs*us*vs*k54 + ws*us*vs*k55 + xs*zs*ws*us*k57 +
xs*zs*ws*vs*k58 + xs*zs*us*vs*k59 + xs*ws*us*vs*k60 + zs*ws*us*vs*k62 + xs*zs*ws*us*vs*k63);

derivs[2] = zd * (k3 + xs*k8 +
ys*k12 + ws*k16 + us*k17 + vs*k18 +
xs*ys*k22 + xs*ws*k26 + xs*us*k27 + xs*vs*k28 +
ys*ws*k32 + ys*us*k33 + ys*vs*k34 +
ws*us*k38 + ws*vs*k39 + us*vs*k40 + xs*ys*ws*k42 + xs*ys*us*k43 + xs*ys*vs*k44 +
xs*ws*us*k48 + xs*ws*vs*k49 + xs*us*vs*k50 +
ys*ws*us*k52 + ys*ws*vs*k53 + ys*us*vs*k54 + ws*us*vs*k56 + xs*ys*ws*us*k57 +
xs*ys*ws*vs*k58 + xs*ys*us*vs*k59 + xs*ws*us*vs*k61 + ys*ws*us*vs*k62 + xs*ys*ws*us*vs*k63);

derivs[3] = wd * (k4 + xs*k9 +
ys*k13 + zs*k16 + us*k19 + vs*k20 +
xs*ys*k23 + xs*zs*k26 + xs*us*k29 +
xs*vs*k30 + ys*zs*k32 + ys*us*k35 + ys*vs*k36 +
zs*us*k38 + zs*vs*k39 + us*vs*k41 + xs*ys*zs*k42 +
xs*ys*us*k45 + xs*ys*vs*k46 + xs*zs*us*k48 + xs*zs*vs*k49 + xs*us*vs*k51 +
ys*zs*us*k52 + ys*zs*vs*k53 + ys*us*vs*k55 + zs*us*vs*k56 + xs*ys*zs*us*k57 +
xs*ys*zs*vs*k58 + xs*ys*us*vs*k60 + xs*zs*us*vs*k61 + ys*zs*us*vs*k62 + xs*ys*zs*us*vs*k63);

derivs[4] = ud * (k5 + xs*k10 +
ys*k14 + zs*k17 + ws*k19 + vs*k21 +
xs*ys*k24 + xs*zs*k27 + xs*ws*k29 +
xs*vs*k31 + ys*zs*k33 + ys*ws*k35 + ys*vs*k37 +
zs*ws*k38 + zs*vs*k40 + ws*vs*k41 + xs*ys*zs*k43 +
xs*ys*ws*k45 + xs*ys*vs*k47 + xs*zs*ws*k48 + xs*zs*vs*k50 + xs*ws*vs*k51 +
ys*zs*ws*k52 + ys*zs*vs*k54 + ys*ws*vs*k55 + zs*ws*vs*k56 + xs*ys*zs*ws*k57 +
xs*ys*zs*vs*k59 + xs*ys*ws*vs*k60 + xs*zs*ws*vs*k61 + ys*zs*ws*vs*k62 + xs*ys*zs*ws*vs*k63);

derivs[5] = vd * (k6 + xs*k11 +
ys*k15 + zs*k18 + ws*k20 + us*k21 +
xs*ys*k25 + xs*zs*k28 +
xs*ws*k30 + xs*us*k31 + ys*zs*k34 + ys*ws*k36 + ys*us*k37 +
zs*ws*k39 + zs*us*k40 + ws*us*k41 + xs*ys*zs*k44 +
xs*ys*ws*k46 + xs*ys*us*k47 + xs*zs*ws*k49 + xs*zs*us*k50 + xs*ws*us*k51 +
ys*zs*ws*k53 + ys*zs*us*k54 + ys*ws*us*k55 + zs*ws*us*k56 +
xs*ys*zs*ws*k58 + xs*ys*zs*us*k59 + xs*ys*ws*us*k60 + xs*zs*ws*us*k61 + ys*zs*ws*us*k62 + xs*ys*zs*ws*us*k63);

return nn;


The noise generation part works. I haven't yet gotten around to testing the derivative calculation. I'm sure it works, but the next step is to figure out how to re-design the basic modules in ANL to incorporate derivative calculation when (and only when) desired, in order to set up a proper test. That, of course, will take a bit more doing since I need to figure out how to calculate the derivative of simplex noise, cellular noise, etc..., as well as the derivatives of the combination/selection functions.
Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!