diff --git a/Assets/BoidsSimulationOnGPU.meta b/Assets/BoidsSimulationOnGPU.meta new file mode 100644 index 0000000..6ecd390 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b95d633f81bd1114f97481999c5fbad6 +folderAsset: yes +timeCreated: 1507619339 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity b/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity new file mode 100644 index 0000000..2644e4b --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity @@ -0,0 +1,348 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 8 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 9 + m_Resolution: 2 + m_BakeResolution: 40 + m_TextureWidth: 1024 + m_TextureHeight: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFiltering: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousColorSigma: 1 + m_PVRFilteringAtrousNormalSigma: 1 + m_PVRFilteringAtrousPositionSigma: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &257653479 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 257653481} + - component: {fileID: 257653480} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &257653480 +Light: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 257653479} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_FalloffTable: + m_Table[0]: 0 + m_Table[1]: 0 + m_Table[2]: 0 + m_Table[3]: 0 + m_Table[4]: 0 + m_Table[5]: 0 + m_Table[6]: 0 + m_Table[7]: 0 + m_Table[8]: 0 + m_Table[9]: 0 + m_Table[10]: 0 + m_Table[11]: 0 + m_Table[12]: 0 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &257653481 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 257653479} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &583646679 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 583646681} + - component: {fileID: 583646680} + - component: {fileID: 583646683} + m_Layer: 0 + m_Name: GPUBoids + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &583646680 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 583646679} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4be9a8d5d645e4545886be6914d2e5e9, type: 3} + m_Name: + m_EditorClassIdentifier: + MaxObjectNum: 16384 + CohesionNeighborhoodRadius: 2 + AlignmentNeighborhoodRadius: 2 + SeparateNeighborhoodRadius: 1 + MaxSpeed: 5 + MaxSteerForce: 0.5 + CohesionWeight: 1 + AlignmentWeight: 1 + SeparateWeight: 3 + AvoidWallWeight: 10 + WallCenter: {x: 0, y: 0, z: 0} + WallSize: {x: 32, y: 32, z: 32} + BoidsCS: {fileID: 7200000, guid: 47567044d261e474db26e25410628c0a, type: 3} +--- !u!4 &583646681 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 583646679} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &583646683 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 583646679} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c1088025d887acd4189b6dc124e0ebc1, type: 3} + m_Name: + m_EditorClassIdentifier: + ObjectScale: {x: 0.1, y: 0.2, z: 0.5} + GPUBoidsScript: {fileID: 583646680} + InstanceMesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} + InstanceRenderMaterial: {fileID: 2100000, guid: 91dbb6b6149df404a986444779bcb3a3, + type: 2} +--- !u!1 &2028803004 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 2028803009} + - component: {fileID: 2028803008} + - component: {fileID: 2028803007} + - component: {fileID: 2028803006} + - component: {fileID: 2028803005} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &2028803005 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2028803004} + m_Enabled: 1 +--- !u!124 &2028803006 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2028803004} + m_Enabled: 1 +--- !u!92 &2028803007 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2028803004} + m_Enabled: 1 +--- !u!20 &2028803008 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2028803004} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 + m_StereoMirrorMode: 0 +--- !u!4 &2028803009 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2028803004} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity.meta b/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity.meta new file mode 100644 index 0000000..ebfe090 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/BoidsSimulationOnGPU.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 90cd09b78ebf02a41b70bb555bded046 +timeCreated: 1505730664 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/ComputeShaders.meta b/Assets/BoidsSimulationOnGPU/ComputeShaders.meta new file mode 100644 index 0000000..5341d58 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/ComputeShaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0032263264a507740a31df52d45c578c +folderAsset: yes +timeCreated: 1505730621 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute b/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute new file mode 100644 index 0000000..30491cb --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute @@ -0,0 +1,206 @@ +// カーネル関数を指定 +#pragma kernel ForceCS // 操舵力を計算 +#pragma kernel IntegrateCS // 速度, 位置を計算 + +// Boidデータの構造体 +struct BoidData +{ + float3 velocity; // 速度 + float3 position; // 位置 +}; + +// スレッドグループのスレッドのサイズ +#define SIMULATION_BLOCK_SIZE 256 + +// Boidデータのバッファ(読み取り用) +StructuredBuffer _BoidDataBufferRead; +// Boidデータのバッファ(読み取り, 書き込み用) +RWStructuredBuffer _BoidDataBufferWrite; +// Boidの操舵力のバッファ(読み取り用) +StructuredBuffer _BoidForceBufferRead; +// Boidの操舵力のバッファ(読み取り, 書き込み用) +RWStructuredBuffer _BoidForceBufferWrite; + +int _MaxBoidObjectNum; // Boidオブジェクト数 + +float _DeltaTime; // 前フレームから経過した時間 + +float _SeparateNeighborhoodRadius; // 分離を適用する他の個体との距離 +float _AlignmentNeighborhoodRadius; // 整列を適用する他の個体との距離 +float _CohesionNeighborhoodRadius; // 結合を適用する他の個体との距離 + +float _MaxSpeed; // 速度の最大値 +float _MaxSteerForce; // 操舵する力の最大値 + +float _SeparateWeight; // 分離適用時の重み +float _AlignmentWeight; // 整列適用時の重み +float _CohesionWeight; // 結合適用時の重み + +float4 _WallCenter; // 壁の中心座標 +float4 _WallSize; // 壁のサイズ +float _AvoidWallWeight; // 壁を避ける強さの重み + + +// ベクトルの大きさを制限する +float3 limit(float3 vec, float max) +{ + float length = sqrt(dot(vec, vec)); // 大きさ + return (length > max && length > 0) ? vec.xyz * (max / length) : vec.xyz; +} + +// 壁に当たった時に逆向きの力を返す +float3 avoidWall(float3 position) +{ + float3 wc = _WallCenter.xyz; + float3 ws = _WallSize.xyz; + float3 acc = float3(0, 0, 0); + // x + acc.x = (position.x < wc.x - ws.x * 0.5) ? acc.x + 1.0 : acc.x; + acc.x = (position.x > wc.x + ws.x * 0.5) ? acc.x - 1.0 : acc.x; + + // y + acc.y = (position.y < wc.y - ws.y * 0.5) ? acc.y + 1.0 : acc.y; + acc.y = (position.y > wc.y + ws.y * 0.5) ? acc.y - 1.0 : acc.y; + + // z + acc.z = (position.z < wc.z - ws.z * 0.5) ? acc.z + 1.0 : acc.z; + acc.z = (position.z > wc.z + ws.z * 0.5) ? acc.z - 1.0 : acc.z; + + return acc; +} + +// シェアードメモリ Boidデータ格納用 +groupshared BoidData boid_data[SIMULATION_BLOCK_SIZE]; + +// 操舵力の計算用カーネル関数 +[numthreads(SIMULATION_BLOCK_SIZE, 1, 1)] +void ForceCS +( + uint3 DTid : SV_DispatchThreadID, // スレッド全体で固有のID + uint3 Gid : SV_GroupID, // グループのID + uint3 GTid : SV_GroupThreadID, // グループ内のスレッドID + uint GI : SV_GroupIndex // SV_GroupThreadIDを一次元にしたもの 0-255 +) +{ + const unsigned int P_ID = DTid.x; // 自身のID + float3 P_position = _BoidDataBufferRead[P_ID].position; // 自身の位置 + float3 P_velocity = _BoidDataBufferRead[P_ID].velocity; // 自身の速度 + + float3 force = float3(0, 0, 0); // 操舵力を初期化 + + float3 sepPosSum = float3(0, 0, 0); // 分離計算用 位置加算変数 + int sepCount = 0; // 分離のために計算した他の個体の数のカウント用変数 + + float3 aliVelSum = float3(0, 0, 0); // 整列計算用 速度加算変数 + int aliCount = 0; // 整列のために計算した他の個体の数のカウント用変数 + + float3 cohPosSum = float3(0, 0, 0); // 結合計算用 位置加算変数 + int cohCount = 0; // 結合のために計算した他の個体の数のカウント用変数 + + // SIMULATION_BLOCK_SIZE(グループスレッド数)ごとの実行 (グループ数分実行) + [loop] + for (uint N_block_ID = 0; N_block_ID < (uint)_MaxBoidObjectNum; + N_block_ID += SIMULATION_BLOCK_SIZE) + { + // SIMULATION_BLOCK_SIZE分のBoidデータを、シェアードメモリに格納 + boid_data[GI] = _BoidDataBufferRead[N_block_ID + GI]; + + // すべてのグループ共有アクセスが完了し、 + // グループ内のすべてのスレッドがこの呼び出しに到達するまで、 + // グループ内のすべてのスレッドの実行をブロックする + GroupMemoryBarrierWithGroupSync(); + + // 他の個体との計算 + for (int N_tile_ID = 0; N_tile_ID < SIMULATION_BLOCK_SIZE; N_tile_ID++) + { + float3 N_position = boid_data[N_tile_ID].position; // 他の個体の位置 + float3 N_velocity = boid_data[N_tile_ID].velocity; // 他の個体の速度 + + float3 diff = P_position - N_position; // 自身と他の個体の位置の差 + float dist = sqrt(dot(diff, diff)); // 自身と他の個体の位置の距離 + + // --- 分離(Separation) --- + if (dist > 0.0 && dist <= _SeparateNeighborhoodRadius) + { + // 他の個体の位置から自身へ向かうベクトル + float3 repulse = normalize(P_position - N_position); + // 自身と他の個体の位置の距離で割る(距離が遠ければ影響を小さく) + repulse /= dist; + sepPosSum += repulse; // 加算 + sepCount++; // 個体数カウント + } + + // --- 整列(Alignment) --- + if (dist > 0.0 && dist <= _AlignmentNeighborhoodRadius) + { + aliVelSum += N_velocity; // 加算 + aliCount++; // 個体数カウント + } + + // --- 結合(Cohesion) --- + if (dist > 0.0 && dist <= _CohesionNeighborhoodRadius) + { + cohPosSum += N_position; // 加算 + cohCount++; // 個体数カウント + } + } + GroupMemoryBarrierWithGroupSync(); + } + + // 操舵力(分離) + float3 sepSteer = (float3)0.0; + if (sepCount > 0) + { + sepSteer = sepPosSum / (float)sepCount; // 平均を求める + sepSteer = normalize(sepSteer) * _MaxSpeed; // 最大速度に調整 + sepSteer = sepSteer - P_velocity; // 操舵力を計算 + sepSteer = limit(sepSteer, _MaxSteerForce); // 操舵力を制限 + } + + // 操舵力(整列) + float3 aliSteer = (float3)0.0; + if (aliCount > 0) + { + aliSteer = aliVelSum / (float)aliCount; // 近い個体の速度の平均を求める + aliSteer = normalize(aliSteer) * _MaxSpeed; // 最大速度に調整 + aliSteer = aliSteer - P_velocity; // 操舵力を計算 + aliSteer = limit(aliSteer, _MaxSteerForce); // 操舵力を制限 + } + // 操舵力(結合) + float3 cohSteer = (float3)0.0; + if (cohCount > 0) + { + cohPosSum = cohPosSum / (float)cohCount; // 近い個体の位置の平均を求める + cohSteer = cohPosSum - P_position; // 平均位置方向へのベクトルを求める + cohSteer = normalize(cohSteer) * _MaxSpeed; // 最大速度に調整 + cohSteer = cohSteer - P_velocity; // 操舵力を計算 + cohSteer = limit(cohSteer, _MaxSteerForce); // 操舵力を制限 + } + force += aliSteer * _AlignmentWeight; // 操舵力に整列する力を加える + force += cohSteer * _CohesionWeight; // 操舵力に結合する力を加える + force += sepSteer * _SeparateWeight; // 操舵力に分離する力を加える + + _BoidForceBufferWrite[P_ID] = force; // 書き込み +} + +// 速度, 位置計算用カーネル関数 +[numthreads(SIMULATION_BLOCK_SIZE, 1, 1)] +void IntegrateCS +( + uint3 DTid : SV_DispatchThreadID // スレッド全体で固有のID +) +{ + const unsigned int P_ID = DTid.x; // インデックスを取得 + + BoidData b = _BoidDataBufferWrite[P_ID]; // 現在のBoidデータを読み込む + float3 force = _BoidForceBufferRead[P_ID]; // 操舵力を読み込む + + // 壁に近づいたら反発する力を与える + force += avoidWall(b.position) * _AvoidWallWeight; + + b.velocity += force * _DeltaTime; // 操舵力を速度に適用 + b.velocity = limit(b.velocity, _MaxSpeed); // 速度を制限 + b.position += b.velocity * _DeltaTime; // 位置を更新 + + _BoidDataBufferWrite[P_ID] = b; // 計算結果を書き込む +} \ No newline at end of file diff --git a/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute.meta b/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute.meta new file mode 100644 index 0000000..2f29996 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/ComputeShaders/Boids.compute.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 47567044d261e474db26e25410628c0a +timeCreated: 1505731768 +licenseType: Pro +ComputeShaderImporter: + currentAPIMask: 4 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Materials.meta b/Assets/BoidsSimulationOnGPU/Materials.meta new file mode 100644 index 0000000..8a15fbf --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Materials.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f5ff61eb07d00644fae8d25fb3168c98 +folderAsset: yes +timeCreated: 1505731715 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat b/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat new file mode 100644 index 0000000..d27be51 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: BoidsRender + m_Shader: {fileID: 4800000, guid: f4853a7a0065e734dacad53805b3b7e3, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 1 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat.meta b/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat.meta new file mode 100644 index 0000000..90e8263 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Materials/BoidsRender.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 91dbb6b6149df404a986444779bcb3a3 +timeCreated: 1505737684 +licenseType: Pro +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Scripts.meta b/Assets/BoidsSimulationOnGPU/Scripts.meta new file mode 100644 index 0000000..b517641 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d357b58f29a0c2c4cbec99353e5dcdd1 +folderAsset: yes +timeCreated: 1505730621 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs b/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs new file mode 100644 index 0000000..e9c7966 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs @@ -0,0 +1,99 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace BoidsSimulationOnGPU +{ + // 同GameObjectに、GPUBoidsコンポーネントがアタッチされていること保証 + [RequireComponent(typeof(GPUBoids))] + public class BoidsRender : MonoBehaviour + { + #region Paremeters + // 描画するBoidsオブジェクトのスケール + public Vector3 ObjectScale = new Vector3(0.1f, 0.2f, 0.5f); + #endregion + + #region Script References + // GPUBoidsスクリプトの参照 + public GPUBoids GPUBoidsScript; + #endregion + + #region Built-in Resources + // 描画するメッシュの参照 + public Mesh InstanceMesh; + // 描画のためのマテリアルの参照 + public Material InstanceRenderMaterial; + #endregion + + #region Private Variables + // GPUインスタンシングのための引数(ComputeBufferへの転送用) + // インスタンスあたりのインデックス数, インスタンス数, + // 開始インデックス位置, ベース頂点位置, インスタンスの開始位置 + uint[] args = new uint[5] { 0, 0, 0, 0, 0 }; + // GPUインスタンシングのための引数バッファ + ComputeBuffer argsBuffer; + #endregion + + #region MonoBehaviour Functions + void Start() + { + // 引数バッファを初期化 + argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), + ComputeBufferType.IndirectArguments); + } + + void Update() + { + // メッシュをインスタンシング + RenderInstancedMesh(); + } + + void OnDisable() + { + // 引数バッファを解放 + if (argsBuffer != null) + argsBuffer.Release(); + argsBuffer = null; + } + #endregion + + #region Private Functions + void RenderInstancedMesh() + { + // 描画用マテリアルがNull, または, GPUBoidsスクリプトがNull, + // またはGPUインスタンシングがサポートされていなければ, 処理をしない + if (InstanceRenderMaterial == null || GPUBoidsScript == null || + !SystemInfo.supportsInstancing) + return; + + // 指定したメッシュのインデックス数を取得 + uint numIndices = (InstanceMesh != null) ? + (uint)InstanceMesh.GetIndexCount(0) : 0; + args[0] = numIndices; // メッシュのインデックス数をセット + args[1] = (uint)GPUBoidsScript.GetMaxObjectNum(); // インスタンス数をセット + argsBuffer.SetData(args); // バッファにセット + + // Boidデータを格納したバッファをマテリアルにセット + InstanceRenderMaterial.SetBuffer("_BoidDataBuffer", + GPUBoidsScript.GetBoidDataBuffer()); + // Boidオブジェクトスケールをセット + InstanceRenderMaterial.SetVector("_ObjectScale", ObjectScale); + // 境界領域を定義 + var bounds = new Bounds + ( + GPUBoidsScript.GetSimulationAreaCenter(), // 中心 + GPUBoidsScript.GetSimulationAreaSize() // サイズ + ); + // メッシュをGPUインスタンシングして描画 + Graphics.DrawMeshInstancedIndirect + ( + InstanceMesh, // インスタンシングするメッシュ + 0, // submeshのインデックス + InstanceRenderMaterial, // 描画を行うマテリアル + bounds, // 境界領域 + argsBuffer // GPUインスタンシングのための引数のバッファ + ); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs.meta b/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs.meta new file mode 100644 index 0000000..7b92eb6 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Scripts/BoidsRender.cs.meta @@ -0,0 +1,16 @@ +fileFormatVersion: 2 +guid: c1088025d887acd4189b6dc124e0ebc1 +timeCreated: 1507467374 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: + - GPUBoidsScript: {instanceID: 0} + - InstanceMesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} + - InstanceRenderMaterial: {fileID: 2100000, guid: 91dbb6b6149df404a986444779bcb3a3, + type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs b/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs new file mode 100644 index 0000000..7d46e96 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs @@ -0,0 +1,195 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace BoidsSimulationOnGPU +{ + public class GPUBoids : MonoBehaviour + { + // Boidデータの構造体 + [System.Serializable] + struct BoidData + { + public Vector3 Velocity; // 速度 + public Vector3 Position; // 位置 + } + // スレッドグループのスレッドのサイズ + const int SIMULATION_BLOCK_SIZE = 256; + + #region Boids Parameters + // 最大オブジェクト数 + [Range(256, 32768)] + public int MaxObjectNum = 16384; + + // 結合を適用する他の個体との半径 + public float CohesionNeighborhoodRadius = 2.0f; + // 整列を適用する他の個体との半径 + public float AlignmentNeighborhoodRadius = 2.0f; + // 分離を適用する他の個体との半径 + public float SeparateNeighborhoodRadius = 1.0f; + + // 速度の最大値 + public float MaxSpeed = 5.0f; + // 操舵力の最大値 + public float MaxSteerForce = 0.5f; + + // 結合する力の重み + public float CohesionWeight = 1.0f; + // 整列する力の重み + public float AlignmentWeight = 1.0f; + // 分離する力の重み + public float SeparateWeight = 3.0f; + + // 壁を避ける力の重み + public float AvoidWallWeight = 10.0f; + + // 壁の中心座標 + public Vector3 WallCenter = Vector3.zero; + // 壁のサイズ + public Vector3 WallSize = new Vector3(32.0f, 32.0f, 32.0f); + #endregion + + #region Built-in Resources + // Boidsシミュレーションを行うComputeShaderの参照 + public ComputeShader BoidsCS; + #endregion + + #region Private Resources + // Boidの操舵力(Force)を格納したバッファ + ComputeBuffer _boidForceBuffer; + // Boidの基本データ(速度, 位置, Transformなど)を格納したバッファ + ComputeBuffer _boidDataBuffer; + #endregion + + #region Accessors + // Boidの基本データを格納したバッファを取得 + public ComputeBuffer GetBoidDataBuffer() + { + return this._boidDataBuffer != null ? this._boidDataBuffer : null; + } + + // オブジェクト数を取得 + public int GetMaxObjectNum() + { + return this.MaxObjectNum; + } + + // シミュレーション領域の中心座標を返す + public Vector3 GetSimulationAreaCenter() + { + return this.WallCenter; + } + + // シミュレーション領域のボックスのサイズを返す + public Vector3 GetSimulationAreaSize() + { + return this.WallSize; + } + #endregion + + #region MonoBehaviour Functions + void Start() + { + // バッファを初期化 + InitBuffer(); + } + + void Update() + { + // シミュレーション + Simulation(); + } + + void OnDestroy() + { + // バッファを破棄 + ReleaseBuffer(); + } + + void OnDrawGizmos() + { + // デバッグとしてシミュレーション領域をワイヤーフレームで描画 + Gizmos.color = Color.cyan; + Gizmos.DrawWireCube(WallCenter, WallSize); + } + #endregion + + #region Private Functions + // バッファを初期化 + void InitBuffer() + { + // バッファを初期化 + _boidDataBuffer = new ComputeBuffer(MaxObjectNum, + Marshal.SizeOf(typeof(BoidData))); + _boidForceBuffer = new ComputeBuffer(MaxObjectNum, + Marshal.SizeOf(typeof(Vector3))); + + // Boidデータ, Forceバッファを初期化 + var forceArr = new Vector3[MaxObjectNum]; + var boidDataArr = new BoidData[MaxObjectNum]; + for (var i = 0; i < MaxObjectNum; i++) + { + forceArr[i] = Vector3.zero; + boidDataArr[i].Position = Random.insideUnitSphere * 1.0f; + boidDataArr[i].Velocity = Random.insideUnitSphere * 0.1f; + } + _boidForceBuffer.SetData(forceArr); + _boidDataBuffer.SetData(boidDataArr); + forceArr = null; + boidDataArr = null; + } + + // シミュレーション + void Simulation() + { + ComputeShader cs = BoidsCS; + int id = -1; + + // スレッドグループの数を求める + int threadGroupSize = Mathf.CeilToInt(MaxObjectNum / SIMULATION_BLOCK_SIZE); + + // 操舵力を計算 + id = cs.FindKernel("ForceCS"); // カーネルIDを取得 + cs.SetInt("_MaxBoidObjectNum", MaxObjectNum); + cs.SetFloat("_CohesionNeighborhoodRadius", CohesionNeighborhoodRadius); + cs.SetFloat("_AlignmentNeighborhoodRadius", AlignmentNeighborhoodRadius); + cs.SetFloat("_SeparateNeighborhoodRadius", SeparateNeighborhoodRadius); + cs.SetFloat("_MaxSpeed", MaxSpeed); + cs.SetFloat("_MaxSteerForce", MaxSteerForce); + cs.SetFloat("_SeparateWeight", SeparateWeight); + cs.SetFloat("_CohesionWeight", CohesionWeight); + cs.SetFloat("_AlignmentWeight", AlignmentWeight); + cs.SetVector("_WallCenter", WallCenter); + cs.SetVector("_WallSize", WallSize); + cs.SetFloat("_AvoidWallWeight", AvoidWallWeight); + cs.SetBuffer(id, "_BoidDataBufferRead", _boidDataBuffer); + cs.SetBuffer(id, "_BoidForceBufferWrite", _boidForceBuffer); + cs.Dispatch(id, threadGroupSize, 1, 1); // ComputeShaderを実行 + + // 操舵力から、速度と位置を計算 + id = cs.FindKernel("IntegrateCS"); // カーネルIDを取得 + cs.SetFloat("_DeltaTime", Time.deltaTime); + cs.SetBuffer(id, "_BoidForceBufferRead", _boidForceBuffer); + cs.SetBuffer(id, "_BoidDataBufferWrite", _boidDataBuffer); + cs.Dispatch(id, threadGroupSize, 1, 1); // ComputeShaderを実行 + } + + // バッファを解放 + void ReleaseBuffer() + { + if (_boidDataBuffer != null) + { + _boidDataBuffer.Release(); + _boidDataBuffer = null; + } + + if (_boidForceBuffer != null) + { + _boidForceBuffer.Release(); + _boidForceBuffer = null; + } + } + #endregion + } // class +} // namespace \ No newline at end of file diff --git a/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs.meta b/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs.meta new file mode 100644 index 0000000..faae5bb --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Scripts/GPUBoids.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 4be9a8d5d645e4545886be6914d2e5e9 +timeCreated: 1507467386 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: + - BoidsCS: {fileID: 7200000, guid: 47567044d261e474db26e25410628c0a, type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Shaders.meta b/Assets/BoidsSimulationOnGPU/Shaders.meta new file mode 100644 index 0000000..8c3c8ba --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3cf58ec6d88ff7d47aa5d3cb91553ab7 +folderAsset: yes +timeCreated: 1505730621 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader b/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader new file mode 100644 index 0000000..10a4052 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader @@ -0,0 +1,109 @@ +Shader "Hidden/BoidsSimulationOnGPU/BoidsRender" +{ + Properties + { + _Color ("Color", Color) = (1,1,1,1) + _MainTex ("Albedo (RGB)", 2D) = "white" {} + _Glossiness ("Smoothness", Range(0,1)) = 0.5 + _Metallic ("Metallic", Range(0,1)) = 0.0 + } + SubShader + { + Tags { "RenderType"="Opaque" } + LOD 200 + + CGPROGRAM + #pragma surface surf Standard vertex:vert addshadow + #pragma instancing_options procedural:setup + + struct Input + { + float2 uv_MainTex; + }; + // Boidの構造体 + struct BoidData + { + float3 velocity; // 速度 + float3 position; // 位置 + }; + + #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED + // Boidデータの構造体バッファ + StructuredBuffer _BoidDataBuffer; + #endif + + sampler2D _MainTex; // テクスチャ + + half _Glossiness; // 光沢 + half _Metallic; // 金属特性 + fixed4 _Color; // カラー + + float3 _ObjectScale; // Boidオブジェクトのスケール + + // オイラー角(ラジアン)を回転行列に変換 + float4x4 eulerAnglesToRotationMatrix(float3 angles) + { + float ch = cos(angles.y); float sh = sin(angles.y); // heading + float ca = cos(angles.z); float sa = sin(angles.z); // attitude + float cb = cos(angles.x); float sb = sin(angles.x); // bank + + // Ry-Rx-Rz (Yaw Pitch Roll) + return float4x4( + ch * ca + sh * sb * sa, -ch * sa + sh * sb * ca, sh * cb, 0, + cb * sa, cb * ca, -sb, 0, + -sh * ca + ch * sb * sa, sh * sa + ch * sb * ca, ch * cb, 0, + 0, 0, 0, 1 + ); + } + + // 頂点シェーダ + void vert(inout appdata_full v) + { + #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED + + // インスタンスIDからBoidのデータを取得 + BoidData boidData = _BoidDataBuffer[unity_InstanceID]; + + float3 pos = boidData.position.xyz; // Boidの位置を取得 + float3 scl = _ObjectScale; // Boidのスケールを取得 + + // オブジェクト座標からワールド座標に変換する行列を定義 + float4x4 object2world = (float4x4)0; + // スケール値を代入 + object2world._11_22_33_44 = float4(scl.xyz, 1.0); + // 速度からY軸についての回転を算出 + float rotY = + atan2(-boidData.velocity.z, boidData.velocity.x) + UNITY_PI * 0.5; + // 速度からX軸についての回転を算出 + float rotX = + -asin(boidData.velocity.y / (length(boidData.velocity.xyz) + 1e-8)); + // オイラー角(ラジアン)から回転行列を求める + float4x4 rotMatrix = eulerAnglesToRotationMatrix(float3(rotX, rotY, 0)); + // 行列に回転を適用 + object2world = mul(rotMatrix, object2world); + // 行列に位置(平行移動)を適用 + object2world._14_24_34 += pos.xyz; + + // 頂点を座標変換 + v.vertex = mul(object2world, v.vertex); + // 法線を座標変換 + v.normal = normalize(mul(object2world, v.normal)); + #endif + } + + void setup() + { + } + + // サーフェスシェーダ + void surf (Input IN, inout SurfaceOutputStandard o) + { + fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; + o.Albedo = c.rgb; + o.Metallic = _Metallic; + o.Smoothness = _Glossiness; + } + ENDCG + } + FallBack "Diffuse" +} \ No newline at end of file diff --git a/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader.meta b/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader.meta new file mode 100644 index 0000000..102c0d8 --- /dev/null +++ b/Assets/BoidsSimulationOnGPU/Shaders/BoidsRender.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f4853a7a0065e734dacad53805b3b7e3 +timeCreated: 1505738193 +licenseType: Pro +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids.meta b/Assets/StableFluids.meta new file mode 100644 index 0000000..00bbcda --- /dev/null +++ b/Assets/StableFluids.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f7ee25e7b2459ae4ca5562a70b4ab48b +folderAsset: yes +timeCreated: 1507625738 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/ComputeShaders.meta b/Assets/StableFluids/ComputeShaders.meta new file mode 100644 index 0000000..a4501f6 --- /dev/null +++ b/Assets/StableFluids/ComputeShaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 36b564faf2cb9574790905e049663802 +folderAsset: yes +timeCreated: 1507625820 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/ComputeShaders/Solver2D.compute b/Assets/StableFluids/ComputeShaders/Solver2D.compute new file mode 100644 index 0000000..46a55cf --- /dev/null +++ b/Assets/StableFluids/ComputeShaders/Solver2D.compute @@ -0,0 +1,377 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel AddSourceDensity +#pragma kernel DiffuseDensity +#pragma kernel AdvectDensity +#pragma kernel AdvectDensityFromExt +#pragma kernel SwapDensity + +#pragma kernel AddSourceVelocity +#pragma kernel DiffuseVelocity +#pragma kernel AdvectVelocity +#pragma kernel SwapVelocity +#pragma kernel ProjectStep1 +#pragma kernel ProjectStep2 +#pragma kernel ProjectStep3 + +#pragma kernel Draw + +#define THREAD_X 32 +#define THREAD_Y 32 +#define THREAD_Z 1 +#define GS_ITERATE 2 //Gauss-Seidel法の反復回数. パフォーマンスに直結します.素早い拡散が必要なければ低い値で. + +float diff; +float visc; +float dt; +float velocityCoef; +float densityCoef; + +RWTexture2D solver; //xy = velocity, z = density +RWTexture2D density; //density field +RWTexture2D velocity; //velocity field +RWTexture2D prev; //xy = prev velocity field, z = prev density field. when project step x = p, y = div +Texture2D source; //xy = velocity source, z = density source + +void SetBoundaryDensity(uint2 id, uint w, uint h) +{ + density[id] = (id.x == 0) ? density[id + uint2(1,0)] : density[id]; + density[id] = (id.x == w-1) ? density[uint2(w-2, id.y)] : density[id]; + density[id] = (id.y == 0) ? density[id + uint2(0,1)] : density[id]; + density[id] = (id.y == h-1) ? density[uint2(id.x, h-2)] : density[id]; + + density[id] = (id.x == 0 && id.y == 0) ? 0.5 * (density[uint2(1,0)] + density[uint2(0,1)]) : density[id]; + density[id] = (id.x == 0 && id.y == h-1) ? 0.5 * (density[uint2(1,h-1)] + density[uint2(0,h-2)]) : density[id]; + density[id] = (id.x == w-1 && id.y == 0) ? 0.5 * (density[uint2(w-2,0)] + density[uint2(w-1,1)]) : density[id]; + density[id] = (id.x == w-1 && id.y == h-1) ? 0.5 * (density[uint2(w-2,h-1)] + density[uint2(w-1,h-2)]) : density[id]; +} + +void SetBoundaryVelocity(uint2 id, uint w, uint h) +{ + velocity[id] = (id.x == 0) ? float2(-velocity[id + uint2(1,0)].x, velocity[id].y) : velocity[id]; + velocity[id] = (id.x == w-1) ? float2(-velocity[uint2(w-2, id.y)].x, velocity[id].y) : velocity[id]; + velocity[id] = (id.y == 0) ? float2(velocity[id].x, -velocity[id + uint2(0,1)].y) : velocity[id]; + velocity[id] = (id.y == h-1) ? float2(velocity[id].x, -velocity[uint2(id.x, h-2)].y) : velocity[id]; + + velocity[id] = (id.x == 0 && id.y == 0) ? 0.5 * (velocity[uint2(1,0)] + velocity[uint2(0,1)]) : velocity[id]; + velocity[id] = (id.x == 0 && id.y == h-1) ? 0.5 * (velocity[uint2(1,h-1)] + velocity[uint2(0,h-2)]) : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == 0) ? 0.5 * (velocity[uint2(w-2,0)] + velocity[uint2(w-1,1)]) : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == h-1) ? 0.5 * (velocity[uint2(w-2,h-1)] + velocity[uint2(w-1,h-2)]) : velocity[id]; +} + +void SetBoundaryDivergence(uint2 id, uint w, uint h) +{ + prev[id] = (id.x == 0) ? float3(prev[id].x, prev[id + uint2(1,0)].y, prev[id].z) : prev[id]; + prev[id] = (id.x == w-1) ? float3(prev[id].x, prev[uint2(w-2, id.y)].y, prev[id].z) : prev[id]; + prev[id] = (id.y == 0) ? float3(prev[id].x, prev[id + uint2(0,1)].y, prev[id].z) : prev[id]; + prev[id] = (id.y == h-1) ? float3(prev[id].x, prev[uint2(id.x, h-2)].y, prev[id].z) : prev[id]; + + prev[id] = (id.x == 0 && id.y == 0) ? float3(prev[id].x, 0.5 * (prev[uint2(1,0)].y + prev[uint2(0,1)].y), prev[id].z) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1) ? float3(prev[id].x, 0.5 * (prev[uint2(1,h-2)].y + prev[uint2(0,h-2)].y), prev[id].z) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0) ? float3(prev[id].x, 0.5 * (prev[uint2(w-2,0)].y + prev[uint2(w-1,1)].y), prev[id].z) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1) ? float3(prev[id].x, 0.5 * (prev[uint2(w-2,h-1)].y + prev[uint2(w-1,h-2)].y), prev[id].z) : prev[id]; +} + +void SetBoundaryDivPositive(uint2 id, uint w, uint h) +{ + prev[id] = (id.x == 0) ? float3(prev[id + uint2(1,0)].x, prev[id].yz) : prev[id]; + prev[id] = (id.x == w-1) ? float3(prev[uint2(w-2, id.y)].x, prev[id].yz) : prev[id]; + prev[id] = (id.y == 0) ? float3(prev[id + uint2(0,1)].x, prev[id].yz) : prev[id]; + prev[id] = (id.y == h-1) ? float3(prev[uint2(id.x, h-2)].x, prev[id].yz) : prev[id]; + + prev[id] = (id.x == 0 && id.y == 0) ? float3(0.5 * (prev[uint2(1,0)].x + prev[uint2(0,1)].x), prev[id].yz) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1) ? float3(0.5 * (prev[uint2(1,h-1)].x + prev[uint2(0,h-2)].x), prev[id].yz) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0) ? float3(0.5 * (prev[uint2(w-2,0)].x + prev[uint2(w-1,1)].x), prev[id].yz) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1) ? float3(0.5 * (prev[uint2(w-2,h-1)].x + prev[uint2(w-1,h-2)].x), prev[id].yz) : prev[id]; +} + +//密度場外力項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AddSourceDensity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + density[id] += source[id].z * densityCoef * dt; + prev[id] = float3(prev[id].xy, source[id].z * densityCoef * dt); + } +} + +//速度場外力項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AddSourceVelocity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + velocity[id] += source[id].xy * velocityCoef * dt; + prev[id] = float3(source[id].xy * velocityCoef * dt, prev[id].z); + } +} + +//密度場拡散項.diff∇・∇densによる勾配・発散をGauss-Seidel法にて計算。diffは拡散係数. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void DiffuseDensity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float a = dt * diff * w * h; + + [unroll] + for (int k = 0; k < GS_ITERATE; k++) { + density[id] = (prev[id].z + a * (density[int2(id.x - 1, id.y)] + density[int2(id.x + 1, id.y)] + density[int2(id.x, id.y - 1)] + density[int2(id.x, id.y + 1)])) / (1 + 4 * a); + SetBoundaryDensity(id, w, h); + } + } +} + +//速度場拡散(粘性)項.visc∇・∇velocityによる勾配・発散をGauss-Seidel法にて計算。viscはkinematic visocity(動粘性摩擦係数). +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void DiffuseVelocity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float a = dt * visc * w * h; + + [unroll] + for (int k = 0; k < GS_ITERATE; k++) { + velocity[id] = (prev[id].xy + a * (velocity[int2(id.x - 1, id.y)] + velocity[int2(id.x + 1, id.y)] + velocity[int2(id.x, id.y - 1)] + velocity[int2(id.x, id.y + 1)])) / (1 + 4 * a); + SetBoundaryVelocity(id, w, h); + } + } +} + +//密度場移流項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectDensity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + int ddx0, ddx1, ddy0, ddy1; + float x, y, s0, t0, s1, t1, dfdt; + + dfdt = dt * (w + h) * 0.5; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * velocity[id].x; + y = (float)id.y - dfdt * velocity[id].y; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w + 0.5); + clamp(y, 0.5, h + 0.5); + //バックトレースポイントの近傍セル割り出し. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + density[id] = s0 * (t0 * prev[int2(ddx0, ddy0)].z + t1 * prev[int2(ddx0, ddy1)].z) + + s1 * (t0 * prev[int2(ddx1, ddy0)].z + t1 * prev[int2(ddx1, ddy1)].z); + SetBoundaryDensity(id, w, h); + } +} + +//密度場移流項(このカーネルはvelocity stepを用いず、速度場を外部からsource texにフェッチした物を参照する事によって軽量化を狙う為用意). +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectDensityFromExt(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + int ddx0, ddx1, ddy0, ddy1; + float x, y, s0, t0, s1, t1, dfdt; + + dfdt = dt * (w + h) * 0.5; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * source[id].x; + y = (float)id.y - dfdt * source[id].y; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w + 0.5); + clamp(y, 0.5, h + 0.5); + //バックトレースポイントの近傍セル割り出し. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + density[id] = s0 * (t0 * prev[int2(ddx0, ddy0)].z + t1 * prev[int2(ddx0, ddy1)].z) + + s1 * (t0 * prev[int2(ddx1, ddy0)].z + t1 * prev[int2(ddx1, ddy1)].z); + SetBoundaryDensity(id, w, h); + } +} + +//速度場移流項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectVelocity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + int ddx0, ddx1, ddy0, ddy1; + float x, y, s0, t0, s1, t1, dfdt; + + dfdt = dt * (w + h) * 0.5; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * prev[id].x; + y = (float)id.y - dfdt * prev[id].y; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w + 0.5); + clamp(y, 0.5, h + 0.5); + //xyのそれぞれ近似の偏微分セルを求める. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + velocity[id] = s0 * (t0 * prev[int2(ddx0, ddy0)].xy + t1 * prev[int2(ddx0, ddy1)].xy) + + s1 * (t0 * prev[int2(ddx1, ddy0)].xy + t1 * prev[int2(ddx1, ddy1)].xy); + SetBoundaryVelocity(id, w, h); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void SwapDensity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + density.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float temp = density[id]; + //density[id] = prev[id].z; + prev[id] = float3(prev[id].xy, temp); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void SwapVelocity(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float2 temp = float2(velocity[id].x, velocity[id].y); + //velocity[id] = prev[id].xy; + prev[id] = float3(temp, prev[id].z); + } +} + +//非圧縮性流体の為、速度divergenceの流出入を∇・u = 0にする外圧pを求め、速度場に投影. +//質量保存Step1. +//step1では、速度場から発散の算出. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep1(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float2 uvd; + uvd = float2(1.0 / w, 1.0 / h); + + prev[id] = float3(0.0, + -0.5 * (uvd.x * (velocity[int2(id.x + 1, id.y)].x - velocity[int2(id.x - 1, id.y)].x)) + + (uvd.y * (velocity[int2(id.x, id.y + 1)].y - velocity[int2(id.x, id.y - 1)].y)), + prev[id].z); + + SetBoundaryDivergence(id, w, h); + SetBoundaryDivPositive(id, w, h); + } +} + +//質量保存Step2. +//step2では、step1で求めた発散からPoisson方程式をガウス・ザイデル法で解く. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep2(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + for (int k = 0; k < GS_ITERATE; k++) + { + prev[id] = float3( + (prev[id].y + prev[uint2(id.x - 1, id.y)].x + prev[uint2(id.x + 1, id.y)].x + + prev[uint2(id.x, id.y - 1)].x + prev[uint2(id.x, id.y + 1)].x) / 4, + prev[id].yz); + SetBoundaryDivPositive(id, w, h); + } + } +} + +//質量保存Step3. +//step3で、∇・u = 0にする. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep3(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + + velocity.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + float velX, velY; + float2 uvd; + uvd = float2(1.0 / w, 1.0 / h); + + velX = velocity[id].x; + velY = velocity[id].y; + + velX -= 0.5 * (prev[uint2(id.x + 1, id.y)].x - prev[uint2(id.x - 1, id.y)].x) / uvd.x; + velY -= 0.5 * (prev[uint2(id.x, id.y + 1)].x - prev[uint2(id.x, id.y - 1)].x) / uvd.y; + + velocity[id] = float2(velX, velY); + SetBoundaryVelocity(id, w, h); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void Draw(uint2 id : SV_DispatchThreadID) +{ + uint w, h; + solver.GetDimensions(w, h); + + if (id.x < w && id.y < h) + { + solver[id] = float4(velocity[id].xy, density[id], 1); + } +} \ No newline at end of file diff --git a/Assets/StableFluids/ComputeShaders/Solver2D.compute.meta b/Assets/StableFluids/ComputeShaders/Solver2D.compute.meta new file mode 100644 index 0000000..c6ed869 --- /dev/null +++ b/Assets/StableFluids/ComputeShaders/Solver2D.compute.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8f93e96f4f3aed44592d1d829b633854 +timeCreated: 1493362868 +licenseType: Pro +ComputeShaderImporter: + currentAPIMask: 4 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/ComputeShaders/Solver3D.compute b/Assets/StableFluids/ComputeShaders/Solver3D.compute new file mode 100644 index 0000000..72dc5e7 --- /dev/null +++ b/Assets/StableFluids/ComputeShaders/Solver3D.compute @@ -0,0 +1,452 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel AddSourceDensity +#pragma kernel DiffuseDensity +#pragma kernel AdvectDensity +#pragma kernel AdvectDensityFromExt +#pragma kernel SwapDensity + +#pragma kernel AddSourceVelocity +#pragma kernel DiffuseVelocity +#pragma kernel AdvectVelocity +#pragma kernel SwapVelocity +#pragma kernel ProjectStep1 +#pragma kernel ProjectStep2 +#pragma kernel ProjectStep3 + +#pragma kernel Draw + +#define THREAD_X 8 +#define THREAD_Y 8 +#define THREAD_Z 4 +#define GS_ITERATE 2 //Gauss-Seidel法の反復回数. パフォーマンスに直結します.素早い拡散が必要なければ低い値で. + +float diff; +float visc; +float dt; +float velocityCoef; +float densityCoef; + +RWTexture3D solver; //xy = velocity, z = density +RWTexture3D density; //density field +RWTexture3D velocity; //velocity field +RWTexture3D prev; //xyz = prev velocity, w = prev density. when project step x = p, y = div +Texture3D source; //xyz = velocity source, w = density source + +SamplerState _PointClamp; + +void SetBoundaryDensity(uint3 id, uint w, uint h, uint d) +{ + density[id] = (id.x == 0) ? density[id + uint3(1,0,0)] : density[id]; + density[id] = (id.x == w-1) ? density[uint3(w-2, id.yz)] : density[id]; + density[id] = (id.y == 0) ? density[id + uint3(0,1,0)] : density[id]; + density[id] = (id.y == h-1) ? density[uint3(id.x, h-2, id.z)] : density[id]; + density[id] = (id.z == 0) ? density[id + uint3(0,0,1)] : density[id]; + density[id] = (id.z == d-1) ? density[uint3(id.xy, d-2)] : density[id]; + + density[id] = (id.x == 0 && id.y == 0 && id.z != 0 && id.z != d-1) ? 0.5 * (density[uint3(1,0,id.z)] + density[uint3(0,1,id.z)]) : density[id]; + density[id] = (id.x == 0 && id.y == 0 && id.z == 0) ? (density[uint3(1,0,0)] + density[uint3(0,1,0)] + density[uint3(0,0,1)]) / 3 : density[id]; + density[id] = (id.x == 0 && id.y == 0 && id.z == d-1) ? (density[uint3(1,0,d-1)] + density[uint3(0,1,d-1)] + density[uint3(0,0,d-2)]) / 3 : density[id]; + density[id] = (id.x == 0 && id.y == h-1 && id.z != 0 && id.z != d-1) ? 0.5 * (density[uint3(1,h-1,id.z)] + density[uint3(0,h-2,id.z)]) : density[id]; + density[id] = (id.x == 0 && id.y == h-1 && id.z == 0) ? (density[uint3(1,h-1,0)] + density[uint3(0,h-2,0)] + density[uint3(0,h-1,1)]) / 3 : density[id]; + density[id] = (id.x == 0 && id.y == h-1 && id.z == d-1) ? (density[uint3(1,h-1,d-1)] + density[uint3(0,h-2,d-1)] + density[uint3(0,h-1,d-2)]) / 3 : density[id]; + density[id] = (id.x == w-1 && id.y == 0 && id.z != 0 && id.z != d-1) ? 0.5 * (density[uint3(w-2,0,id.z)] + density[uint3(w-1,1,id.z)]) : density[id]; + density[id] = (id.x == w-1 && id.y == 0 && id.z == 0) ? (density[uint3(w-2,0,0)] + density[uint3(w-1,1,0)] + density[uint3(w-1,0,1)]) / 3 : density[id]; + density[id] = (id.x == w-1 && id.y == 0 && id.z == d-1) ? (density[uint3(w-2,0,d-1)] + density[uint3(w-1,1,d-1)] + density[uint3(w-1,0,d-2)]) / 3 : density[id]; + density[id] = (id.x == w-1 && id.y == h-1 && id.z != 0 && id.z != d-1) ? 0.5 * (density[uint3(w-2,h-1,id.z)] + density[uint3(w-1,h-2,id.z)]) : density[id]; + density[id] = (id.x == w-1 && id.y == h-1 && id.z == 0) ? (density[uint3(w-2,h-1,0)] + density[uint3(w-1,h-2,0)] + density[uint3(w-1,h-1,1)]) / 3 : density[id]; + density[id] = (id.x == w-1 && id.y == h-1 && id.z != d-1) ? (density[uint3(w-2,h-1,d-1)] + density[uint3(w-1,h-2,d-1)] + density[uint3(w-1,h-1,d-2)]) / 3 : density[id]; +} + +void SetBoundaryVelocity(uint3 id, uint w, uint h, uint d) +{ + velocity[id] = (id.x == 0) ? float3(-velocity[id + uint3(1,0,0)].x, velocity[id].yz) : velocity[id]; + velocity[id] = (id.x == w-1) ? float3(-velocity[uint3(w-2,id.yz)].x, velocity[id].yz) : velocity[id]; + velocity[id] = (id.y == 0) ? float3(velocity[id].x, -velocity[id + uint3(0,1,0)].y, velocity[id].z) : velocity[id]; + velocity[id] = (id.y == h-1) ? float3(velocity[id].x, -velocity[uint3(id.x,h-2,id.z)].y, velocity[id].z) : velocity[id]; + velocity[id] = (id.z == 0) ? float3(velocity[id].xy, -velocity[id + uint3(0,0,1)].z) : velocity[id]; + velocity[id] = (id.z == d-1) ? float3(velocity[id].xy, -velocity[uint3(id.xy,d-2)].z) : velocity[id]; + + velocity[id] = (id.x == 0 && id.y == 0 && id.z != 0 && id.z != d-1) ? 0.5 * (velocity[uint3(1,0,id.z)] + velocity[uint3(0,1,id.z)]) : velocity[id]; + velocity[id] = (id.x == 0 && id.y == 0 && id.z == 0) ? (velocity[uint3(1,0,0)] + velocity[uint3(0,1,0)] + velocity[uint3(0,0,1)]) / 3 : velocity[id]; + velocity[id] = (id.x == 0 && id.y == 0 && id.z == d-1) ? (velocity[uint3(1,0,d-1)] + velocity[uint3(0,1,d-1)] + velocity[uint3(0,0,d-2)]) / 3 : velocity[id]; + velocity[id] = (id.x == 0 && id.y == h-1 && id.z != 0 && id.z != d-1) ? 0.5 * (velocity[uint3(1,h-1,id.z)] + velocity[uint3(0,h-2,id.z)]) : velocity[id]; + velocity[id] = (id.x == 0 && id.y == h-1 && id.z == 0) ? (velocity[uint3(1,h-1,0)] + velocity[uint3(0,h-2,0)] + velocity[uint3(0,h-1,1)]) / 3 : velocity[id]; + velocity[id] = (id.x == 0 && id.y == h-1 && id.z == d-1) ? (velocity[uint3(1,h-1,d-1)] + velocity[uint3(0,h-2,d-1)] + velocity[uint3(0,h-1,d-2)]) / 3 : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == 0 && id.z != 0 && id.z != d-1) ? 0.5 * (velocity[uint3(w-2,0,id.z)] + velocity[uint3(w-1,1,id.z)]) : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == 0 && id.z == 0) ? (velocity[uint3(w-2,0,0)] + velocity[uint3(w-1,1,0)] + velocity[uint3(w-1,0,1)]) / 3 : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == 0 && id.z == d-1) ? (velocity[uint3(w-2,0,d-1)] + velocity[uint3(w-1,1,d-1)] + velocity[uint3(w-1,0,d-2)]) / 3 : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == h-1 && id.z != 0 && id.z != d-1) ? 0.5 * (velocity[uint3(w-2,h-1,id.z)] + velocity[uint3(w-1,h-2,id.z)]) : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == h-1 && id.z == 0) ? (velocity[uint3(w-2,h-1,0)] + velocity[uint3(w-1,h-2,0)] + velocity[uint3(w-1,h-1,1)]) / 3 : velocity[id]; + velocity[id] = (id.x == w-1 && id.y == h-1 && id.z != d-1) ? (velocity[uint3(w-2,h-1,d-1)] + velocity[uint3(w-1,h-2,d-1)] + velocity[uint3(w-1,h-1,d-2)]) / 3 : velocity[id]; +} + +void SetBoundaryDiv(uint3 id, uint w, uint h, uint d) +{ + prev[id] = (id.x == 0) ? float4(prev[id].x, prev[id + uint3(1,0,0)].y, prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1) ? float4(prev[id].x, prev[uint3(w-2, id.yz)].y, prev[id].zw) : prev[id]; + prev[id] = (id.y == 0) ? float4(prev[id].x, prev[id + uint3(0,1,0)].y, prev[id].zw) : prev[id]; + prev[id] = (id.y == h-1) ? float4(prev[id].x, prev[uint3(id.x, h-2, id.z)].y, prev[id].zw) : prev[id]; + prev[id] = (id.z == 0) ? float4(prev[id].x, prev[id + uint3(0,0,1)].y, prev[id].zw) : prev[id]; + prev[id] = (id.z == d-1) ? float4(prev[id].x, prev[uint3(id.xy, d-2)].y, prev[id].zw) : prev[id]; + + prev[id] = (id.x == 0 && id.y == 0 && id.z != 0 && id.z != d-1) ? float4(prev[id].x, 0.5 * (prev[uint3(1,0,id.z)].y + prev[uint3(0,1,id.z)].y), prev[id].zw) : prev[id]; + prev[id] = (id.x == 0 && id.y == 0 && id.z == 0) ? float4(prev[id].x, (prev[uint3(1,0,0)].y + prev[uint3(0,1,0)].y + prev[uint3(0,0,1)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == 0 && id.y == 0 && id.z == d-1) ? float4(prev[id].x, (prev[uint3(1,0,d-1)].y + prev[uint3(0,1,d-1)].y + prev[uint3(0,0,d-2)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z != 0 && id.z != d-1) ? float4(prev[id].x, 0.5 * (prev[uint3(1,h-1,id.z)].y + prev[uint3(0,h-2,id.z)].y), prev[id].zw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z == 0) ? float4(prev[id].x, (prev[uint3(1,h-1,0)].y + prev[uint3(0,h-2,0)].y + prev[uint3(0,h-1,1)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z == d-1) ? float4(prev[id].x, (prev[uint3(1,h-1,d-1)].y + prev[uint3(0,h-2,d-1)].y + prev[uint3(0,h-1,d-2)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z != 0 && id.z != d-1) ? float4(prev[id].x, 0.5 * (prev[uint3(w-2,0,id.z)].y + prev[uint3(w-1,1,id.z)].y), prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z == 0) ? float4(prev[id].x, (prev[uint3(w-2,0,0)].y + prev[uint3(w-1,1,0)].y + prev[uint3(w-1,0,1)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z == d-1) ? float4(prev[id].x, (prev[uint3(w-2,0,d-1)].y + prev[uint3(w-1,1,d-1)].y + prev[uint3(w-1,0,d-2)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z != 0 && id.z != d-1) ? float4(prev[id].x, 0.5 * (prev[uint3(w-2,h-1,id.z)].y + prev[uint3(w-1,h-2,id.z)].y), prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z == 0) ? float4(prev[id].x, (prev[uint3(w-2,h-1,0)].y + prev[uint3(w-1,h-2,0)].y + prev[uint3(w-1,h-1,1)].y) / 3, prev[id].zw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z != d-1) ? float4(prev[id].x, (prev[uint3(w-2,h-1,d-1)].y + prev[uint3(w-1,h-2,d-1)].y + prev[uint3(w-1,h-1,d-2)].y) / 3, prev[id].zw) : prev[id]; +} + +void SetBoundaryP(uint3 id, uint w, uint h, uint d) +{ + prev[id] = (id.x == 0) ? float4(prev[id + uint3(1,0,0)].x, prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1) ? float4(prev[uint3(w-2, id.yz)].x, prev[id].yzw) : prev[id]; + prev[id] = (id.y == 0) ? float4(prev[id + uint3(0,1,0)].x, prev[id].yzw) : prev[id]; + prev[id] = (id.y == h-1) ? float4(prev[uint3(id.x, h-2, id.z)].x, prev[id].yzw) : prev[id]; + prev[id] = (id.z == 0) ? float4(prev[id + uint3(0,0,1)].x, prev[id].yzw) : prev[id]; + prev[id] = (id.z == d-1) ? float4(prev[uint3(id.xy, d-2)].x, prev[id].yzw) : prev[id]; + + prev[id] = (id.x == 0 && id.y == 0 && id.z != 0 && id.z != d-1) ? float4(0.5 * (prev[uint3(1,0,id.z)].x + prev[uint3(0,1,id.z)].x), prev[id].yzw) : prev[id]; + prev[id] = (id.x == 0 && id.y == 0 && id.z == 0) ? float4((prev[uint3(1,0,0)].x + prev[uint3(0,1,0)].x + prev[uint3(0,0,1)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == 0 && id.y == 0 && id.z == d-1) ? float4((prev[uint3(1,0,d-1)].x + prev[uint3(0,1,d-1)].x + prev[uint3(0,0,d-2)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z != 0 && id.z != d-1) ? float4(0.5 * (prev[uint3(1,h-1,id.z)].x + prev[uint3(0,h-2,id.z)].x), prev[id].yzw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z == 0) ? float4((prev[uint3(1,h-1,0)].x + prev[uint3(0,h-2,0)].x + prev[uint3(0,h-1,1)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == 0 && id.y == h-1 && id.z == d-1) ? float4((prev[uint3(1,h-1,d-1)].x + prev[uint3(0,h-2,d-1)].x + prev[uint3(0,h-1,d-2)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z != 0 && id.z != d-1) ? float4(0.5 * (prev[uint3(w-2,0,id.z)].x + prev[uint3(w-1,1,id.z)].x), prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z == 0) ? float4((prev[uint3(w-2,0,0)].x + prev[uint3(w-1,1,0)].x + prev[uint3(w-1,0,1)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == 0 && id.z == d-1) ? float4((prev[uint3(w-2,0,d-1)].x + prev[uint3(w-1,1,d-1)].x + prev[uint3(w-1,0,d-2)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z != 0 && id.z != d-1) ? float4(0.5 * (prev[uint3(w-2,h-1,id.z)].x + prev[uint3(w-1,h-2,id.z)].x), prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z == 0) ? float4((prev[uint3(w-2,h-1,0)].x + prev[uint3(w-1,h-2,0)].x + prev[uint3(w-1,h-1,1)].x) / 3, prev[id].yzw) : prev[id]; + prev[id] = (id.x == w-1 && id.y == h-1 && id.z != d-1) ? float4((prev[uint3(w-2,h-1,d-1)].x + prev[uint3(w-1,h-2,d-1)].x + prev[uint3(w-1,h-1,d-2)].x) / 3, prev[id].yzw) : prev[id]; +} + +//密度場外力項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AddSourceDensity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + float3 uvw; + density.GetDimensions(w, h, d); + uvw = float3(id.x/w, id.y/h, id.z/d); + + if (id.x < w && id.y < h && id.z < d) + { + density[id] += source.SampleLevel(_PointClamp, uvw, 0).w * densityCoef * dt; + prev[id] = float4(prev[id].xyz, source.SampleLevel(_PointClamp, uvw, 0).w * densityCoef * dt); + } +} + +//速度場外力項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AddSourceVelocity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + float3 uvw; + velocity.GetDimensions(w, h, d); + uvw = float3(id.x/w, id.y/h, id.z/d); + + if (id.x < w && id.y < h && id.z < d) + { + velocity[id] += source.SampleLevel(_PointClamp, uvw, 0).xyz * velocityCoef * dt; + prev[id] = float4(source.SampleLevel(_PointClamp, uvw, 0).xyz * velocityCoef * dt, prev[id].w); + } +} + +//密度場拡散項.diff∇・∇densによる勾配・発散をGauss-Seidel法にて計算。diffは拡散係数. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void DiffuseDensity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + density.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.z < d) + { + float a = dt * diff * w * h * d; + + [unroll] + for (int k = 0; k < GS_ITERATE; k++) { + density[id] = (prev[id].w + a * (density[int3(id.x - 1, id.yz)] + density[int3(id.x + 1, id.yz)] + density[int3(id.x, id.y - 1, id.z)] + density[int3(id.x, id.y + 1, id.z)] + density[int3(id.xy, id.z - 1)] + density[int3(id.xy, id.z + 1)])) / (1 + 6 * a); + SetBoundaryDensity(id, w, h, d); + } + } +} + +//速度場拡散(粘性)項.visc∇・∇velocityによる勾配・発散をGauss-Seidel法にて計算。viscはkinematic visocity(動粘性摩擦係数). +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void DiffuseVelocity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + velocity.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + float a = dt * visc * w * h; + + [unroll] + for (int k = 0; k < GS_ITERATE; k++) { + velocity[id] = (prev[id].xyz + a * (velocity[int3(id.x - 1, id.yz)] + velocity[int3(id.x + 1, id.yz)] + velocity[int3(id.x, id.y - 1, id.z)] + velocity[int3(id.x, id.y + 1, id.z)] + velocity[int3(id.xy, id.z - 1)] + velocity[int3(id.xy, id.z + 1)])) / (1 + 6 * a); + SetBoundaryVelocity(id, w, h, d); + } + } +} + +//密度場移流項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectDensity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + density.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + int ddx0, ddx1, ddy0, ddy1, ddz0, ddz1; + float x, y, z, s0, t0, u0, s1, t1, u1, dfdt; + + dfdt = dt * (w + h + d) / 3; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * velocity[id].x; + y = (float)id.y - dfdt * velocity[id].y; + z = (float)id.z - dfdt * velocity[id].z; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w - 1.5); + clamp(y, 0.5, h - 1.5); + clamp(z, 0.5, d - 1.5); + //バックトレースポイントの近傍セル割り出し. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + ddz0 = floor(z); + ddz1 = ddz0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + u1 = z - ddz0; + u0 = 1.0 - u1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + density[id] = s0 * u0 * (t0 * prev[int3(ddx0, ddy0, ddz0)].z + t1 * prev[int3(ddx0, ddy1, ddz0)].z) + + s1 * u0 * (t0 * prev[int3(ddx1, ddy0, ddz0)].z + t1 * prev[int3(ddx1, ddy1, ddz0)].z) + + s0 * u1 * (t0 * prev[int3(ddx0, ddy0, ddz1)].z + t1 * prev[int3(ddx0, ddy1, ddz1)].z) + + s1 * u1 * (t0 * prev[int3(ddx1, ddy0, ddz1)].z + t1 * prev[int3(ddx1, ddy1, ddz1)].z); + SetBoundaryDensity(id, w, h, d); + } +} + +//密度場移流項(このカーネルはvelocity stepを用いず、速度場を外部からsource texにフェッチした物を参照する事によって軽量化を狙う為用意). +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectDensityFromExt(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + float3 uvw; + density.GetDimensions(w, h, d); + uvw = float3(id.x/w, id.y/h, id.z/d); + + if (id.x < w && id.y < h && id.x < d) + { + int ddx0, ddx1, ddy0, ddy1, ddz0, ddz1; + float x, y, z, s0, t0, u0, s1, t1, u1, dfdt; + + dfdt = dt * (w + h + d) / 3; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * source.SampleLevel(_PointClamp, uvw, 0).x; + y = (float)id.y - dfdt * source.SampleLevel(_PointClamp, uvw, 0).y; + z = (float)id.z - dfdt * source.SampleLevel(_PointClamp, uvw, 0).z; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w - 1.5); + clamp(y, 0.5, h - 1.5); + clamp(z, 0.5, d - 1.5); + //バックトレースポイントの近傍セル割り出し. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + ddz0 = floor(z); + ddz1 = ddz0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + u1 = z - ddz0; + u0 = 1.0 - u1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + density[id] = s0 * u0 * (t0 * prev[int3(ddx0, ddy0, ddz0)].z + t1 * prev[int3(ddx0, ddy1, ddz0)].z) + + s1 * u0 * (t0 * prev[int3(ddx1, ddy0, ddz0)].z + t1 * prev[int3(ddx1, ddy1, ddz0)].z) + + s0 * u1 * (t0 * prev[int3(ddx0, ddy0, ddz1)].z + t1 * prev[int3(ddx0, ddy1, ddz1)].z) + + s1 * u1 * (t0 * prev[int3(ddx1, ddy0, ddz1)].z + t1 * prev[int3(ddx1, ddy1, ddz1)].z); + SetBoundaryDensity(id, w, h, d); + } +} + +//速度場移流項. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void AdvectVelocity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + density.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + int ddx0, ddx1, ddy0, ddy1, ddz0, ddz1; + float x, y, z, s0, t0, u0, s1, t1, u1, dfdt; + + dfdt = dt * (w + h + d) / 3; + + //バックトレースポイント割り出し. + x = (float)id.x - dfdt * prev[id].x; + y = (float)id.y - dfdt * prev[id].y; + z = (float)id.z - dfdt * prev[id].z; + //ポイントがシミュレーション範囲内に収まるようにクランプ. + clamp(x, 0.5, w - 1.5); + clamp(y, 0.5, h - 1.5); + clamp(z, 0.5, d - 1.5); + //xyzのそれぞれ近似の偏微分セルを求める. + ddx0 = floor(x); + ddx1 = ddx0 + 1; + ddy0 = floor(y); + ddy1 = ddy0 + 1; + ddz0 = floor(z); + ddz1 = ddz0 + 1; + //近傍セルとの線形補間用の差分を取っておく. + s1 = x - ddx0; + s0 = 1.0 - s1; + t1 = y - ddy0; + t0 = 1.0 - t1; + u1 = z - ddz0; + u0 = 1.0 - u1; + + //バックトレースし、1step前の値を近傍との線形補間をとって、現在の速度場に代入。 + velocity[id] = s0 * u0 * (t0 * prev[int3(ddx0, ddy0, ddz0)].xyz + t1 * prev[int3(ddx0, ddy1, ddz0)].xyz) + + s1 * u0 * (t0 * prev[int3(ddx1, ddy0, ddz0)].xyz + t1 * prev[int3(ddx1, ddy1, ddz0)].xyz) + + s0 * u1 * (t0 * prev[int3(ddx0, ddy0, ddz1)].xyz + t1 * prev[int3(ddx0, ddy1, ddz1)].xyz) + + s1 * u1 * (t0 * prev[int3(ddx1, ddy0, ddz1)].xyz + t1 * prev[int3(ddx1, ddy1, ddz1)].xyz); + SetBoundaryVelocity(id, w, h, d); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void SwapDensity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + density.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + float temp = density[id]; + //density[id] = prev[id].z; + prev[id] = float4(prev[id].xyz, temp); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void SwapVelocity(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + velocity.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + float3 temp = velocity[id]; + //velocity[id] = prev[id].xy; + prev[id] = float4(temp, prev[id].w); + } +} + +//非圧縮性流体の為、速度divergenceの流出入を∇・u = 0にする外圧pを求め、速度場に投影. +//質量保存Step1. +//step1では、速度場から発散の算出. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep1(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + velocity.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + float3 uvd; + uvd = float3(1.0 / w, 1.0 / h, 1.0 / d); + //prev[id].yをdivergenceとして使用. + prev[id] = float4(0.0, + -0.5 * (uvd.x * (velocity[int3(id.x + 1, id.y, id.z)].x - velocity[int3(id.x - 1, id.y, id.z)].x)) + + (uvd.y * (velocity[int3(id.x, id.y + 1, id.z)].y - velocity[int3(id.x, id.y - 1, id.z)].y)) + + (uvd.z * (velocity[int3(id.x, id.y, id.z + 1)].z - velocity[int3(id.x, id.y, id.z - 1)].z)), + prev[id].zw); + + SetBoundaryDiv(id, w, h, d); + SetBoundaryP(id, w, h, d); + } +} + +//質量保存Step2. +//step2では、step1で求めた発散からPoisson方程式をガウス・ザイデル法で解く. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep2(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + velocity.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + for (int k = 0; k < GS_ITERATE; k++) + { + //prev[id].xを外圧∇pとして使用. + prev[id] = float4( + (prev[id].y + prev[uint3(id.x - 1, id.y, id.z)].x + prev[uint3(id.x + 1, id.y, id.z)].x + + prev[uint3(id.x, id.y - 1, id.z)].x + prev[uint3(id.x, id.y + 1, id.z)].x + + prev[uint3(id.x, id.y, id.z - 1)].x + prev[uint3(id.x, id.y, id.z + 1)].x) / 6, + prev[id].yzw); + SetBoundaryP(id, w, h, d); + } + } +} + +//質量保存Step3. +//step3で、∇・u = 0にする. +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void ProjectStep3(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + velocity.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + float velX, velY, velZ; + float3 uvd; + uvd = float3(1.0 / w, 1.0 / h, 1.0 / d); + + velX = velocity[id].x; + velY = velocity[id].y; + velZ = velocity[id].z; + + velX -= 0.5 * (prev[uint3(id.x + 1, id.y, id.z)].x - prev[uint3(id.x - 1, id.y, id.z)].x) / uvd.x; + velY -= 0.5 * (prev[uint3(id.x, id.y + 1, id.z)].x - prev[uint3(id.x, id.y - 1, id.z)].x) / uvd.y; + velZ -= 0.5 * (prev[uint3(id.x, id.y, id.z + 1)].x - prev[uint3(id.x, id.y, id.z - 1)].x) / uvd.z; + + velocity[id] = float3(velX, velY, velZ); + SetBoundaryVelocity(id, w, h, d); + } +} + +[numthreads(THREAD_X, THREAD_Y, THREAD_Z)] +void Draw(uint3 id : SV_DispatchThreadID) +{ + uint w, h, d; + solver.GetDimensions(w, h, d); + + if (id.x < w && id.y < h && id.x < d) + { + solver[id] = float4(velocity[id].xyz, density[id]); + } +} \ No newline at end of file diff --git a/Assets/StableFluids/ComputeShaders/Solver3D.compute.meta b/Assets/StableFluids/ComputeShaders/Solver3D.compute.meta new file mode 100644 index 0000000..9301969 --- /dev/null +++ b/Assets/StableFluids/ComputeShaders/Solver3D.compute.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 98a53d1c593876240ba3fe84345d2f17 +timeCreated: 1493362868 +licenseType: Pro +ComputeShaderImporter: + currentAPIMask: 4 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Material.meta b/Assets/StableFluids/Material.meta new file mode 100644 index 0000000..4c5da1b --- /dev/null +++ b/Assets/StableFluids/Material.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7e80e7f25a3f3fa478255a09f5e5d003 +folderAsset: yes +timeCreated: 1507625820 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Material/AddSource.mat b/Assets/StableFluids/Material/AddSource.mat new file mode 100644 index 0000000..76ce30b --- /dev/null +++ b/Assets/StableFluids/Material/AddSource.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: AddSource + m_Shader: {fileID: 4800000, guid: 2bf7b9de857590545861a2316f3ef4ff, type: 3} + m_ShaderKeywords: _EMISSION + m_LightmapFlags: 1 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _InvRadius: 50 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _Radius: 0.022 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _DirAndCenter: {r: 0, g: 0, b: 0.38849765, a: 0.5722161} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _Source: {r: 0, g: 0, b: 0.318555, a: 0.41274238} diff --git a/Assets/StableFluids/Material/AddSource.mat.meta b/Assets/StableFluids/Material/AddSource.mat.meta new file mode 100644 index 0000000..024f491 --- /dev/null +++ b/Assets/StableFluids/Material/AddSource.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c50ccf50d761fd543a108b1579e0055b +timeCreated: 1493267834 +licenseType: Pro +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Material/Debug.mat b/Assets/StableFluids/Material/Debug.mat new file mode 100644 index 0000000..848b4c7 --- /dev/null +++ b/Assets/StableFluids/Material/Debug.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Debug + m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _EMISSION + m_LightmapFlags: 1 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/StableFluids/Material/Debug.mat.meta b/Assets/StableFluids/Material/Debug.mat.meta new file mode 100644 index 0000000..ee4d021 --- /dev/null +++ b/Assets/StableFluids/Material/Debug.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c6014208ef1b22c43baca45cece86c1a +timeCreated: 1493496755 +licenseType: Pro +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Material/Sample.mat b/Assets/StableFluids/Material/Sample.mat new file mode 100644 index 0000000..1eb7911 --- /dev/null +++ b/Assets/StableFluids/Material/Sample.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Sample + m_Shader: {fileID: 4800000, guid: 282dcb7cfb3e90142a89ad33496fa1e9, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/StableFluids/Material/Sample.mat.meta b/Assets/StableFluids/Material/Sample.mat.meta new file mode 100644 index 0000000..fa90d63 --- /dev/null +++ b/Assets/StableFluids/Material/Sample.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d3b631bf92818147b24cdc99d38a2a0 +timeCreated: 1499327481 +licenseType: Pro +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Sample.meta b/Assets/StableFluids/Sample.meta new file mode 100644 index 0000000..84558a0 --- /dev/null +++ b/Assets/StableFluids/Sample.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 90b70b1b3ebb7b04b90c258244e2b058 +folderAsset: yes +timeCreated: 1507625820 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Sample/Scene.meta b/Assets/StableFluids/Sample/Scene.meta new file mode 100644 index 0000000..3b5a0ea --- /dev/null +++ b/Assets/StableFluids/Sample/Scene.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6fd2ab580f276574a8ded2388e830ad4 +folderAsset: yes +timeCreated: 1492769258 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Sample/Scene/sample.unity b/Assets/StableFluids/Sample/Scene/sample.unity new file mode 100644 index 0000000..963b6a5 --- /dev/null +++ b/Assets/StableFluids/Sample/Scene/sample.unity @@ -0,0 +1,417 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 8 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 9 + m_Resolution: 2 + m_BakeResolution: 40 + m_TextureWidth: 1024 + m_TextureHeight: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 1 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFiltering: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousColorSigma: 1 + m_PVRFilteringAtrousNormalSigma: 1 + m_PVRFilteringAtrousPositionSigma: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &365674904 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 365674907} + - component: {fileID: 365674905} + m_Layer: 0 + m_Name: AddSource + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &365674905 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 365674904} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d6027424a3a36ad4a8debccd22920a94, type: 3} + m_Name: + m_EditorClassIdentifier: + addSourceMat: {fileID: 2100000, guid: c50ccf50d761fd543a108b1579e0055b, type: 2} + sourceRadius: 0.022 + addSourceTex: {fileID: 0} + OnSourceUpdated: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1837855862} + m_MethodName: set_SorceTex + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: StableFluid.MouseSourceProvider+SourceEvent, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null +--- !u!4 &365674907 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 365674904} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &571547252 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 571547257} + - component: {fileID: 571547256} + - component: {fileID: 571547255} + - component: {fileID: 571547254} + - component: {fileID: 571547253} + - component: {fileID: 571547258} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &571547253 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_Enabled: 1 +--- !u!124 &571547254 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_Enabled: 1 +--- !u!92 &571547255 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_Enabled: 1 +--- !u!20 &571547256 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 1 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 + m_StereoMirrorMode: 0 +--- !u!4 &571547257 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &571547258 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 571547252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3b5841f782e494a489de3ff0ca3b975f, type: 3} + m_Name: + m_EditorClassIdentifier: + OnCreateTex: + m_PersistentCalls: + m_Calls: [] + m_TypeName: Render.RenderEffect+TextureEvent, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null + propName: _PropName + effects: + - {fileID: 2100000, guid: 8d3b631bf92818147b24cdc99d38a2a0, type: 2} + show: 1 + format: 0 + wrapMode: 0 + downSample: 0 +--- !u!1 &1837855861 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1837855863} + - component: {fileID: 1837855862} + m_Layer: 0 + m_Name: Solver + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1837855862 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1837855861} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c4234aa3aeedb7c4a97e164dbe948a47, type: 3} + m_Name: + m_EditorClassIdentifier: + computeShader: {fileID: 7200000, guid: 8f93e96f4f3aed44592d1d829b633854, type: 3} + solverTexProp: SolverTex + diff: 10 + visc: 20 + velocityCoef: 2 + densityCoef: 2 + isDensityOnly: 0 + lod: 0 + debug: 0 + debugMat: {fileID: 2100000, guid: c6014208ef1b22c43baca45cece86c1a, type: 2} + sourceTex: {fileID: 0} +--- !u!4 &1837855863 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1837855861} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2041457313 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 2041457317} + - component: {fileID: 2041457316} + - component: {fileID: 2041457315} + - component: {fileID: 2041457314} + m_Layer: 0 + m_Name: Debug + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!23 &2041457314 +MeshRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2041457313} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_Materials: + - {fileID: 2100000, guid: c6014208ef1b22c43baca45cece86c1a, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!64 &2041457315 +MeshCollider: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2041457313} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Convex: 0 + m_InflateMesh: 0 + m_SkinWidth: 0.01 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &2041457316 +MeshFilter: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2041457313} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2041457317 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2041457313} + m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: -4.7, y: 5.6, z: 0} + m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0} diff --git a/Assets/StableFluids/Sample/Scene/sample.unity.meta b/Assets/StableFluids/Sample/Scene/sample.unity.meta new file mode 100644 index 0000000..16db4eb --- /dev/null +++ b/Assets/StableFluids/Sample/Scene/sample.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 973ae194b2629384589d13a9ee387f98 +timeCreated: 1492769278 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Sample/Script.meta b/Assets/StableFluids/Sample/Script.meta new file mode 100644 index 0000000..5f6d25b --- /dev/null +++ b/Assets/StableFluids/Sample/Script.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a02beeedb0cbfff44ace9321841f3a7b +folderAsset: yes +timeCreated: 1507568905 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts.meta b/Assets/StableFluids/Scripts.meta new file mode 100644 index 0000000..6a484de --- /dev/null +++ b/Assets/StableFluids/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7e73d146561c3de418957a5e77711116 +folderAsset: yes +timeCreated: 1507625820 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/MouseSourceProvider.cs b/Assets/StableFluids/Scripts/MouseSourceProvider.cs new file mode 100644 index 0000000..2224db4 --- /dev/null +++ b/Assets/StableFluids/Scripts/MouseSourceProvider.cs @@ -0,0 +1,95 @@ +using System; +using UnityEngine; + +namespace StableFluid +{ + public class MouseSourceProvider : MonoBehaviour + { + private string source2dProp = "_Source"; + private string sourceRadiusProp = "_Radius"; + private int source2dId, sourceRadiusId; + private Vector3 lastMousePos; + + [SerializeField] + private Material addSourceMat; + + [SerializeField] + private float sourceRadius = 0.03f; + + public RenderTexture addSourceTex; + public SourceEvent OnSourceUpdated; + + void Awake() + { + source2dId = Shader.PropertyToID(source2dProp); + sourceRadiusId = Shader.PropertyToID(sourceRadiusProp); + } + + void Update() + { + InitializeSourceTex(Screen.width, Screen.height); + UpdateSource(); + } + + void OnDestroy() + { + ReleaseForceField(); + } + + void InitializeSourceTex(int width, int height) + { + if (addSourceTex == null || addSourceTex.width != width || addSourceTex.height != height) + { + ReleaseForceField(); + addSourceTex = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat); + } + } + + void UpdateSource() + { + var mousePos = Input.mousePosition; + var dpdt = UpdateMousePos(mousePos); + var velocitySource = Vector2.zero; + var uv = Vector2.zero; + + if (Input.GetMouseButton(0)) + { + uv = Camera.main.ScreenToViewportPoint(mousePos); + velocitySource = Vector2.ClampMagnitude(dpdt, 1f); + addSourceMat.SetVector(source2dId, new Vector4(velocitySource.x, velocitySource.y, uv.x, uv.y)); + addSourceMat.SetFloat(sourceRadiusId, sourceRadius); + Graphics.Blit(null, addSourceTex, addSourceMat); + NotifySourceTexUpdate(); + } + else + { + NotifyNoSourceTexUpdate(); + } + } + + void NotifySourceTexUpdate() + { + OnSourceUpdated.Invoke(addSourceTex); + } + + void NotifyNoSourceTexUpdate() + { + OnSourceUpdated.Invoke(null); + } + + Vector3 UpdateMousePos(Vector3 mousePos) + { + var dpdt = mousePos - lastMousePos; + lastMousePos = mousePos; + return dpdt; + } + + void ReleaseForceField() + { + Destroy(addSourceTex); + } + + [Serializable] + public class SourceEvent : UnityEngine.Events.UnityEvent { } + } +} \ No newline at end of file diff --git a/Assets/StableFluids/Scripts/MouseSourceProvider.cs.meta b/Assets/StableFluids/Scripts/MouseSourceProvider.cs.meta new file mode 100644 index 0000000..ee23359 --- /dev/null +++ b/Assets/StableFluids/Scripts/MouseSourceProvider.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d6027424a3a36ad4a8debccd22920a94 +timeCreated: 1493266982 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/Render.meta b/Assets/StableFluids/Scripts/Render.meta new file mode 100644 index 0000000..2663dda --- /dev/null +++ b/Assets/StableFluids/Scripts/Render.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c8bc0091dc625114cb402d499fa86b6b +folderAsset: yes +timeCreated: 1492761758 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/Render/RenderEffect.cs b/Assets/StableFluids/Scripts/Render/RenderEffect.cs new file mode 100644 index 0000000..7d7ff66 --- /dev/null +++ b/Assets/StableFluids/Scripts/Render/RenderEffect.cs @@ -0,0 +1,75 @@ +using UnityEngine; + +namespace Render +{ + public class RenderEffect : MonoBehaviour + { + public TextureEvent OnCreateTex; + public RenderTexture Output { get; private set; } + + [SerializeField] string propName = "_PropName"; + [SerializeField] Material[] effects; + [SerializeField] bool show = true; + [SerializeField] RenderTextureFormat format = RenderTextureFormat.ARGBFloat; + [SerializeField] TextureWrapMode wrapMode; + [SerializeField] int downSample = 0; + + RenderTexture[] rts = new RenderTexture[2]; + + void Update() + { + if (Input.GetKeyDown(KeyCode.Alpha6)) + show = !show; + } + + void OnRenderImage(RenderTexture s, RenderTexture d) + { + CheckRTs(s); + Graphics.Blit(s, rts[0]); + foreach (var m in effects) + { + Graphics.Blit(rts[0], rts[1], m); + SwapRTs(); + } + + Graphics.Blit(rts[0], Output); + Shader.SetGlobalTexture(propName, Output); + if (show) + Graphics.Blit(Output, d); + else + Graphics.Blit(s, d); + } + + void CheckRTs(RenderTexture s) + { + if (rts[0] == null || rts[0].width != s.width >> downSample || rts[0].height != s.height >> downSample) + { + for (var i = 0; i < rts.Length; i++) + { + var rt = rts[i]; + rts[i] = RenderUtility.CreateRenderTexture(s.width >> downSample, s.height >> downSample, 16, format, wrapMode, FilterMode.Bilinear, rt); + } + Output = RenderUtility.CreateRenderTexture(s.width >> downSample, s.height >> downSample, 16, format, wrapMode, FilterMode.Bilinear, Output); + OnCreateTex.Invoke(Output); + } + } + + void SwapRTs() + { + var tmp = rts[0]; + rts[0] = rts[1]; + rts[1] = tmp; + } + + void OnDisabled() + { + foreach (var rt in rts) + RenderUtility.ReleaseRenderTexture(rt); + RenderUtility.ReleaseRenderTexture(Output); + } + + [System.Serializable] + public class TextureEvent : UnityEngine.Events.UnityEvent { } + } +} + diff --git a/Assets/StableFluids/Scripts/Render/RenderEffect.cs.meta b/Assets/StableFluids/Scripts/Render/RenderEffect.cs.meta new file mode 100644 index 0000000..100ac3b --- /dev/null +++ b/Assets/StableFluids/Scripts/Render/RenderEffect.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3b5841f782e494a489de3ff0ca3b975f +timeCreated: 1492761548 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/Render/RenderUtility.cs b/Assets/StableFluids/Scripts/Render/RenderUtility.cs new file mode 100644 index 0000000..0675bc7 --- /dev/null +++ b/Assets/StableFluids/Scripts/Render/RenderUtility.cs @@ -0,0 +1,63 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; + +namespace Render +{ + public class RenderUtility : MonoBehaviour + { + + public static RenderTexture CreateRenderTexture(int width, int height, int depth, RenderTextureFormat format, TextureWrapMode wrapMode = TextureWrapMode.Repeat, FilterMode filterMode = FilterMode.Bilinear, RenderTexture rt = null) + { + if (rt != null) + { + if (rt.width == width && rt.height == height) return rt; + } + + ReleaseRenderTexture(rt); + rt = new RenderTexture(width, height, depth, format); + rt.enableRandomWrite = true; + rt.wrapMode = wrapMode; + rt.filterMode = filterMode; + rt.Create(); + ClearRenderTexture(rt, Color.clear); + return rt; + } + + public static RenderTexture CreateVolumetricRenderTexture(int width, int height, int volumeDepth, int depth, RenderTextureFormat format, TextureWrapMode wrapMode = TextureWrapMode.Repeat, FilterMode filterMode = FilterMode.Bilinear, RenderTexture rt = null) + { + if (rt != null) + { + if (rt.width == width && rt.height == height) return rt; + } + + ReleaseRenderTexture(rt); + rt = new RenderTexture(width, height, depth, format); + rt.dimension = TextureDimension.Tex3D; + rt.volumeDepth = volumeDepth; + rt.enableRandomWrite = true; + rt.wrapMode = wrapMode; + rt.filterMode = filterMode; + rt.Create(); + ClearRenderTexture(rt, Color.clear); + return rt; + } + + public static void ReleaseRenderTexture(RenderTexture rt) + { + if (rt == null) return; + + rt.Release(); + Destroy(rt); + } + + public static void ClearRenderTexture(RenderTexture target, Color bg) + { + var active = RenderTexture.active; + RenderTexture.active = target; + GL.Clear(true, true, bg); + RenderTexture.active = active; + } + } +} \ No newline at end of file diff --git a/Assets/StableFluids/Scripts/Render/RenderUtility.cs.meta b/Assets/StableFluids/Scripts/Render/RenderUtility.cs.meta new file mode 100644 index 0000000..1c267cf --- /dev/null +++ b/Assets/StableFluids/Scripts/Render/RenderUtility.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 98f7d519136c0c547b53f0623e7a0370 +timeCreated: 1499754254 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/Solver2D.cs b/Assets/StableFluids/Scripts/Solver2D.cs new file mode 100644 index 0000000..a7c8058 --- /dev/null +++ b/Assets/StableFluids/Scripts/Solver2D.cs @@ -0,0 +1,128 @@ +using UnityEngine; + +namespace StableFluid +{ + public class Solver2D : SolverBase + { + #region Initialize + + protected override void InitializeComputeShader() + { + width = Screen.width; + height = Screen.height; + solverTex = CreateRenderTexture(width >> lod, height >> lod, 0, RenderTextureFormat.ARGBFloat, solverTex); + densityTex = CreateRenderTexture(width >> lod, height >> lod, 0, RenderTextureFormat.RHalf, densityTex); + velocityTex = CreateRenderTexture(width >> lod, height >> lod, 0, RenderTextureFormat.RGHalf, velocityTex); + prevTex = CreateRenderTexture(width >> lod, height >> lod, 0, RenderTextureFormat.ARGBHalf, prevTex); + + Shader.SetGlobalTexture(solverTexId, solverTex); + + computeShader.SetFloat(diffId, diff); + computeShader.SetFloat(viscId, visc); + computeShader.SetFloat(dtId, Time.deltaTime); + computeShader.SetFloat(velocityCoefId, velocityCoef); + computeShader.SetFloat(densityCoefId, densityCoef); + } + + #endregion + + #region StableFluid gpu kernel steps + + protected override void DensityStep() + { + //Add density source to density field + if (SorceTex != null) + { + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], sourceId, SorceTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AddSourceDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + } + + //Diffuse density + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.DiffuseDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + + //Swap density + computeShader.SetTexture(kernelMap[ComputeKernels.SwapDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.SwapDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.SwapDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + + if (isDensityOnly) + { + //Advection using external velocity field via ForceTex. + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], prevId, prevTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], velocityId, velocityTex); + if (SorceTex != null) computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], sourceId, SorceTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + } + else + { + //Advection using velocity solver + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], prevId, prevTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], velocityId, velocityTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + } + } + + protected override void VelocityStep() + { + //Add velocity source to velocity field + if (SorceTex != null) + { + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], sourceId, SorceTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AddSourceVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + } + + //Diffuse velocity + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.DiffuseVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep1], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep2], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep2], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep3], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Swap velocity + computeShader.SetTexture(kernelMap[ComputeKernels.SwapVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.SwapVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.SwapVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Advection + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep1], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep2], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep2], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep3], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), 1); + } + + #endregion + } +} \ No newline at end of file diff --git a/Assets/StableFluids/Scripts/Solver2D.cs.meta b/Assets/StableFluids/Scripts/Solver2D.cs.meta new file mode 100644 index 0000000..d1b99fc --- /dev/null +++ b/Assets/StableFluids/Scripts/Solver2D.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c4234aa3aeedb7c4a97e164dbe948a47 +timeCreated: 1499009510 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/Solver3D.cs b/Assets/StableFluids/Scripts/Solver3D.cs new file mode 100644 index 0000000..7a8670a --- /dev/null +++ b/Assets/StableFluids/Scripts/Solver3D.cs @@ -0,0 +1,135 @@ +using UnityEngine; + +namespace StableFluid +{ + public class Solver3D : SolverBase + { + #region Variables + + [SerializeField] + private int depth; + + #endregion + + #region Initialize + + protected override void InitializeComputeShader() + { + width = Screen.width; + height = Screen.height; + solverTex = CreateVolumetricRenderTexture(width >> lod, height >> lod, depth >> lod, 0, RenderTextureFormat.ARGBFloat, solverTex); + densityTex = CreateVolumetricRenderTexture(width >> lod, height >> lod, depth >> lod, 0, RenderTextureFormat.RHalf, densityTex); + velocityTex = CreateVolumetricRenderTexture(width >> lod, height >> lod, depth >> lod, 0, RenderTextureFormat.RGHalf, velocityTex); + prevTex = CreateVolumetricRenderTexture(width >> lod, height >> lod, depth >> lod, 0, RenderTextureFormat.ARGBHalf, prevTex); + + Shader.SetGlobalTexture(solverTexId, solverTex); + + computeShader.SetFloat(diffId, diff); + computeShader.SetFloat(viscId, visc); + computeShader.SetFloat(dtId, Time.deltaTime); + computeShader.SetFloat(velocityCoefId, velocityCoef); + computeShader.SetFloat(densityCoefId, densityCoef); + } + + #endregion + + #region StableFluid gpu kernel steps + + protected override void DensityStep() + { + //Add density source to density field + if (SorceTex != null) + { + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], sourceId, SorceTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AddSourceDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + } + + //Diffuse density + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.DiffuseDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Swap density + computeShader.SetTexture(kernelMap[ComputeKernels.SwapDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.SwapDensity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.SwapDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + if (isDensityOnly) + { + //Advection using external velocity field via ForceTex. + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], prevId, prevTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], velocityId, velocityTex); + if (SorceTex != null) computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensityFromExt], sourceId, SorceTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + } + else + { + //Advection using velocity solver + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], prevId, prevTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectDensity], velocityId, velocityTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectDensity], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + } + } + + protected override void VelocityStep() + { + //Add velocity source to velocity field + if (SorceTex != null) + { + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], sourceId, SorceTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AddSourceVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AddSourceVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + } + + //Diffuse velocity + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.DiffuseVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.DiffuseVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep1], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep2], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep2], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep3], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Swap velocity + computeShader.SetTexture(kernelMap[ComputeKernels.SwapVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.SwapVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.SwapVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Advection + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectVelocity], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.AdvectVelocity], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.AdvectVelocity], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep1], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep1], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep2], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep2], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + + //Project + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], velocityId, velocityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.ProjectStep3], prevId, prevTex); + computeShader.Dispatch(kernelMap[ComputeKernels.ProjectStep3], Mathf.CeilToInt(velocityTex.width / gpuThreads.x), Mathf.CeilToInt(velocityTex.height / gpuThreads.y), Mathf.CeilToInt(depth / gpuThreads.z)); + } + + #endregion + } +} \ No newline at end of file diff --git a/Assets/StableFluids/Scripts/Solver3D.cs.meta b/Assets/StableFluids/Scripts/Solver3D.cs.meta new file mode 100644 index 0000000..4d57c6c --- /dev/null +++ b/Assets/StableFluids/Scripts/Solver3D.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 200e648b64770c741bfd0a7aad51d5df +timeCreated: 1499351914 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Scripts/SolverBase.cs b/Assets/StableFluids/Scripts/SolverBase.cs new file mode 100644 index 0000000..59eb9b7 --- /dev/null +++ b/Assets/StableFluids/Scripts/SolverBase.cs @@ -0,0 +1,279 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.Rendering; + +namespace StableFluid +{ + public struct GPUThreads + { + public int x; + public int y; + public int z; + + public GPUThreads(uint x, uint y, uint z) + { + this.x = (int)x; + this.y = (int)y; + this.z = (int)z; + } + } + + public static class DirectCompute5_0 + { + //Use DirectCompute 5.0 on DirectX11 hardware. + public const int MAX_THREAD = 1024; + public const int MAX_X = 1024; + public const int MAX_Y = 1024; + public const int MAX_Z = 64; + public const int MAX_DISPATCH = 65535; + public const int MAX_PROCESS = MAX_DISPATCH * MAX_THREAD; + } + + public abstract class SolverBase : MonoBehaviour + { + #region Variables + + protected enum ComputeKernels + { + AddSourceDensity, + DiffuseDensity, + AdvectDensity, + AdvectDensityFromExt, + SwapDensity, + + AddSourceVelocity, + DiffuseVelocity, + AdvectVelocity, + SwapVelocity, + ProjectStep1, + ProjectStep2, + ProjectStep3, + + Draw + } + + protected Dictionary kernelMap = new Dictionary(); + protected GPUThreads gpuThreads; + protected RenderTexture solverTex; + protected RenderTexture densityTex; + protected RenderTexture velocityTex; + protected RenderTexture prevTex; + protected string solverProp = "solver"; + protected string densityProp = "density"; + protected string velocityProp = "velocity"; + protected string prevProp = "prev"; + protected string sourceProp = "source"; + protected string diffProp = "diff"; + protected string viscProp = "visc"; + protected string dtProp = "dt"; + protected string velocityCoefProp = "velocityCoef"; + protected string densityCoefProp = "densityCoef"; + protected int solverId, densityId, velocityId, prevId, sourceId, diffId, viscId, dtId, velocityCoefId, densityCoefId, solverTexId; + protected int width, height; + + + [SerializeField] + protected ComputeShader computeShader; + + [SerializeField] + protected string solverTexProp = "SolverTex"; + + [SerializeField] + protected float diff; + + [SerializeField] + protected float visc; + + [SerializeField] + protected float velocityCoef; + + [SerializeField] + protected float densityCoef; + + [SerializeField] + protected bool isDensityOnly = false; + + [SerializeField] + protected int lod = 0; + + [SerializeField] + protected bool debug = false; + + [SerializeField] + protected Material debugMat; + + [SerializeField] RenderTexture sourceTex; + public RenderTexture SorceTex { set { sourceTex = value; } get { return sourceTex; } } + + #endregion + + #region unity builtin + + protected virtual void Start() + { + Initialize(); + } + + protected virtual void Update() + { + if (width != Screen.width || height != Screen.height) InitializeComputeShader(); + computeShader.SetFloat(diffId, diff); + computeShader.SetFloat(viscId, visc); + computeShader.SetFloat(diffId, diff); + computeShader.SetFloat(viscId, visc); + computeShader.SetFloat(dtId, Time.deltaTime); + computeShader.SetFloat(velocityCoefId, velocityCoef); + computeShader.SetFloat(densityCoefId, densityCoef); + + if (!isDensityOnly) VelocityStep(); + DensityStep(); + + computeShader.SetTexture(kernelMap[ComputeKernels.Draw], densityId, densityTex); + computeShader.SetTexture(kernelMap[ComputeKernels.Draw], velocityId, velocityTex); + computeShader.SetTextureFromGlobal(kernelMap[ComputeKernels.Draw], solverId, solverTexId); + computeShader.Dispatch(kernelMap[ComputeKernels.Draw], Mathf.CeilToInt(solverTex.width / gpuThreads.x), Mathf.CeilToInt(solverTex.height / gpuThreads.y), 1); + + Shader.SetGlobalTexture(solverTexId, solverTex); + } + + void OnDestroy() + { + CleanUp(); + } + + #endregion + + #region Initialize + + protected virtual void Initialize() + { + uint threadX, threadY, threadZ; + + InitialCheck(); + + kernelMap = System.Enum.GetValues(typeof(ComputeKernels)) + .Cast() + .ToDictionary(t => t, t => computeShader.FindKernel(t.ToString())); + + computeShader.GetKernelThreadGroupSizes(kernelMap[ComputeKernels.Draw], out threadX, out threadY, out threadZ); + + gpuThreads = new GPUThreads(threadX, threadY, threadZ); + solverTexId = Shader.PropertyToID(solverTexProp); + + solverId = Shader.PropertyToID(solverProp); + densityId = Shader.PropertyToID(densityProp); + velocityId = Shader.PropertyToID(velocityProp); + prevId = Shader.PropertyToID(prevProp); + sourceId = Shader.PropertyToID(sourceProp); + diffId = Shader.PropertyToID(diffProp); + viscId = Shader.PropertyToID(viscProp); + dtId = Shader.PropertyToID(dtProp); + velocityCoefId = Shader.PropertyToID(velocityCoefProp); + densityCoefId = Shader.PropertyToID(densityCoefProp); + + InitializeComputeShader(); + + if (debug) + { + if (debugMat == null) return; + debugMat.mainTexture = solverTex; + } + } + + protected virtual void InitialCheck() + { + Assert.IsTrue(SystemInfo.graphicsShaderLevel >= 50, "Under the DirectCompute5.0 (DX11 GPU) doesn't work : StableFluid"); + Assert.IsTrue(gpuThreads.x * gpuThreads.y * gpuThreads.z <= DirectCompute5_0.MAX_PROCESS, "Resolution is too heigh : Stablefluid"); + Assert.IsTrue(gpuThreads.x <= DirectCompute5_0.MAX_X, "THREAD_X is too large : StableFluid"); + Assert.IsTrue(gpuThreads.y <= DirectCompute5_0.MAX_Y, "THREAD_Y is too large : StableFluid"); + Assert.IsTrue(gpuThreads.z <= DirectCompute5_0.MAX_Z, "THREAD_Z is too large : StableFluid"); + } + + protected abstract void InitializeComputeShader(); + + #endregion + + + #region StableFluid gpu kernel steps + + protected abstract void DensityStep(); + + protected abstract void VelocityStep(); + + #endregion + + #region render texture + + public RenderTexture CreateRenderTexture(int width, int height, int depth, RenderTextureFormat format, RenderTexture rt = null) + { + if (rt != null) + { + if (rt.width == width && rt.height == height) return rt; + } + + ReleaseRenderTexture(rt); + rt = new RenderTexture(width, height, depth, format); + rt.enableRandomWrite = true; + rt.wrapMode = TextureWrapMode.Clamp; + rt.filterMode = FilterMode.Point; + rt.Create(); + ClearRenderTexture(rt, Color.clear); + return rt; + } + + public RenderTexture CreateVolumetricRenderTexture(int width, int height, int volumeDepth, int depth, RenderTextureFormat format, RenderTexture rt = null) + { + if (rt != null) + { + if (rt.width == width && rt.height == height) return rt; + } + + ReleaseRenderTexture(rt); + rt = new RenderTexture(width, height, depth, format); + rt.dimension = TextureDimension.Tex3D; + rt.volumeDepth = volumeDepth; + rt.enableRandomWrite = true; + rt.wrapMode = TextureWrapMode.Clamp; + rt.filterMode = FilterMode.Point; + rt.Create(); + ClearRenderTexture(rt, Color.clear); + return rt; + } + + public void ReleaseRenderTexture(RenderTexture rt) + { + if (rt == null) return; + + rt.Release(); + Destroy(rt); + } + + public void ClearRenderTexture(RenderTexture target, Color bg) + { + var active = RenderTexture.active; + RenderTexture.active = target; + GL.Clear(true, true, bg); + RenderTexture.active = active; + } + + #endregion + + #region release + + void CleanUp() + { + ReleaseRenderTexture(solverTex); + ReleaseRenderTexture(densityTex); + ReleaseRenderTexture(velocityTex); + ReleaseRenderTexture(prevTex); + +#if UNITY_EDITOR + Debug.Log("Buffer released"); +#endif + } + + #endregion + } +} diff --git a/Assets/StableFluids/Scripts/SolverBase.cs.meta b/Assets/StableFluids/Scripts/SolverBase.cs.meta new file mode 100644 index 0000000..2541e4b --- /dev/null +++ b/Assets/StableFluids/Scripts/SolverBase.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9167428fcc81cff439fb3cdfe3d547e2 +timeCreated: 1499351954 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Shaders.meta b/Assets/StableFluids/Shaders.meta new file mode 100644 index 0000000..9410745 --- /dev/null +++ b/Assets/StableFluids/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: aa2b398ee5eb67149b6919535182216c +folderAsset: yes +timeCreated: 1507625820 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Shaders/AddSource.shader b/Assets/StableFluids/Shaders/AddSource.shader new file mode 100644 index 0000000..b162452 --- /dev/null +++ b/Assets/StableFluids/Shaders/AddSource.shader @@ -0,0 +1,43 @@ +Shader "StableFluid/AddSource" { + Properties { + _Source ("Adding source", Vector) = (0, 1, 0.5, 0.5) //xy = velocity, zw = center pos + _Radius ("Radius", Float) = 10 + } + SubShader { + Cull Off ZWrite Off ZTest Always + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + float4 _Source; + float _Radius; + + v2f vert (appdata v) { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + float4 frag (v2f i) : SV_Target { + float2 dpdt = (i.uv - _Source.zw) / _Radius; + return float4(_Source.xy * saturate(1.0 - dot(dpdt, dpdt)), saturate(1.0 - dot(dpdt, dpdt)), 0); + } + ENDCG + } + } +} diff --git a/Assets/StableFluids/Shaders/AddSource.shader.meta b/Assets/StableFluids/Shaders/AddSource.shader.meta new file mode 100644 index 0000000..410e935 --- /dev/null +++ b/Assets/StableFluids/Shaders/AddSource.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2bf7b9de857590545861a2316f3ef4ff +timeCreated: 1493267028 +licenseType: Pro +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StableFluids/Shaders/Sample.shader b/Assets/StableFluids/Shaders/Sample.shader new file mode 100644 index 0000000..0aa30fc --- /dev/null +++ b/Assets/StableFluids/Shaders/Sample.shader @@ -0,0 +1,54 @@ +Shader "StableFluid/Sample" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + } + SubShader + { + // No culling or depth + Cull Off ZWrite Off ZTest Always + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + v2f vert (appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + sampler2D _MainTex; + sampler2D SolverTex; + + fixed4 frag (v2f i) : SV_Target + { + fixed3 solver = tex2D(SolverTex, i.uv); + fixed4 col = tex2D(_MainTex, i.uv); + col *= 1 - solver.z * 5; + col.a = 1; + return col; + } + ENDCG + } + } +} diff --git a/Assets/StableFluids/Shaders/Sample.shader.meta b/Assets/StableFluids/Shaders/Sample.shader.meta new file mode 100644 index 0000000..49e24c1 --- /dev/null +++ b/Assets/StableFluids/Shaders/Sample.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 282dcb7cfb3e90142a89ad33496fa1e9 +timeCreated: 1499326984 +licenseType: Pro +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index 30e2007..07e7494 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,8 @@ UnityGraphicsProgramming - 雰囲気で始めるMarchingCubes入門 [@kaiware007](https://github.com/kaiware007) - https://github.com/IndieVisualLab/UnityGraphicsProgramming/tree/master/Assets/GPUMarchingCubes +- 格子法による流体シミュレーション [@sakope](https://github.com/sakope) - https://github.com/IndieVisualLab/UnityGraphicsProgramming/tree/master/Assets/StableFluids + - SPH法による流体シミュレーション [@kodai100](https://github.com/kodai100) - https://github.com/IndieVisualLab/UnityGraphicsProgramming/tree/master/Assets/SPHFluid + +- 群のシミュレーションのGPU実装 [@hiroakioishi](https://github.com/hiroakioishi) - https://github.com/IndieVisualLab/UnityGraphicsProgramming/tree/master/Assets/BoidsSimulationOnGPU