// POV-Ray 3.7 Include File "gradients.inc" // author : Ingo Janssen // date : 2009-09-23 // rev. : 2012-11-19, functions from iquilezes.org // rev. : 2018-12-17, rewrite and scene added, still rather unordered // sources : Various, but mainly, // Golan Levin : http://www.flong.com/texts/code/ // Inigo Quilez: http://www.iquilezles.org/www/articles/functions/functions.htm // //-------------------------------------------------------------------------- #version 3.7; #include "math.inc" //== //epsilon #declare eps=0.00001; // a predefined epsilon in POV-Ray would be nice ;) //== // clip to range zero-one // A: input value 0-1 #declare clipzo = function(A){clip(A,0,1)}; //== // clip to range zero-one +/- epsilon // A: input value A>0 A<1 #declare clipzo_eps = function(A){clip(A,0+eps,1-eps)}; //== //fraction of value #declare fract = function(x){x-floor(x)}; //something for math.inc //== // #0 // a noisy function #declare Noise_sin = function{fract((sin(x)+sin(y)+sin(z))*10000000)}; //== // #1 // exponential // P: power #declare G_expo = function(x,P){pow(x,P)}; //== // #2 // exponential step funtion // K: // N: #declare G_exp_step = function(x,K,N){exp(-K*pow(x,N))}; //== // #3 // impulse function, usefull for animation // K: stretch the impulse width, reaches max 1.0 @ x=1/k #declare G_impulse = function(x,K){(K*x)*exp(1-(K*x))}; //== // #4 // from 0 to one with circular transition #declare G_circ_ease_in = function(x){1-sqrt(1-pow(x,2))}; //== // #5 // from 1 to zero with circular transition #declare G_circ_ease_out = function(x){sqrt(1-pow((1-x),2))}; //== // #6 // approximation to the Raised Inverted Cosine curve but faster to compute // less than 0.1% error in range 0-1 // use, approximate sin or cos trig functions #declare G_Blinn_Wyvill = function(x){4/9*pow(x,6)-17/9*pow(x,4)+22/9*pow(x,2)}; //== // #7 // sigmoid like clamping/clipping function // x: value // y: min value // z: max value #declare G_smoothstep = function(x,y,z){ (3*pow(clipzo((x-y)/(z-y)),2))- (2*pow(clipzo((x-y)/(z-y)),3)) }; //== // #8 // smoother version by K.Perlin // x: value // y: min value // z: max value #declare G_smootherstep=function(x,y,z){ ( 6*pow(clipzo((x-y)/(z-y)),5))- (15*pow(clipzo((x-y)/(z-y)),4))+ (10*pow(clipzo((x-y)/(z-y)),3)) }; //== // #9 // Remap the range 0-1 to 0-1 setting the corners at 0 and the centre at 1 // K: power #declare G_parabola = function(x,K){pow(4.0*x*(1.0-x),K)}; //== // #10 // Two cubic curves meet with a horizontal inflection point // at the control coordinate (A,B), within the unit square #declare G_double_cubic_seat = function(x,A,B){ select( (x<=clipzo_eps(A)), -1, clipzo(B)+(1-clipzo(B))*pow((x-clipzo_eps(A))/(1-clipzo_eps(A)),3), clipzo(B)-clipzo(B)*pow(1-x/clipzo_eps(A), 3.0) ) }; //== // #11 // A: location of inflection on the 0-1 diagonal of unit square // B: the amount of blend with the identity function, tilt of plateau #declare G_double_cubic_seat_linear_blend = function(x,A,B){ select( (x<=clipzo_eps(A)), -1, (1-clipzo(B))*x+(1-(1-clipzo(B)))*(clipzo_eps(A) +(1-clipzo_eps(A))*pow((x-clipzo_eps(A)) /(1-clipzo_eps(A)), 3.0)) , (1-clipzo(B))*x+(1-(1-clipzo(B)))*clipzo_eps(A) *(1-pow(1-x/clipzo_eps(A), 3.0)) ) }; //== // #12 // A: // B: A,B vicinity of plateau // N: flattness, breath of plateau, integer 1-20 #declare G_double_odd_polynomial_seat = function(x,A,B,N){ select( (x<=clipzo_eps(A)), -1, clipzo(B)+(1-clipzo(B))*pow((x-clipzo_eps(A))/(1-clipzo_eps(A)),(2*N+1)), clipzo(B)-clipzo(B)*pow(1-x/clipzo_eps(A),(2*N+1)) ) }; //== // #13 // A: ease out 0-0.5, ease in 0.5-1. #declare G_expo_ease_in_out = function(x,A){ select( (clipzo_eps(A)< 0.5), -1, pow(x, 1.0/(1-(2*(clipzo_eps(A)-0.5)))), pow(x,clipzo_eps(A)*2) ) }; //== // #14 // usefull for adjusting contrast // A: 0-1 #declare G_double_expo_sigmoid = function(x,A){ select( (x<= 0.5), -1, 1-(pow(2*(1-x),1/(1-clipzo_eps(A))))/2, (pow(2*x,1/(1-clipzo_eps(A))))/2 ) }; //== // #15 // has a very natural rate of change // A: slope or growth rate. #declare G_logistic_sigmoid = function(x,A){ (((1/(1+exp(0-((x-0.5)*(1/(1-(clipzo_eps(A)))-1)*2.0)))) -(1/(1+exp((1/(1-(clipzo_eps(A)))-1))))) /((1/(1+exp(0-(1/(1-(clipzo_eps(A)))-1)))) -(1/(1+exp((1/(1-(clipzo_eps(A)))-1)))))) }; //== // #16 // two circular arcs with seat // A: curves inflection point along the diagonal of unit box #declare G_double_circ_seat = function(x,A){ select( (x<=clipzo(A)), -1, 1-sqrt(pow((1-clipzo(A)),2)-pow(x-clipzo(A),2)), sqrt(pow(clipzo(A),2)-pow(x-clipzo(A),2)) ) }; //== // #17 // two circular arcs with vertical joit, sigmoid // A: curves inflection point along the diagonal of unit box #declare G_double_circ_sigmoid = function(x,A){ select( (x<=clipzo(A)), -1, clipzo(A)+sqrt(pow(1-clipzo(A),2)-pow(x-1,2)), clipzo(A)-sqrt(clipzo(A)*clipzo(A)-x*x) ) }; //== // #18 // A,B: two elliptic arcs meet at within the unit square #declare G_double_elliptic_seat = function(x,A,B){ select( (x<=(clipzo_eps(A))), -1, 1-((1-clipzo(B))/(1-(clipzo_eps(A))))*sqrt(pow(1-(clipzo_eps(A)),2) - pow(x-(clipzo_eps(A)),2)) , (clipzo(B)/(clipzo_eps(A))) * sqrt(pow((clipzo_eps(A)),2) - pow(x-(clipzo_eps(A)),2)) ) }; //== // #19 // A,B: two elliptic arcs meet at within the unit square #declare G_double_elliptic_sigmoid = function(x,A,B){ select( (x<=(clipzo_eps(A))), -1, clipzo(B) +((1-clipzo(B))/(1-(clipzo_eps(A))))*sqrt(pow(1-(clipzo_eps(A)),2) -pow(x-1,2)) , clipzo(B)*(1 - (sqrt(pow((clipzo_eps(A)),2) -pow(x,2))/(clipzo_eps(A)))) ) }; //== // #20 // quadratic bezier with a single spline control point // starts at 0,0 ends at 1,1 // A,B : coordinates of the control point #declare G_quadratic_bezier = function(x,A,B){ select( (A=0.5), -1, (1-2*(clipzo(B)))*((sqrt((clipzo(A))*(clipzo(A)) +(1-2*(clipzo(A)))*x) -(clipzo(A)))/(1-2*(clipzo(A)))*(sqrt((clipzo(A))*(clipzo(A)) +(1-2*(clipzo(A)))*x) -(clipzo(A)))/(1-2*(clipzo(A)))) +(2*(clipzo(B)))*(sqrt((clipzo(A))*(clipzo(A)) +(1-2*(clipzo(A)))*x) -(clipzo(A)))/(1-2*(clipzo(A))) , (1-2*(clipzo(B)))*((sqrt((clipzo(A)+eps)*(clipzo(A)+eps) +(1-2*(clipzo(A)+eps))*x) -(clipzo(A)+eps))/(1-2*(clipzo(A)+eps))*(sqrt((clipzo(A)+eps)*(clipzo(A)+eps) +(1-2*(clipzo(A)+eps))*x) -(clipzo(A)+eps))/(1-2*(clipzo(A)+eps))) +(2*(clipzo(B)))*(sqrt((clipzo(A)+eps)*(clipzo(A)+eps) +(1-2*(clipzo(A)+eps))*x) -(clipzo(A)+eps))/(1-2*(clipzo(A)+eps)) ) }; //== // #21 // S-shape with flat tangents at 0 and 1 // N: steepness of the curve ~1-~10 #declare G_symm_double_poly_sigmoid = function(x,N){ select( (even(floor(N))), -1, select((x<=0.5),-1,1+pow(2*(x-1),floor(N))/2,pow(2*x,floor(N))/2)//odd poly , select ((x<=0.5),-1,1-pow(2*(x-1),floor(N))/2,pow(2*x,floor(N))/2)//even poly ) }; //== // #22 // //symmetric around y-axis x -1,1 got no names? http://www.kynd.info #declare G_one_ease_in = function(x,P){1-pow(abs(x),P)}; //== // #23 // #declare G_one_ease_out= function(x,P){1-pow(abs(1-x),P)}; //== // #24 // #declare G_two_ease_in = function(x,P){pow(cos(pi*x/2),P)} //== // #25 // #declare G_two_ease_out= function(x,P){pow(cos(pi*(1-x)/2),P)} //== // #26 // #declare G_three= function(x,P){1-pow(abs(sin(pi*x/2)),P)} //== // #27 // #declare G_four= function(x,P){pow(min(cos(pi*x/2),1-abs(x)),P)} //== // #28 // #declare G_five= function(x,P){1-pow(max(0,abs(x)*2-1),P)} //== // #29 // Won't change an input value unless too small // M = above this threshold there is no change, below a smooth change // N = replaces a zero value #declare G_almost_identity = function(x,M,N){ select( (x>M), -1, ((2.0*N-M)*(x/M)+(2.0*M-3.0*N))*(x/M)*(x/M)+N, x ) } //== // #30 // smoothest step version by K.McDonald // x: value // y: min value // z: max value #declare G_smootheststep=function(x,y,z){ (-20*pow(clipzo((x-y)/(z-y)),7))+ (70*pow(clipzo((x-y)/(z-y)),6))- (84*pow(clipzo((x-y)/(z-y)),5))+ (35*pow(clipzo((x-y)/(z-y)),4)) }; //== // #31 // Remapping range 0-1 to 0-1, corners are remapped to 0 // Curve max at 1.0 // A: // B: #declare G_power_curve_k=function(x,A,B){ (pow(A+B,A+B)/(pow(A,A)*pow(B,B)))*pow(x,A)*pow(1.0-x,B) }; //== // #32 // Remapping range 0-1 to 0-1, corners are remapped to 0 // Curve max defined by S // A: // B: // S: set peak on y #declare G_power_curve=function(x,S,A,B){ S*pow(x,A)*pow(1.0-x,B) }; //== // #33 // K=1 identity curve (in = out) // K<1 gain shape // K>1 S-shaped #declare G_gain = function(x,K){ select( (x<0.5), -1, 1-(0.5*pow(2*(select((x<0.5),-1,1-x,x)),K)), (0.5*pow(2*(select((x<0.5),-1,1-x,x)),K)) ) }; //== // #34 // C: centre of the curve // W: half width of the curve #declare G_cubic_pulse = function(x,C,W){ select((abs(x-C)>W), -1, 1-(abs(x-C)/W)*(abs(x-C)/W)*(3-2*(abs(x-C)/W)), 0 ) }; //== // #35 // smoother but more expensive than cubic // A: 0-1 #declare G_double_expo = function(x,A){ select( (x<=0.5), -1, 1.0 - (pow(2.0*(1.0-x), 1-clipzo_eps(A)))/2.0, (pow(2.0*x, 1-clipzo_eps(A)))/2.0 ) }; //== // test scene // #if(input_file_name="gradients.inc") //+a0.1 +w400 +h400 global_settings {assumed_gamma 1.0} #default{ finish{ ambient 1 diffuse 0}} camera { orthographic location <0.5,0.5,-.1> look_at <0.5,0.5,0> right x*image_width/image_height } #declare Gradient=0; #if(Gradient=0) #declare Pigm=pigment{function{Noise_sin(x,y,x)}}; #elseif(Gradient=1) #declare P=3; #declare Pigm=pigment{function{G_expo(x,P)}} #declare Curve=function(I){G_expo(I,P)}; #elseif(Gradient=2) #declare K=10; #declare N=1; #declare Pigm=pigment{function{G_exp_step(x,K,N)}} #declare Curve=function(I){G_exp_step(I,K,N)} #elseif(Gradient=3) #declare K=10; #declare Pigm=pigment{function{G_impulse(x,K)}}; #declare Curve=function(I){G_impulse(I,K)}; #elseif(Gradient=4) #declare Pigm=pigment{function{G_circ_ease_in(x)}}; #declare Curve=function(I){G_circ_ease_in(I)}; #elseif(Gradient=5) #declare Pigm=pigment{function{G_circ_ease_out(x)}}; #declare Curve=function(I){G_circ_ease_out(I)}; #elseif(Gradient=6) #declare Pigm=pigment{function{G_Blinn_Wyvill(x)}}; #declare Curve=function(I){G_Blinn_Wyvill(I)}; #elseif(Gradient=7) #declare Low = 0.2; #declare High = 0.3; #declare Pigm=pigment{function{G_smoothstep(x,Low,High)}}; #declare Curve=function(I){G_smoothstep(I,Low,High)}; #elseif(Gradient=8) #declare Low = 0.2; #declare High = 0.76; #declare Pigm=pigment{function{G_smootherstep(x,Low,High)}}; #declare Curve=function(I){G_smootherstep(I,Low,High)}; #elseif(Gradient=9) #declare P = 1; #declare Pigm=pigment{function{G_parabola(x,P)}}; #declare Curve=function(I){G_parabola(I,1)}; #elseif(Gradient=10) #declare X = 0.1; #declare Y = 0.7 #declare Pigm=pigment{function{G_double_cubic_seat(x,X,Y)}}; #declare Curve=function(I){G_double_cubic_seat(I,X,Y)}; #elseif(Gradient=11) #declare A = 0.3; #declare B = 0.9; #declare Pigm=pigment{function{G_double_cubic_seat_linear_blend(x,A,B)}}; #declare Curve=function(I){G_double_cubic_seat_linear_blend(I,A,B)}; #elseif(Gradient=12) #declare X = 0.5; #declare Y = 0.6; #declare Breath = 2; #declare Pigm=pigment{function{G_double_odd_polynomial_seat(x,X,Y,Breath)}}; #declare Curve=function(I){G_double_odd_polynomial_seat(I,X,Y,Breath)}; #elseif(Gradient=13) #declare Pigm=pigment{function{G_expo_ease_in_out(x,0.8)}}; #declare Curve=function(I){G_expo_ease_in_out(I,0.8)}; #elseif(Gradient=14) #declare Pigm=pigment{function{G_double_expo_sigmoid(x,0.6)}}; #declare Curve=function(I){G_double_expo_sigmoid(I,0.6)}; #elseif(Gradient=15) #declare Pigm=pigment{function{G_logistic_sigmoid(x,0.8)}}; #declare Curve=function(I){G_logistic_sigmoid(I,0.8)}; #elseif(Gradient=16) #declare Pigm=pigment{function{G_double_circ_seat(x,0.5)}}; #declare Curve=function(I){G_double_circ_seat(I,0.5)}; #elseif(Gradient=17) #declare Pigm=pigment{function{G_double_circ_sigmoid(x,0.3)}}; #declare Curve=function(I){G_double_circ_sigmoid(I,0.3)}; #elseif(Gradient=18) #declare Pigm=pigment{function{G_double_elliptic_seat(x,0.3,0.7)}}; #declare Curve=function(I){G_double_elliptic_seat(I,0.3,0.7)}; #elseif(Gradient=19) #declare Pigm=pigment{function{G_double_elliptic_sigmoid(x,0.7,0.5)}}; #declare Curve=function(I){G_double_elliptic_sigmoid(I,0.7,0.5)}; #elseif(Gradient=20) #declare Pigm=pigment{function{G_quadratic_bezier(x,0.94,0.3)}}; #declare Curve=function(I){G_quadratic_bezier(I,0.94,0.3)}; #elseif(Gradient=21) #declare Exponent = 3; #declare Pigm=pigment{function{G_symm_double_poly_sigmoid(x,Exponent)}}; #declare Curve=function(I){G_symm_double_poly_sigmoid(I,Exponent)}; #elseif(Gradient=22) #declare Exponent = .5; #declare Pigm=pigment{function{G_one_ease_in(x,Exponent)}}; #declare Curve=function(I){G_one_ease_in(I,Exponent)}; #elseif(Gradient=23) #declare Exponent = .5; #declare Pigm=pigment{function{G_one_ease_out(x,Exponent)}}; #declare Curve=function(I){G_one_ease_out(I,Exponent)}; #elseif(Gradient=24) #declare Exponent = .5; #declare Pigm=pigment{function{G_two_ease_in(x,Exponent)}}; #declare Curve=function(I){G_two_ease_in(I,Exponent)}; #elseif(Gradient=25) #declare Exponent = .5; #declare Pigm=pigment{function{G_two_ease_out(x,Exponent)}}; #declare Curve=function(I){G_two_ease_out(I,Exponent)}; #elseif(Gradient=26) #declare Exponent = .5; #declare Pigm=pigment{function{G_three(x,Exponent)}}; #declare Curve=function(I){G_three(I,Exponent)}; #elseif(Gradient=27) #declare Exponent = 0.5; #declare Pigm=pigment{function{G_four(x,Exponent)}}; #declare Curve=function(I){G_four(I,Exponent)}; #elseif(Gradient=28) #declare Exponent = 0.5; #declare Pigm=pigment{function{G_five(x,Exponent)}}; #declare Curve=function(I){G_five(I,Exponent)}; #elseif(Gradient=29) #declare M = 0.2;//threshold |Extreme example values to show result #declare N = 0.1;//if x is zero |M=0.05 N=0.01 or N=eps may make more sense #declare Pigm=pigment{function{G_almost_identity(x,M,N)}}; #declare Curve=function(I){G_almost_identity(I,M,N)}; #elseif(Gradient=30) #declare Low = 0.2; #declare High = 0.76; #declare Pigm=pigment{function{G_smootheststep(x,Low,High)}}; #declare Curve=function(I){G_smootheststep(I,Low,High)}; #elseif(Gradient=31) #declare A = 2; #declare B = 5; #declare S = 30; #declare Pigm=pigment{function{G_power_curve_k(x,A,B)}}; #declare Curve=function(I){G_power_curve_k(I,A,B)}; #elseif(Gradient=32) #declare A = 2; #declare B = 5; #declare S = 50; #declare Pigm=pigment{function{G_power_curve(x,S,A,B)}}; #declare Curve=function(I){G_power_curve(I,S,A,B)}; #elseif(Gradient=33) #declare K = .2; //#declare K = 5; #declare Pigm=pigment{function{G_gain(x,K)}}; #declare Curve=function(I){G_gain(I,K)}; #elseif(Gradient=34) #declare C = .4; //centre of curve #declare W = .2; //half width of curve #declare Pigm=pigment{function{G_cubic_pulse(x,C,W)}}; #declare Curve=function(I){G_cubic_pulse(I,C,W)}; #elseif(Gradient=35) #declare A = .3; //0-1 #declare Pigm=pigment{function{G_double_expo(x,A)}}; #declare Curve=function(I){G_double_expo(I,A)}; #end box {0,1 texture {pigment {Pigm}}} #ifdef(Curve) #for (I,0,1,0.001) sphere{,0.005 pigment{rgb <.5,0,0>}} #end #end #end /* // Splines are nice to define al kind of curves, but handing/control // with functions is a tad cumbersome. Best defined per use case. #declare S= 0.5; #declare G = function{ spline{ linear_spline 0, 0 S, 0.5 1, 1 } }; box { <0, 0, 0>, <1, 1, 1> texture { pigment { function{G(x).gray} } finish { ambient 1.0 diffuse 0.0 } } } #for (I,0,1,0.001) sphere{,0.005 pigment{rgb x}} #end sphere{<1,1,0>,0.05} */