// weights for blending results into a flownoise uniform float3 timeWeights; // opacity of each texture, varying over time uniform float4 scaleWeights; // weight of each scale // linear control of noise uniform float noiseOffset; uniform float noiseFactor; uniform float maximumDensity; sampler2D decorrTex = sampler_state { MinFilter = Nearest; MagFilter = Nearest; }; sampler2D densTex = sampler_state { MinFilter = Linear; MagFilter = Linear; }; sampler2D rotTex = sampler_state { MinFilter = Linear; MagFilter = Linear; }; #ifdef HQ_INTERPOLATION sampler2D texCoords1Tex = sampler_state { MinFilter = Nearest; MagFilter = Nearest; }; sampler2D texCoords2Tex = sampler_state { MinFilter = Nearest; MagFilter = Nearest; }; sampler2D texCoords3Tex = sampler_state { MinFilter = Nearest; MagFilter = Nearest; }; #else sampler2D texCoords1Tex = sampler_state { MinFilter = Linear; MagFilter = Linear; }; sampler2D texCoords2Tex = sampler_state { MinFilter = Linear; MagFilter = Linear; }; sampler2D texCoords3Tex = sampler_state { MinFilter = Linear; MagFilter = Linear; }; #endif // custom perlin noise // it is better if all data related to space position is expressed // in 32 bits floating point ; otherwise 'blocking' appears at small scales // sScale : space scale of the flownoise : size of a 'cell' // rScale : rotation speed is multiplied by this value half fp_perlin_noise_2D(float sScale, float rScale, float2 uv, int layer) { #ifdef HQ_INTERPOLATION float2 c, ci, cf, f; #else half2 c, ci, cf, f; #endif half2 cs00, cs01, cs10, cs11; half4 r; // order : 00, 01, 10, 11 half a, b, alt; // get int/float value of the point relatively to scale c = uv/sScale; ci = floor(c); cf = c - ci; alt = frac(dot(ci,float2(.5,.5))); // will be used for parity checking // compute rotation values on the summits of the surrounding cube // center of texture pixels is (0.5, 0.5) r.x = tex2D(rotTex, (ci+float2(0.5,0.5))*sScale)[layer]; // corner 00 (lower left) r.y = tex2D(rotTex, (ci+float2(0.5,1.5))*sScale)[layer]; // corner 01 (upper left) r.z = tex2D(rotTex, (ci+float2(1.5,0.5))*sScale)[layer]; // corner 10 (lower left) r.w = tex2D(rotTex, (ci+float2(1.5,1.5))*sScale)[layer]; // corner 11 (upper right) // alternates rotation using the parity of ci.x + ci.y // we check whether (ci.x + ci.y)/2 has a fractionnal part if (alt) { r *= float4(1, -1, -1, 1); } else { r *= float4(-1, 1, 1, -1); } r += tex2D(decorrTex, (ci+0.5)*DECORR_TEXEL_SIZE); r *= rScale; // cos and sin of these values sincos(r.x, cs00.y, cs00.x); sincos(r.y, cs01.y, cs01.x); sincos(r.z, cs10.y, cs10.x); sincos(r.w, cs11.y, cs11.x); // dot products + interpolation to compute Perlin Noise f.x = smoothstep(0, 1, cf.x); f.y = smoothstep(0, 1, cf.y); a = lerp(dot(float2(cf.x,cf.y),cs00), dot(float2(cf.x-1,cf.y),cs10), f.x); b = lerp(dot(float2(cf.x,cf.y-1),cs01), dot(float2(cf.x-1,cf.y-1),cs11), f.x); return lerp(a, b, f.y); } // this computes the flownoise, returning 4 noise values in [-1,1] half4 fp_raw_flownoise_2D(float2 uv) { float4 n; half4 nn; half4 scaleCoeff = NOISE_SCALE*half4(1.0, 0.5, 0.25, 0.125); half4 rotCoeff = ROTATION_FACTOR*half4(NOISE_ROTATION); // interpolate texture coordinates #ifdef HQ_INTERPOLATION float2 tc; // using 32 bits interpolation (instead of HW 16 bits interpolation) // avoids some artifacts float2 c, ci, cf; float2 tc00, tc01, tc10, tc11; c = uv/GRID_TEXEL_SIZE + 0.5; ci = floor(c); cf = c - ci; tc00 = tex2D(texCoords1Tex, (ci+float2(-0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 00 (lower left) tc01 = tex2D(texCoords1Tex, (ci+float2(-0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 01 (upper left) tc10 = tex2D(texCoords1Tex, (ci+float2( 0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 10 (lower left) tc11 = tex2D(texCoords1Tex, (ci+float2( 0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 11 (upper right) tc = lerp(lerp(tc00, tc10, cf.x), lerp(tc01, tc11, cf.x), cf.y); #define OFF1 float2( 0 , 0)*OFFSET #define OFF2 float2( 0.11, 0.04)*OFFSET #define OFF3 float2(-0.07,-0.03)*OFFSET #define OFF4 float2(-0.03, 0.01)*OFFSET #else half2 tc; tc = tex2D(texCoords1Tex, uv).xy; #define OFF1 half2(0,0)*OFFSET #define OFF2 half2(0,0)*OFFSET #define OFF3 half2(0,0)*OFFSET #define OFF4 half2(0,0)*OFFSET #endif nn.x = fp_perlin_noise_2D(scaleCoeff.x, rotCoeff.x, tc+OFF1, 0); nn.y = fp_perlin_noise_2D(scaleCoeff.y, rotCoeff.y, tc+OFF2, 0); nn.z = fp_perlin_noise_2D(scaleCoeff.z, rotCoeff.z, tc+OFF3, 0); nn.w = fp_perlin_noise_2D(scaleCoeff.w, rotCoeff.w, tc+OFF4, 0); n = timeWeights.x*nn; #ifdef HQ_INTERPOLATION tc00 = tex2D(texCoords2Tex, (ci+float2(-0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 00 (lower left) tc01 = tex2D(texCoords2Tex, (ci+float2(-0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 01 (upper left) tc10 = tex2D(texCoords2Tex, (ci+float2( 0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 10 (lower left) tc11 = tex2D(texCoords2Tex, (ci+float2( 0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 11 (upper right) tc = lerp(lerp(tc00, tc10, cf.x), lerp(tc01, tc11, cf.x), cf.y); #else tc = tex2D(texCoords2Tex, uv).xy; #endif nn.x = fp_perlin_noise_2D(scaleCoeff.x, rotCoeff.x, tc+OFF1, 1); nn.y = fp_perlin_noise_2D(scaleCoeff.y, rotCoeff.y, tc+OFF2, 1); nn.z = fp_perlin_noise_2D(scaleCoeff.z, rotCoeff.z, tc+OFF3, 1); nn.w = fp_perlin_noise_2D(scaleCoeff.w, rotCoeff.w, tc+OFF4, 1); n += timeWeights.y*nn; #ifdef HQ_INTERPOLATION tc00 = tex2D(texCoords3Tex, (ci+float2(-0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 00 (lower left) tc01 = tex2D(texCoords3Tex, (ci+float2(-0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 01 (upper left) tc10 = tex2D(texCoords3Tex, (ci+float2( 0.5,-0.5))*GRID_TEXEL_SIZE).xy; // corner 10 (lower left) tc11 = tex2D(texCoords3Tex, (ci+float2( 0.5, 0.5))*GRID_TEXEL_SIZE).xy; // corner 11 (upper right) tc = lerp(lerp(tc00, tc10, cf.x), lerp(tc01, tc11, cf.x), cf.y); #else tc = tex2D(texCoords3Tex, uv).xy; #endif nn.x = fp_perlin_noise_2D(scaleCoeff.x, rotCoeff.x, tc+OFF1, 2); nn.y = fp_perlin_noise_2D(scaleCoeff.y, rotCoeff.y, tc+OFF2, 2); nn.z = fp_perlin_noise_2D(scaleCoeff.z, rotCoeff.z, tc+OFF3, 2); nn.w = fp_perlin_noise_2D(scaleCoeff.w, rotCoeff.w, tc+OFF4, 2); n += timeWeights.z*nn; return n; } // defaults : noiseOffset = noiseFactor = 0.25 => remaps noise to [0,1/2] float fp_flownoise_2D(float2 uv) { half4 n = fp_raw_flownoise_2D(uv); float noise = noiseOffset + noiseFactor*clamp(dot(MOD_N, scaleWeights), -1, 1); float dens = clamp(tex2D(densTex, uv).r-0.5, -1, maximumDensity);; return dens + noise; } // simple call to flow noise half4 fp_flownoise_basic(float2 uv : TEXCOORD0) : COLOR { half noise = saturate(fp_flownoise_2D(uv)); return half4(noise,noise,noise,1); } technique t_flownoise { pass P0 { VertexProgram = NULL; FragmentProgram = compile fp40 fp_flownoise_basic(); } }