海洋シェーダ(反射と屈折)

海洋シェーダ(反射と屈折)

 前回の記事(海洋シェーダ(海の色))では、ノイズで変形させたゲルストナー波によって生成された波に、フレネル反射等を追加することで海を表現しました。この海へ、海面に反射して映るオブジェクトや海中にあるオブジェクトが透過して見える様子を追加しました。

海面の反射

 以前作成した平面の鏡面反射を利用して、海面に反射して映る画像を取得します。取得した画像をそのまま表示するのではなく、スクリーン座標を海の法線によって歪めています。さらに、GetSkyColorで処理し、海面に反射する色を求めています。

float3 GetSkyColor(float3 dir, float3 c){
	dir.y = max(0.0, dir.y);
	float et = 1.0 - dir.y;
	return (1.0 - c) * et + c;
}
			
float3 Reflection(float4 proj_coord, float3 normal, float3 reflect_dir)
{
	float3 sea_reflect_color;
	proj_coord.xyz += normalize(float3(normal.xy * _BumpScale, normal.z));

	#ifdef _PLANARREFLECTION_ON
		sea_reflect_color = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(proj_coord));
		sea_reflect_color = GetSkyColor(reflect_dir, sea_reflect_color);
	#else
		sea_reflect_color = GetSkyColor(reflect_dir, _SkyColor);
	#endif

	return sea_reflect_color;
}

float3 OceanColor(float3 world_pos, float wave_height, float3 normal, float4 proj_coord){
	//lighting
	float3 lightDir = normalize(UnityWorldSpaceLightDir(world_pos));
	float3 viewDir = normalize(UnityWorldSpaceViewDir(world_pos));
	float3 halfDir = normalize(lightDir + viewDir);
	
	//fresnel
	float r = 0.02;
	float facing = saturate(1.0 - dot(normal, viewDir));
	float fresnel = r + (1.0 - r) * pow(facing, 5.0);
	float3 reflectDir = reflect(-viewDir, normal);
	
	float diff = saturate(dot(normal, lightDir)) * _LightColor0;
	//float spec = pow(max(0.0, dot(normal, halfDir)), _Shininess * 128.0) * _LightColor0;	//Blinn-Phong
	
	//https://www.gamedev.net/articles/programming/graphics/rendering-water-as-a-post-process-effect-r2642/
	float dotSpec = saturate(dot(reflectDir, lightDir) * 0.5 + 0.5);
	float spec = (1.0 - fresnel) * saturate(lightDir.y) * pow(dotSpec, 512.0) * (_Shininess * 1.8 + 0.2);
	spec += spec * 25.0 * saturate(_Shininess - 0.05) * _LightColor0;
	
	//reflection
	float3 sea_reflect_color = Reflection(proj_coord, normal, reflectDir);

	float3 sea_base_color = _SeaBaseColor * diff * _BaseColorStrength + lerp(_SeaBaseColor, _SeaShallowColor * _ShallowColorStrength, diff);
	float3 water_color = lerp(sea_base_color, sea_reflect_color, fresnel);
	float3 sea_color = water_color + _SeaShallowColor * (wave_height * 0.5 + 0.2) * _ColorHightOffset;

	//return sea_color + spec;
	return sea_color;
}

海中の屈折

 以前作成した水中の屈折(shader)を利用しています。これを使用することで海面下のオブジェクトを歪ませて表示することができます。

float2 AlignWithGrabTexel (float2 uv)
{
	return (floor(uv * _CameraDepthTexture_TexelSize.zw) + 0.5) * abs(_CameraDepthTexture_TexelSize.xy);
}

float3 RefractionColor(float4 grab_pos, float4 scr_pos, float3 normal)
{
	float4 depth_uv = grab_pos;
	float2 uv_offset = normal.xy * _Distortion;

	depth_uv.xy = grab_pos.xy + uv_offset;
	float surf_depth = UNITY_Z_0_FAR_FROM_CLIPSPACE(scr_pos.z);
	float ref_fix = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float depth_diff = saturate(ref_fix - surf_depth);

	float2 grab_uv;
	grab_uv = AlignWithGrabTexel((grab_pos.xy + uv_offset * depth_diff) / grab_pos.w);
		
	float3 refraction_color = tex2D(_GrabTex, grab_uv);

	return refraction_color;
}

歪ませたオブジェクト以外の場所に海が表示されるようにコードを変更します。base_colorは海の色、_RefractionDepthで海面下のオブジェクトが表示される深さを変更できます。

