Since the noise generation will only happen once per world save, I think I’ll just brute it the way I described above. Naturally I assumed I could ±1 or ±1/width the texcoords to get the neighboring samples, but all attempts failed. How exactly do I access the neighbors in a procedural shader? The texture2d images are created at runtime with a resolution of 129 x 129, but the resolution could change.
Current Shader:
Shader "Modified3DPerlinFrag" {
Properties {
_Octaves ("Octaves" , Float) = 8
_Frequency ("Frequency" , Float) = 1
_Amplitude ("Amplitude" , Float) = 1
_Lacunarity ("Lacunarity" , Float) = 1.92
_Persistence ("Persistence", Float) = 0.8
_Offset ("Offset" , Vector) = (0, 0, 0, 0)
_RidgeOffset ("Ridge Offset", Float) = 1.0
// _HeightScalar ("Height Scalar" , Float) = 1000
// _QuadScalar ("Quad Scalar" , Float) = 1.8
}
SubShader {
Tags {
"RenderType" = "Opaque"
"PreviewType" = "Plane"
}
LOD 100
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
Blend One Zero
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertInput {
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
// float4 color : COLOR;
};
struct vertOutput {
float4 pos : SV_POSITION;
half2 texcoord : TEXCOORD0;
// fixed4 color : COLOR;
};
vertOutput vert (vertInput input) {
vertOutput o;
o.pos = UnityObjectToClipPos (input.pos);
o.texcoord = input.texcoord;
// o.color = input.color;
return o;
}
void FAST32_hash_3D( float3 gridcell,
out float4 lowz_hash_0,
out float4 lowz_hash_1,
out float4 lowz_hash_2,
out float4 highz_hash_0,
out float4 highz_hash_1,
out float4 highz_hash_2 ) // generates 3 random numbers for each of the 8 cell corners
{
const float2 OFFSET = float2( 50.0, 161.0 );
const float DOMAIN = 69.0;
const float3 SOMELARGEFLOATS = float3( 635.298681, 682.357502, 668.926525 );
const float3 ZINC = float3( 48.500388, 65.294118, 63.934599 );
// truncate the domain
gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;
float3 gridcell_inc1 = step( gridcell, float3( DOMAIN - 1.5, DOMAIN - 1.5, DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );
// calculate the noise
float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;
P *= P;
P = P.xzxz * P.yyww;
float3 lowz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );
float3 highz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );
lowz_hash_0 = frac( P * lowz_mod.xxxx );
highz_hash_0 = frac( P * highz_mod.xxxx );
lowz_hash_1 = frac( P * lowz_mod.yyyy );
highz_hash_1 = frac( P * highz_mod.yyyy );
lowz_hash_2 = frac( P * lowz_mod.zzzz );
highz_hash_2 = frac( P * highz_mod.zzzz );
}
float3 Interpolation_C2( float3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }
float Perlin3D( float3 P )
{
// establish our grid cell and unit position
float3 Pi = floor(P);
float3 Pf = P - Pi;
float3 Pf_min1 = Pf - 1.0;
float4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;
FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );
// calculate the gradients
float4 grad_x0 = hashx0 - 0.49999;
float4 grad_y0 = hashy0 - 0.49999;
float4 grad_z0 = hashz0 - 0.49999;
float4 grad_x1 = hashx1 - 0.49999;
float4 grad_y1 = hashy1 - 0.49999;
float4 grad_z1 = hashz1 - 0.49999;
float4 grad_results_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );
float4 grad_results_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );
// Classic Perlin Interpolation
float3 blend = Interpolation_C2( Pf );
float4 res0 = lerp( grad_results_0, grad_results_1, blend.z );
float2 res1 = lerp( res0.xy, res0.zw, blend.y );
float final = lerp( res1.x, res1.y, blend.x );
final *= 1.1547005383792515290182975610039; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)
return final;
}
float PerlinRidged(float3 p, int octaves, float3 offset, float frequency, float amplitude, float lacunarity, float persistence, float ridgeOffset)
{
float sum = 0;
for (int i = 0; i < octaves; i++)
{
float h = 0;
h = 0.5 * (ridgeOffset - abs(4*Perlin3D((p + offset) * frequency)));
sum += h*amplitude;
frequency *= lacunarity;
amplitude *= persistence;
}
return sum;
}
fixed _Octaves;
float _Frequency;
float _Amplitude;
float _Lacunarity;
float _Persistence;
float3 _Offset;
float _CutoutThreshold;
float _RidgeOffset;
float _HeightScalar;
float _QuadScalar;
half4 frag (vertOutput output) : COLOR {
float ResultHeight = PerlinRidged (float3 (output.texcoord.x, output.texcoord.y, 0), _Octaves, _Offset, _Frequency, _Amplitude, _Lacunarity, _Persistence, _RidgeOffset);
ResultHeight = ResultHeight * 0.5;// + 0.5;
return float4 (ResultHeight, 0, 0, 1);
}
ENDCG
}
}
}