float3 RefractionColor(float3 base_color, float4 grab_pos, float4 scr_pos, float3 normal)
{
	float4 depth_uv = grab_pos;
	float2 uv_offset = normal.xy * _Distortion;

	depth_uv.xy = grab_pos.xy + uv_offset;
	float surf_depth = UNITY_Z_0_FAR_FROM_CLIPSPACE(scr_pos.z);
	float ref_fix = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float diff = ref_fix - surf_depth;
	float depth_diff = saturate(diff);

	float2 grab_uv;
	grab_uv = AlignWithGrabTexel((grab_pos.xy + uv_offset * depth_diff) / grab_pos.w);
		
	float3 refraction_color = tex2D(_GrabTex, grab_uv);

	float ref_depth = _RefractionDepth;
	float grad = saturate(diff * ref_depth);
	
	return lerp(refraction_color, base_color, grad);
}

オブジェクトの後ろに灰色の領域ができています。これは、カメラの深度テクスチャ(_CameraDepthTexture)全体を歪ませたまま深度差を計算しているためです。そこで以下のように、 カメラの深度テクスチャを海面下のみ歪ませ(ref_fix_f)、これを元に深度差を計算することで修正しました。

float3 RefractionColor(float3 base_color, float4 grab_pos, float4 scr_pos, float3 normal)
{
	float4 depth_uv = grab_pos;
	float2 uv_offset = normal.xy * _Distortion;

	depth_uv.xy = grab_pos.xy + uv_offset;
	float surf_depth = UNITY_Z_0_FAR_FROM_CLIPSPACE(scr_pos.z);
	float ref_fix = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float depth_diff = saturate(ref_fix - surf_depth);

	float2 uv = grab_pos.xy + uv_offset * depth_diff;
	float2 grab_uv = AlignWithGrabTexel(uv / grab_pos.w);
		
	float3 refraction_color = tex2D(_GrabTex, grab_uv);

	depth_uv.xy = uv;
	float ref_fix_f = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));

	float ref_depth = _RefractionDepth;
	float diff = ref_fix_f - surf_depth;
	float grad = saturate(diff * ref_depth);
	
	return lerp(refraction_color, base_color, grad);
}

さらに、深くなるにつれ_SeaBaseColorへ変化した後に徐々に見えなくなるように処理を変更しました。これにより、一定の深さの間はオブジェクトのシルエットが表示されるようになります。_SilhouetteDepthによってシルエットが見える深さを調整することができます。

float3 RefractionColor(float3 base_color, float4 grab_pos, float4 scr_pos, float3 normal)
{
	float4 depth_uv = grab_pos;
	float2 uv_offset = normal.xy * _Distortion;

	depth_uv.xy = grab_pos.xy + uv_offset;
	float surf_depth = UNITY_Z_0_FAR_FROM_CLIPSPACE(scr_pos.z);
	float ref_fix = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float depth_diff = saturate(ref_fix - surf_depth);

	float2 uv = grab_pos.xy + uv_offset * depth_diff;
	float2 grab_uv = AlignWithGrabTexel(uv / grab_pos.w);
		
	float3 refraction_color = tex2D(_GrabTex, grab_uv);

	depth_uv.xy = uv;
	float ref_fix_2 = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float diff = ref_fix_2 - surf_depth;

	float ref_depth = _RefractionDepth;
	float grad_1 = saturate(diff * (ref_depth + _SilhouetteDepth));
	float grad_2 = saturate(diff * ref_depth);
	float3 col = lerp(refraction_color, _SeaBaseColor, grad_1);
	
	return lerp(col, base_color, grad_2);
}

Shader

 作成したシェーダは以下の通りです。

Shader "Ocean/OceanRef"
{
    Properties
    {
		_MainTex ("Texture", 2D) = "gray" {}

		_WaveSpeed("Value", Float) = 1.0
		_WaveTime("Time for Debug", Float) = 0.0 

		[Header(OceanColor)]
		_SeaBaseColor ("SeaBaseColor", Color) = (0, 0, 0, 1)
		_SeaShallowColor ("SeaShallowColor", Color) = (0, 0, 0, 1)
		_BaseColorStrength ("Base Color Strength", Range(0, 2.0)) = 0.5
		_ShallowColorStrength ("Shallow Color Strength", Range(0, 1.0)) = 0.36
		_ColorHightOffset ("Color Hight Offset", Range(0, 1.0)) = 0.15

		[Header(SkyColor)]
		_SkyColor ("SkyColor", Color) = (0, 0, 0, 1)

		[Header(GerstnerWave)]
		_Amplitude ("Amplitude", Vector) = (0.78, 0.81, 0.6, 0.27)
		_Frequency ("Frequency", Vector) = (0.16, 0.18, 0.21, 0.27)
		_Steepness ("Steepness", Vector) = (1.70, 1.60, 1.20, 1.80)
		_Speed ("Speed", Vector) = (24, 40, 48, 60)
		_Noise ("Noise", Vector) = (0.39, 0.31, 0.27, 0.57) 
		_DirectionA ("Wave A(X,Y) and B(Z,W)", Vector) = (0.35, 0.31, 0.08, 0.60)
		_DirectionB ("C(X,Y) and D(Z,W)", Vector) = (-0.95, -0.74, 0.7, -0.5)

		_Amplitude2 ("Amplitude", Vector) = (0.17, 0.12, 0.21, 0.06)
		_Frequency2 ("Frequency", Vector) = (0.7, 0.84, 0.54, 0.80)
		_Steepness2 ("Steepness", Vector) = (1.56, 2.18, 2.80, 1.90)
		_Speed2 ("Speed", Vector) = (32, 40, 48, 60)
		_Noise2 ("Noise", Vector) = (0.33, 0.81, 0.39, 0.45) 
		_DirectionC ("Wave A(X,Y) and B(Z,W)", Vector) = (0.7, 0.6, 0.10, 0.38)
		_DirectionD ("C(X,Y) and D(Z,W)", Vector) = (0.43, 0.07, 0.42, 0.61)

		_NoiseSizeLerp("Noise Size", Range(0, 0.5)) = 0.5
		_NoiseStrength("Noise Strength", Range(0, 5)) = 1.26

		_Shininess ("Shininess", Range(0 ,5)) = 0.27

		[Header(Refraction)]
		_Distortion ("Distortion", Range(-0.5, 0.5)) = 0
		_RefractionDepth ("Refraction Depth", Range(0.01, 0.2)) = 0.03
		_SilhouetteDepth ("Silhouette Depth", Range(0.0, 0.1)) = 0.03

		[Header(Reflection)]
		[Toggle]
		_PlanarReflection ("PlanarReflection", Int) = 0
		_ReflectionTex ("ReflectionTexture", 2D) = "white" {}
		_BumpScale ("BunpScale", Range(0.0, 20.0)) = 1
    }
    SubShader
    {
		Tags { "RenderType"="Transparent" "Queue"="Transparent"}

		GrabPass{ "_GrabTex" }

        Pass
        {
 			Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
			
			#pragma shader_feature _PLANARREFLECTION_ON

            #include "UnityCG.cginc"
			#include "Assets/Shaders/Ocean.cginc"

            struct appdata
            {
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
            };

            struct v2f
            {
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float3 world_pos : TEXCOORD1;
				float4 proj_coord : TEXCOORD2;
				float4 scrPos : TEXCOORD3;
				float4 grabPos : TEXCOORD4;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

			float _WaveTime;

			static const int wave_number = 8;
			static const int count = 4;

            v2f vert (appdata v)
            {
				v2f o;
				float4 vt = v.vertex;
				float4 world_pos = mul(unity_ObjectToWorld, vt);
				o.world_pos = world_pos.xyz;

				float time = _Time.x * _WaveSpeed;
				time = _WaveTime;

				float3 p = 0.0;
				for(int i = 0; i < count; i++){
					p += GerstnerWave(amp[i], freq[i], steep[i], speed[i], noise_size[i], dir[i], world_pos.xz, time, i);
				}
				for(int j = wave_number - count; j < wave_number; j++){
					p += GerstnerWave_Cross(amp[j], freq[j], steep[j], speed[j], noise_size[j], dir[j], world_pos.xz, time, j);
				}
				world_pos.xyz += p;

				vt = mul(unity_WorldToObject, world_pos);

				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.vertex = UnityObjectToClipPos(vt);
				o.proj_coord = ComputeScreenPos(o.vertex);
				o.grabPos = ComputeGrabScreenPos(o.vertex);
				o.scrPos = ComputeScreenPos(o.vertex);

				return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
				//CalcNormal
				float3 world_pos = i.world_pos;
				float3 geo_pos = world_pos;

				float time = _Time.x * _WaveSpeed;
				time = _WaveTime;

				float3 p = 0.0;
				float3 pb = float3(0.05, 0.0, 0.0);
				float3 pt =float3(0.0, 0.0, 0.05);
				float3 v_bi = world_pos.xyz + float3(0.05, 0.0, 0.0);
				float3 v_tan = world_pos.xyz + float3(0.0, 0.0, 0.05);
				for(int m = 0; m < count; m++){
					p += GerstnerWave(amp[m], freq[m], steep[m], speed[m], noise_size[m], dir[m], world_pos.xz, time, m);
					pb += GerstnerWave(amp[m], freq[m], steep[m], speed[m], noise_size[m], dir[m], v_bi.xz, time, m);
					pt += GerstnerWave(amp[m], freq[m], steep[m], speed[m], noise_size[m], dir[m], v_tan.xz, time, m);
				}
				for(int n = wave_number - count; n < wave_number; n++){
					p += GerstnerWave_Cross(amp[n], freq[n], steep[n], speed[n], noise_size[n], dir[n], world_pos.xz, time, n);
					pb += GerstnerWave_Cross(amp[n], freq[n], steep[n], speed[n], noise_size[n], dir[n], v_bi.xz, time, n);
					pt += GerstnerWave_Cross(amp[n], freq[n], steep[n], speed[n], noise_size[n], dir[n], v_tan.xz, time, n);
				}
				world_pos += p;
				float3 normal = normalize(cross(pt - p, pb - p));

				float wave_height = world_pos.y - geo_pos.y;
				float3 result = OceanColor(world_pos, wave_height, normal, i.proj_coord);
				result = RefractionColor(result, i.grabPos, i.scrPos, normal);
                fixed4 col = fixed4(result, 1.0);
                return col;
            }
            ENDCG
        }
    }
}

Ocean.cginc

#include "Noise.cginc"
float _NoiseStrength;
float _NoiseSizeLerp;

sampler2D _FoamTex;
float4 _FoamTex_ST;
float _FoamSize;
float2 _FoamDir;
float _HightFoamStrength;
float _NormalFoamStrength;
float _FoamHeightOffset;

sampler2D _FoamNormalTex;
float4 _FoamNormalTex_ST;
float _FoamNormalStrength;

float4 _Amplitude;
float4 _Frequency;
float4 _Steepness;
float4 _Speed;
float4 _Noise;
float4 _DirectionA;
float4 _DirectionB;
float4 _FoamSpeed;

float4 _Amplitude2;
float4 _Frequency2;
float4 _Steepness2;
float4 _Speed2;
float4 _Noise2;
float4 _DirectionC;
float4 _DirectionD;
float4 _FoamSpeed2;

static const float amp[8] = {_Amplitude.x, _Amplitude.y, _Amplitude.z, _Amplitude.w, _Amplitude2.x, _Amplitude2.y, _Amplitude2.z, _Amplitude2.w};
static const float freq[8] = {_Frequency.x, _Frequency.y, _Frequency.z, _Frequency.w, _Frequency2.x, _Frequency2.y, _Frequency2.z, _Frequency2.w};
static const float steep[8] = {_Steepness.x, _Steepness.y, _Steepness.z, _Steepness.w, _Steepness2.x, _Steepness2.y, _Steepness2.z, _Steepness2.w};
static const float speed[8] = {_Speed.x, _Speed.y, _Speed.z, _Speed.w, _Speed2.x, _Speed2.y, _Speed2.z, _Speed2.w};
static const float2 dir[8] = {_DirectionA.xy, _DirectionA.zw, _DirectionB.xy, _DirectionB.zw, _DirectionC.xy, _DirectionC.zw, _DirectionD.xy, _DirectionD.zw};
static const float noise_size[8] = {_Noise.x, _Noise.y, _Noise.z, _Noise.w, _Noise2.x, _Noise2.y, _Noise2.z, _Noise2.w};
static const float foam_speed[8] = {_FoamSpeed.x, _FoamSpeed.y, _FoamSpeed.z, _FoamSpeed.w, _FoamSpeed2.x, _FoamSpeed2.y, _FoamSpeed2.z, _FoamSpeed2.w};

float4 _SeaBaseColor;
float4 _SeaShallowColor;
float _SeaColorStrength;

float _BaseColorStrength;
float _ShallowColorStrength;
float _ColorHightOffset;

float _WaveSpeed;

float4 _LightColor0;
float _Shininess;

float _Distortion;
float _RefractionDepth;
float _SilhouetteDepth;

sampler2D _GrabTex;
float4 _GrabTex_ST;
float4 _GrabTex_TexelSize;

sampler2D _CameraDepthTexture;
float4 _CameraDepthTexture_ST;
float4 _CameraDepthTexture_TexelSize;

float3 GerstnerWave(float2 amp, float freq, float steep, float speed, float noise, float2 dir, float2 v, float time, int seed)
{
	float3 p;
	float2 d = normalize(dir.xy);
	float q = steep;

	seed *= 3;
	v +=  noise2(v * noise + time, seed) * _NoiseStrength;
	float f = dot(d, v) * freq + time * speed;
	p.xz = q * amp * d.xy * cos(f);
	p.y = amp * sin(f);

	return p;
}

float3 GerstnerWave_Cross(float2 amp, float freq, float steep, float speed, float noise, float2 dir, float2 v, float time, int seed)
{
	float3 p;
	float2 d = normalize(dir.xy);
	float q = steep;

	float noise_strength = _NoiseStrength;
	seed *= 3;

	float3 p1;
	float3 p2;
	float2 d1 = normalize(dir.xy);
	float2 d2 = float2(-d.y, d.x);

	float2 v1 = v + noise2(v * noise + time * d * 10.0, seed) * noise_strength;
	float2 v2 = v + noise2(v * noise + time * d * 10.0, seed + 12) * noise_strength;

	float2 f1 = dot(d1, v1) * freq + time * speed;
	float2 f2 = dot(d2, v2) * freq + time * speed;
	p1.xz = q * amp * d1.xy * cos(f1);
	p1.y = amp * sin(f1);
	p2.xz = q * amp * d2.xy * cos(f2);
	p2.y = amp * sin(f2);

	p = lerp(p1, p2, noise2(v * _NoiseSizeLerp + time, seed) * 0.5 + 0.5);

	return p;
}

int _PlanarReflection;
sampler2D _ReflectionTex;
float4 _ReflectionTex_ST;
float _BumpScale;
float4 _SkyColor;

float3 GetSkyColor(float3 dir, float3 c){
	dir.y = max(0.0, dir.y);
	float et = 1.0 - dir.y;
	return (1.0 - c) * et + c;
}
			
float3 Reflection(float4 proj_coord, float3 normal, float3 reflect_dir)
{
	float3 sea_reflect_color;
	proj_coord.xyz += normalize(float3(normal.xy * _BumpScale, normal.z));

	#ifdef _PLANARREFLECTION_ON
		sea_reflect_color = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(proj_coord));
		sea_reflect_color = GetSkyColor(reflect_dir, sea_reflect_color);
	#else
		sea_reflect_color = GetSkyColor(reflect_dir, _SkyColor);
	#endif

	return sea_reflect_color;
}

float2 AlignWithGrabTexel (float2 uv)
{
	return (floor(uv * _CameraDepthTexture_TexelSize.zw) + 0.5) * abs(_CameraDepthTexture_TexelSize.xy);
}

float3 RefractionColor(float3 base_color, float4 grab_pos, float4 scr_pos, float3 normal)
{
	float4 depth_uv = grab_pos;
	float2 uv_offset = normal.xy * _Distortion;

	depth_uv.xy = grab_pos.xy + uv_offset;
	float surf_depth = UNITY_Z_0_FAR_FROM_CLIPSPACE(scr_pos.z);
	float ref_fix = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float depth_diff = saturate(ref_fix - surf_depth);

	float2 uv = grab_pos.xy + uv_offset * depth_diff;
	float2 grab_uv = AlignWithGrabTexel(uv / grab_pos.w);
		
	float3 refraction_color = tex2D(_GrabTex, grab_uv);

	depth_uv.xy = uv;
	float ref_fix_2 = LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(depth_uv))));
	float diff = ref_fix_2 - surf_depth;

	float ref_depth = _RefractionDepth;
	float grad_1 = saturate(diff * (ref_depth + _SilhouetteDepth));
	float grad_2 = saturate(diff * ref_depth);
	float3 col = lerp(refraction_color, _SeaBaseColor, grad_1);
	
	return lerp(col, base_color, grad_2);
}

float3 OceanColor(float3 world_pos, float wave_height, float3 normal, float4 proj_coord){
	//lighting
	float3 lightDir = normalize(UnityWorldSpaceLightDir(world_pos));
	float3 viewDir = normalize(UnityWorldSpaceViewDir(world_pos));
	float3 halfDir = normalize(lightDir + viewDir);
	
	//fresnel
	float r = 0.02;
	float facing = saturate(1.0 - dot(normal, viewDir));
	float fresnel = r + (1.0 - r) * pow(facing, 5.0);
	float3 reflectDir = reflect(-viewDir, normal);
	
	float diff = saturate(dot(normal, lightDir)) * _LightColor0;
	//float spec = pow(max(0.0, dot(normal, halfDir)), _Shininess * 128.0) * _LightColor0;	//Blinn-Phong
	
	//https://www.gamedev.net/articles/programming/graphics/rendering-water-as-a-post-process-effect-r2642/
	float dotSpec = saturate(dot(reflectDir, lightDir) * 0.5 + 0.5);
	float spec = (1.0 - fresnel) * saturate(lightDir.y) * pow(dotSpec, 512.0) * (_Shininess * 1.8 + 0.2);
	spec += spec * 25.0 * saturate(_Shininess - 0.05) * _LightColor0;
	
	//reflection
	float3 sea_reflect_color = Reflection(proj_coord, normal, reflectDir);

	float3 sea_base_color = _SeaBaseColor * diff * _BaseColorStrength + lerp(_SeaBaseColor, _SeaShallowColor * _ShallowColorStrength, diff);
	float3 water_color = lerp(sea_base_color, sea_reflect_color, fresnel);
	float3 sea_color = water_color + _SeaShallowColor * (wave_height * 0.5 + 0.2) * _ColorHightOffset;

	//return sea_color + spec;
	return sea_color;
}

Noise.cginc

float2 rand2d(float2 st, int seed)
{
	float2 s = float2(dot(st, float2(127.1, 311.7)) + seed, dot(st, float2(269.5, 183.3)) + seed);
	return -1.0 + 2.0 * frac(sin(s) * 43758.5453123);
}

float noise2(float2 st, int seed)
{
	float2 p = floor(st);
	float2 f = frac(st);

	float w00 = dot(rand2d(p, seed), f);
	float w10 = dot(rand2d(p + float2(1.0, 0.0), seed), f - float2(1.0, 0.0));
	float w01 = dot(rand2d(p + float2(0.0, 1.0), seed), f - float2(0.0, 1.0));
	float w11 = dot(rand2d(p + float2(1.0, 1.0), seed), f - float2(1.0, 1.0));
	
	float2 u = f * f * (3.0 - 2.0 * f);

	return lerp(lerp(w00, w10, u.x), lerp(w01, w11, u.x), u.y);
}

float3 rand3d(float3 p, int seed)
{
	float3 s = float3(dot(p, float3(127.1, 311.7, 74.7)) + seed,
					  dot(p, float3(269.5, 183.3, 246.1)) + seed,
					  dot(p, float3(113.5, 271.9, 124.6)) + seed);
	return -1.0 + 2.0 * frac(sin(s) * 43758.5453123);
}

float noise3(float3 st, int seed)
{
	float3 p = floor(st);
	float3 f = frac(st);

	float w000 = dot(rand3d(p, seed), f);
	float w100 = dot(rand3d(p + float3(1, 0, 0), seed), f - float3(1, 0, 0));
	float w010 = dot(rand3d(p + float3(0, 1, 0), seed), f - float3(0, 1, 0));
	float w110 = dot(rand3d(p + float3(1, 1, 0), seed), f - float3(1, 1, 0));
	float w001 = dot(rand3d(p + float3(0, 0, 1), seed), f - float3(0, 0, 1));
	float w101 = dot(rand3d(p + float3(1, 0, 1), seed), f - float3(1, 0, 1));
	float w011 = dot(rand3d(p + float3(0, 1, 1), seed), f - float3(0, 1, 1));
	float w111 = dot(rand3d(p + float3(1, 1, 1), seed), f - float3(1, 1, 1));
	
	float3 u = f * f * (3.0 - 2.0 * f);

	float r1 = lerp(lerp(w000, w100, u.x), lerp(w010, w110, u.x), u.y);
	float r2 = lerp(lerp(w001, w101, u.x), lerp(w011, w111, u.x), u.y);

	return lerp(r1, r2, u.z);
}