Add water system package
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
// Buoyancy.cs
|
||||
// by Alex Zhdankin
|
||||
// Version 2.1
|
||||
//
|
||||
// http://forum.unity3d.com/threads/72974-Buoyancy-script
|
||||
//
|
||||
// Terms of use: do whatever you like
|
||||
//
|
||||
// Further tweaks by Andre McGrail
|
||||
//
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
public class BuoyantObject : MonoBehaviour
|
||||
{
|
||||
public BuoyancyType _buoyancyType; // type of buoyancy to calculate
|
||||
public float density; // density of the object, this is calculated off it's volume and mass
|
||||
public float volume; // volume of the object, this is calculated via it's colliders
|
||||
public float voxelResolution = 0.51f; // voxel resolution, represents the half size of a voxel when creating the voxel representation
|
||||
private Bounds _voxelBounds; // bounds of the voxels
|
||||
public Vector3 centerOfMass = Vector3.zero; // Center Of Mass offset
|
||||
public float waterLevelOffset = 0f;
|
||||
|
||||
private const float Dampner = 0.005f;
|
||||
private const float WaterDensity = 1000;
|
||||
|
||||
private float _baseDrag; // reference to original drag
|
||||
private float _baseAngularDrag; // reference to original angular drag
|
||||
private int _guid; // GUID for the height system
|
||||
private float3 _localArchimedesForce;
|
||||
|
||||
private Vector3[] _voxels; // voxel position
|
||||
private NativeArray<float3> _samplePoints; // sample points for height calc
|
||||
[NonSerialized] public float3[] Heights; // water height array(only size of 1 when simple or non-physical)
|
||||
private float3[] _normals; // water normal array(only used when non-physical and size of 1 also when simple)
|
||||
private float3[] _velocity; // voxel velocity for buoyancy
|
||||
[SerializeField] Collider[] colliders; // colliders attatched ot this object
|
||||
private Rigidbody _rb;
|
||||
private DebugDrawing[] _debugInfo; // For drawing force gizmos
|
||||
[NonSerialized] public float PercentSubmerged;
|
||||
|
||||
[ContextMenu("Initialize")]
|
||||
private void Init()
|
||||
{
|
||||
_voxels = null;
|
||||
|
||||
switch (_buoyancyType)
|
||||
{
|
||||
case BuoyancyType.NonPhysical:
|
||||
SetupVoxels();
|
||||
SetupData();
|
||||
break;
|
||||
case BuoyancyType.NonPhysicalVoxel:
|
||||
SetupColliders();
|
||||
SetupVoxels();
|
||||
SetupData();
|
||||
break;
|
||||
case BuoyancyType.Physical:
|
||||
SetupVoxels();
|
||||
SetupData();
|
||||
SetupPhysical();
|
||||
break;
|
||||
case BuoyancyType.PhysicalVoxel:
|
||||
SetupColliders();
|
||||
SetupVoxels();
|
||||
SetupData();
|
||||
SetupPhysical();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupVoxels()
|
||||
{
|
||||
if (_buoyancyType == BuoyancyType.NonPhysicalVoxel || _buoyancyType == BuoyancyType.PhysicalVoxel)
|
||||
{
|
||||
SliceIntoVoxels();
|
||||
}
|
||||
else
|
||||
{
|
||||
_voxels = new Vector3[1];
|
||||
_voxels[0] = centerOfMass;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupData()
|
||||
{
|
||||
_debugInfo = new DebugDrawing[_voxels.Length];
|
||||
Heights = new float3[_voxels.Length];
|
||||
_normals = new float3[_voxels.Length];
|
||||
_samplePoints = new NativeArray<float3>(_voxels.Length, Allocator.Persistent);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_guid = gameObject.GetInstanceID();
|
||||
Init();
|
||||
LocalToWorldConversion();
|
||||
}
|
||||
|
||||
private void SetupColliders()
|
||||
{
|
||||
// The object must have a Collider
|
||||
colliders = GetComponentsInChildren<Collider>();
|
||||
if (colliders.Length != 0) return;
|
||||
|
||||
colliders = new Collider[1];
|
||||
colliders[0] = gameObject.AddComponent<BoxCollider>();
|
||||
Debug.LogError($"Buoyancy:Object \"{name}\" had no coll. BoxCollider has been added.");
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
#if STATIC_EVERYTHING
|
||||
var dt = 0.0f;
|
||||
#else
|
||||
var dt = Time.deltaTime;
|
||||
#endif
|
||||
switch (_buoyancyType)
|
||||
{
|
||||
case BuoyancyType.NonPhysical:
|
||||
{
|
||||
var t = transform;
|
||||
var vec = t.position;
|
||||
vec.y = Heights[0].y + waterLevelOffset;
|
||||
t.position = vec;
|
||||
t.up = Vector3.Slerp(t.up, _normals[0], dt);
|
||||
break;
|
||||
}
|
||||
case BuoyancyType.NonPhysicalVoxel:
|
||||
// do the voxel non-physical
|
||||
break;
|
||||
case BuoyancyType.Physical:
|
||||
LocalToWorldJob.CompleteJob(_guid);
|
||||
GetVelocityPoints();
|
||||
break;
|
||||
case BuoyancyType.PhysicalVoxel:
|
||||
LocalToWorldJob.CompleteJob(_guid);
|
||||
GetVelocityPoints();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
GerstnerWavesJobs.UpdateSamplePoints(ref _samplePoints, _guid);
|
||||
GerstnerWavesJobs.GetData(_guid, ref Heights, ref _normals);
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
var submergedAmount = 0f;
|
||||
|
||||
switch (_buoyancyType)
|
||||
{
|
||||
case BuoyancyType.PhysicalVoxel:
|
||||
{
|
||||
LocalToWorldJob.CompleteJob(_guid);
|
||||
//Debug.Log("new pass: " + gameObject.name);
|
||||
Physics.autoSyncTransforms = false;
|
||||
|
||||
for (var i = 0; i < _voxels.Length; i++)
|
||||
BuoyancyForce(_samplePoints[i], _velocity[i], Heights[i].y + waterLevelOffset, ref submergedAmount, ref _debugInfo[i]);
|
||||
Physics.SyncTransforms();
|
||||
Physics.autoSyncTransforms = true;
|
||||
UpdateDrag(submergedAmount);
|
||||
break;
|
||||
}
|
||||
case BuoyancyType.Physical:
|
||||
//LocalToWorldJob.CompleteJob(_guid);
|
||||
BuoyancyForce(Vector3.zero, _velocity[0], Heights[0].y + waterLevelOffset, ref submergedAmount, ref _debugInfo[0]);
|
||||
//UpdateDrag(submergedAmount);
|
||||
break;
|
||||
case BuoyancyType.NonPhysical:
|
||||
break;
|
||||
case BuoyancyType.NonPhysicalVoxel:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate() { LocalToWorldConversion(); }
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
void CleanUp()
|
||||
{
|
||||
if (_buoyancyType == BuoyancyType.Physical || _buoyancyType == BuoyancyType.PhysicalVoxel)
|
||||
{
|
||||
LocalToWorldJob.Cleanup(_guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_samplePoints.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void LocalToWorldConversion()
|
||||
{
|
||||
if (_buoyancyType != BuoyancyType.Physical && _buoyancyType != BuoyancyType.PhysicalVoxel) return;
|
||||
|
||||
var transformMatrix = transform.localToWorldMatrix;
|
||||
LocalToWorldJob.ScheduleJob(_guid, transformMatrix);
|
||||
}
|
||||
|
||||
private void BuoyancyForce(Vector3 position, float3 velocity, float waterHeight, ref float submergedAmount, ref DebugDrawing debug)
|
||||
{
|
||||
debug.Position = position;
|
||||
debug.WaterHeight = waterHeight;
|
||||
debug.Force = Vector3.zero;
|
||||
|
||||
if (!(position.y - voxelResolution < waterHeight)) return;
|
||||
|
||||
var k = math.clamp(waterHeight - (position.y - voxelResolution), 0f, 1f);
|
||||
|
||||
submergedAmount += k / _voxels.Length;
|
||||
|
||||
var localDampingForce = Dampner * _rb.mass * -velocity;
|
||||
var force = localDampingForce + math.sqrt(k) * _localArchimedesForce;
|
||||
_rb.AddForceAtPosition(force, position);
|
||||
|
||||
debug.Force = force; // For drawing force Gizmos
|
||||
//Debug.Log(string.Format("Position: {0:f1} -- Force: {1:f2} -- Height: {2:f2}\nVelocity: {3:f2} -- Damp: {4:f2} -- Mass: {5:f1} -- K: {6:f2}", wp, force, waterLevel, velocity, localDampingForce, RB.mass, localArchimedesForce));
|
||||
}
|
||||
|
||||
private void UpdateDrag(float submergedAmount)
|
||||
{
|
||||
PercentSubmerged = math.lerp(PercentSubmerged, submergedAmount, 0.25f);
|
||||
_rb.drag = _baseDrag + _baseDrag * (PercentSubmerged * 10f);
|
||||
_rb.angularDrag = _baseAngularDrag + PercentSubmerged * 0.5f;
|
||||
}
|
||||
|
||||
private void GetVelocityPoints()
|
||||
{
|
||||
for (var i = 0; i < _voxels.Length; i++) { _velocity[i] = _rb.GetPointVelocity(_samplePoints[i]); }
|
||||
}
|
||||
|
||||
private void SliceIntoVoxels()
|
||||
{
|
||||
var t = transform;
|
||||
var rot = t.rotation;
|
||||
var pos = t.position;
|
||||
var size = t.localScale;
|
||||
t.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
t.localScale = Vector3.one;
|
||||
|
||||
_voxels = null;
|
||||
var points = new List<Vector3>();
|
||||
|
||||
var rawBounds = VoxelBounds();
|
||||
_voxelBounds = rawBounds;
|
||||
_voxelBounds.size = RoundVector(rawBounds.size, voxelResolution);
|
||||
for (var ix = -_voxelBounds.extents.x; ix < _voxelBounds.extents.x; ix += voxelResolution)
|
||||
{
|
||||
for (var iy = -_voxelBounds.extents.y; iy < _voxelBounds.extents.y; iy += voxelResolution)
|
||||
{
|
||||
for (var iz = -_voxelBounds.extents.z; iz < _voxelBounds.extents.z; iz += voxelResolution)
|
||||
{
|
||||
var x = (voxelResolution * 0.5f) + ix;
|
||||
var y = (voxelResolution * 0.5f) + iy;
|
||||
var z = (voxelResolution * 0.5f) + iz;
|
||||
|
||||
var p = new Vector3(x, y, z) + _voxelBounds.center;
|
||||
|
||||
var inside = false;
|
||||
foreach (var t1 in colliders)
|
||||
{
|
||||
if (PointIsInsideCollider(t1, p))
|
||||
{
|
||||
inside = true;
|
||||
}
|
||||
}
|
||||
if(inside)
|
||||
points.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_voxels = points.ToArray();
|
||||
t.SetPositionAndRotation(pos, rot);
|
||||
t.localScale = size;
|
||||
var voxelVolume = Mathf.Pow(voxelResolution, 3f) * _voxels.Length;
|
||||
var rawVolume = rawBounds.size.x * rawBounds.size.y * rawBounds.size.z;
|
||||
volume = Mathf.Min(rawVolume, voxelVolume);
|
||||
density = gameObject.GetComponent<Rigidbody>().mass / volume;
|
||||
}
|
||||
|
||||
private Bounds VoxelBounds()
|
||||
{
|
||||
var bounds = new Bounds();
|
||||
foreach (var nextCollider in colliders)
|
||||
{
|
||||
bounds.Encapsulate(nextCollider.bounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private static Vector3 RoundVector(Vector3 vec, float rounding)
|
||||
{
|
||||
return new Vector3(Mathf.Ceil(vec.x / rounding) * rounding, Mathf.Ceil(vec.y / rounding) * rounding, Mathf.Ceil(vec.z / rounding) * rounding);
|
||||
}
|
||||
|
||||
private bool PointIsInsideCollider(Collider c, Vector3 p)
|
||||
{
|
||||
var cp = Physics.ClosestPoint(p, c, Vector3.zero, Quaternion.identity);
|
||||
return Vector3.Distance(cp, p) < 0.01f;
|
||||
}
|
||||
|
||||
private void SetupPhysical()
|
||||
{
|
||||
if (!TryGetComponent(out _rb))
|
||||
{
|
||||
_rb = gameObject.AddComponent<Rigidbody>();
|
||||
Debug.LogError($"Buoyancy:Object \"{name}\" had no Rigidbody. Rigidbody has been added.");
|
||||
}
|
||||
_rb.centerOfMass = centerOfMass + _voxelBounds.center;
|
||||
_baseDrag = _rb.drag;
|
||||
_baseAngularDrag = _rb.angularDrag;
|
||||
|
||||
_velocity = new float3[_voxels.Length];
|
||||
var archimedesForceMagnitude = WaterDensity * Mathf.Abs(Physics.gravity.y) * volume;
|
||||
_localArchimedesForce = new float3(0, archimedesForceMagnitude, 0) / _voxels.Length;
|
||||
LocalToWorldJob.SetupJob(_guid, _voxels, ref _samplePoints);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
const float gizmoSize = 0.05f;
|
||||
var t = transform;
|
||||
var matrix = Matrix4x4.TRS(t.position, t.rotation, t.lossyScale);
|
||||
|
||||
if (_voxels != null)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
|
||||
foreach (var p in _voxels)
|
||||
{
|
||||
Gizmos.DrawCube(p, new Vector3(gizmoSize, gizmoSize, gizmoSize));
|
||||
}
|
||||
}
|
||||
|
||||
Gizmos.matrix = matrix;
|
||||
if (voxelResolution >= 0.1f)
|
||||
{
|
||||
Gizmos.DrawWireCube(_voxelBounds.center, _voxelBounds.size);
|
||||
Vector3 center = _voxelBounds.center;
|
||||
float y = center.y - _voxelBounds.extents.y;
|
||||
for (float x = -_voxelBounds.extents.x; x < _voxelBounds.extents.x; x += voxelResolution)
|
||||
{
|
||||
Gizmos.DrawLine(new Vector3(x, y, -_voxelBounds.extents.z + center.z), new Vector3(x, y, _voxelBounds.extents.z + center.z));
|
||||
}
|
||||
for (float z = -_voxelBounds.extents.z; z < _voxelBounds.extents.z; z += voxelResolution)
|
||||
{
|
||||
Gizmos.DrawLine(new Vector3(-_voxelBounds.extents.x, y, z + center.z), new Vector3(_voxelBounds.extents.x, y, z + center.z));
|
||||
}
|
||||
}
|
||||
else
|
||||
_voxelBounds = VoxelBounds();
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawSphere(_voxelBounds.center + centerOfMass, 0.2f);
|
||||
|
||||
Gizmos.matrix = Matrix4x4.identity;Gizmos.matrix = Matrix4x4.identity;
|
||||
|
||||
if (_debugInfo != null)
|
||||
{
|
||||
foreach (DebugDrawing debug in _debugInfo)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawCube(debug.Position, new Vector3(gizmoSize, gizmoSize, gizmoSize)); // drawCenter
|
||||
var water = debug.Position;
|
||||
water.y = debug.WaterHeight;
|
||||
Gizmos.DrawLine(debug.Position, water); // draw the water line
|
||||
Gizmos.DrawSphere(water, gizmoSize * 4f);
|
||||
if(_buoyancyType == BuoyancyType.Physical || _buoyancyType == BuoyancyType.PhysicalVoxel)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawRay(debug.Position, debug.Force / _rb.mass); // draw force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private struct DebugDrawing
|
||||
{
|
||||
public Vector3 Force;
|
||||
public Vector3 Position;
|
||||
public float WaterHeight;
|
||||
}
|
||||
|
||||
public enum BuoyancyType
|
||||
{
|
||||
NonPhysical,
|
||||
NonPhysicalVoxel,
|
||||
Physical,
|
||||
PhysicalVoxel
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d5ac9593b59d4847815e4528b52a6d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a28afa3e85133492baf302b1d3849fe9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e73e41b71c5f541679f1bb37dee026c1, type: 3}
|
||||
m_Name: WaterResources
|
||||
m_EditorClassIdentifier:
|
||||
defaultFoamRamp: {fileID: 2800000, guid: 4098762bce32f44b9b7c56531d1a988e, type: 3}
|
||||
defaultFoamMap: {fileID: 2800000, guid: ca439406e806ecf44a9586510be9477d, type: 3}
|
||||
defaultSurfaceMap: {fileID: 2800000, guid: f395b7d64f44848c6a3cba1f1173fe90, type: 3}
|
||||
defaultSeaMaterial: {fileID: 2100000, guid: 4849d4d15a9cb42eb9533ae935e2630b, type: 2}
|
||||
defaultWaterMeshes:
|
||||
- {fileID: 4300036, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300050, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300038, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300048, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300040, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300046, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300042, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300044, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300026, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300024, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300022, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300020, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300018, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300014, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300012, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300006, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300004, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300034, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300032, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300030, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300028, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300010, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300016, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300008, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
- {fileID: 4300002, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40615c805d6e84ce7800418367985fe1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This scriptable object holds default resources for the water rendering
|
||||
/// </summary>
|
||||
[System.Serializable][CreateAssetMenu(fileName = "WaterResources", menuName = "WaterSystem/Resource", order = 0)]
|
||||
public class WaterResources : ScriptableObject
|
||||
{
|
||||
public Texture2D defaultFoamRamp; // a default foam ramp for the basic foam setting
|
||||
public Texture2D defaultFoamMap; // a default foam texture map
|
||||
public Texture2D defaultSurfaceMap; // a default normal/caustic map
|
||||
public Material defaultSeaMaterial;
|
||||
public Mesh[] defaultWaterMeshes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e73e41b71c5f541679f1bb37dee026c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 7ad544ee67a064d3eb407c8c47c67b8d, type: 3}
|
||||
m_Name: WaterSettingsData
|
||||
m_EditorClassIdentifier:
|
||||
waterGeomType: 0
|
||||
refType: 2
|
||||
planarSettings:
|
||||
m_ResolutionMultiplier: 1
|
||||
m_ClipPlaneOffset: 0.24
|
||||
m_ReflectLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_Shadows: 0
|
||||
cubemapRefType: {fileID: 8900000, guid: a90d3c9b1c3ed4d36bffc02f6b335464, type: 3}
|
||||
isInfinite: 0
|
||||
originOffset: {x: 0, y: 0, z: 500, w: 500}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4cacf74654e54b35b9eaea74fd20b4e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace WaterSystem.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// This scriptable object stores teh graphical/rendering settings for a water system
|
||||
/// </summary>
|
||||
[System.Serializable][CreateAssetMenu(fileName = "WaterSettingsData", menuName = "WaterSystem/Settings", order = 0)]
|
||||
public class WaterSettingsData : ScriptableObject
|
||||
{
|
||||
public GeometryType waterGeomType; // The type of geometry, either vertex offset or tessellation
|
||||
public ReflectionType refType = ReflectionType.PlanarReflection; // How the reflecitons are generated
|
||||
// planar
|
||||
public PlanarReflections.PlanarReflectionSettings planarSettings; // Planar reflection settings
|
||||
// cubemap
|
||||
public Cubemap cubemapRefType; // custom cubemap reference
|
||||
|
||||
public bool isInfinite; // Is the water infinite (shader incomplete)
|
||||
public Vector4 originOffset = new Vector4(0f, 0f, 500f, 500f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of reflection source, custom cubemap, closest refelction probe, planar reflection
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public enum ReflectionType
|
||||
{
|
||||
Cubemap,
|
||||
ReflectionProbe,
|
||||
PlanarReflection
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of geometry, either vertex offset or tessellation
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public enum GeometryType
|
||||
{
|
||||
VertexOffset,
|
||||
Tesselation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ad544ee67a064d3eb407c8c47c67b8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,258 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b17284ebf5e80436eb6089f8c7459dab, type: 3}
|
||||
m_Name: WaterSurfaceData
|
||||
m_EditorClassIdentifier:
|
||||
_waterMaxVisibility: 16.2
|
||||
_absorptionRamp:
|
||||
serializedVersion: 2
|
||||
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||
key1: {r: 0.18396229, g: 0.89963174, b: 1, a: 1}
|
||||
key2: {r: 0.021626905, g: 0.49048996, b: 0.509434, a: 0}
|
||||
key3: {r: 0, g: 0.275, b: 0.44, a: 0}
|
||||
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 6746
|
||||
ctime2: 20817
|
||||
ctime3: 43581
|
||||
ctime4: 65535
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_NumColorKeys: 5
|
||||
m_NumAlphaKeys: 2
|
||||
_scatterRamp:
|
||||
serializedVersion: 2
|
||||
key0: {r: 0, g: 0, b: 0, a: 1}
|
||||
key1: {r: 0.07843137, g: 0.24471705, b: 0.4117647, a: 1}
|
||||
key2: {r: 0.12549019, g: 0.50673026, b: 0.58431375, a: 0}
|
||||
key3: {r: 0.28888598, g: 0.45325178, b: 0.542, a: 0}
|
||||
key4: {r: 0.19571698, g: 0.3647036, b: 0.45099998, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 3277
|
||||
ctime2: 24672
|
||||
ctime3: 52043
|
||||
ctime4: 63029
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 65535
|
||||
atime2: 0
|
||||
atime3: 0
|
||||
atime4: 0
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_NumColorKeys: 4
|
||||
m_NumAlphaKeys: 2
|
||||
_waves:
|
||||
- amplitude: 1
|
||||
direction: -6.03
|
||||
wavelength: 10
|
||||
origin:
|
||||
x: -146.4
|
||||
y: 326.2
|
||||
onmiDir: 1
|
||||
- amplitude: 0.9
|
||||
direction: 166
|
||||
wavelength: 8
|
||||
origin:
|
||||
x: 0
|
||||
y: 0
|
||||
onmiDir: 0
|
||||
- amplitude: 0.5
|
||||
direction: 6.532788
|
||||
wavelength: 7.455916
|
||||
origin:
|
||||
x: -63.14
|
||||
y: 931.6
|
||||
onmiDir: 1
|
||||
- amplitude: 0.7
|
||||
direction: 4.051435
|
||||
wavelength: 4.6626315
|
||||
origin:
|
||||
x: -72.74
|
||||
y: -16.01
|
||||
onmiDir: 1
|
||||
- amplitude: 0.5
|
||||
direction: 180
|
||||
wavelength: 2.6733496
|
||||
origin:
|
||||
x: -50.43
|
||||
y: 0
|
||||
onmiDir: 0
|
||||
- amplitude: 0.5
|
||||
direction: -151
|
||||
wavelength: 2.9848163
|
||||
origin:
|
||||
x: 0
|
||||
y: 0
|
||||
onmiDir: 0
|
||||
_customWaves: 0
|
||||
randomSeed: 891
|
||||
_basicWaveSettings:
|
||||
numWaves: 6
|
||||
amplitude: 0.6
|
||||
direction: -176
|
||||
wavelength: 8
|
||||
_foamSettings:
|
||||
foamType: 1
|
||||
basicFoam:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0.16228558
|
||||
outSlope: 0.16228558
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0.15316528
|
||||
- serializedVersion: 3
|
||||
time: 0.6247411
|
||||
value: 0.39698213
|
||||
inSlope: 1.2551872
|
||||
outSlope: 1.2551872
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.28785437
|
||||
- serializedVersion: 3
|
||||
time: 0.8523668
|
||||
value: 0.81129223
|
||||
inSlope: 1.9929253
|
||||
outSlope: 1.9929253
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.27977753
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 1
|
||||
inSlope: 0.6611113
|
||||
outSlope: 0.6611113
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.55429333
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
liteFoam:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0.2
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 0.4
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 0.7
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
mediumFoam:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0.4
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 0.7
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
denseFoam:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0.7
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
_init: 1
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 846f3dc3b5341447a86687211f741bc7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WaterSystem.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// This scriptable object contains setting for how the water looks visually
|
||||
/// </summary>
|
||||
[System.Serializable][CreateAssetMenu(fileName = "WaterSurfaceData", menuName = "WaterSystem/Surface Data", order = 0)]
|
||||
public class WaterSurfaceData : ScriptableObject
|
||||
{
|
||||
public float _waterMaxVisibility = 40.0f;
|
||||
public Gradient _absorptionRamp;
|
||||
public Gradient _scatterRamp;
|
||||
public List<Wave> _waves = new List<Wave>();
|
||||
public bool _customWaves = false;
|
||||
public int randomSeed = 3234;
|
||||
public BasicWaves _basicWaveSettings = new BasicWaves(1.5f, 45.0f, 5.0f);
|
||||
public FoamSettings _foamSettings = new FoamSettings();
|
||||
[SerializeField]
|
||||
public bool _init = false;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct Wave
|
||||
{
|
||||
public float amplitude; // height of the wave in units(m)
|
||||
public float direction; // direction the wave travels in degrees from Z+
|
||||
public float wavelength; // distance between crest>crest
|
||||
public float2 origin; // Omi directional point of origin
|
||||
public float onmiDir; // Is omni?
|
||||
|
||||
public Wave(float amp, float dir, float length, float2 org, bool omni)
|
||||
{
|
||||
amplitude = amp;
|
||||
direction = dir;
|
||||
wavelength = length;
|
||||
origin = org;
|
||||
onmiDir = omni ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class BasicWaves
|
||||
{
|
||||
public int numWaves = 6;
|
||||
public float amplitude;
|
||||
public float direction;
|
||||
public float wavelength;
|
||||
|
||||
public BasicWaves(float amp, float dir, float len)
|
||||
{
|
||||
numWaves = 6;
|
||||
amplitude = amp;
|
||||
direction = dir;
|
||||
wavelength = len;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class FoamSettings
|
||||
{
|
||||
public int foamType; // 0=default, 1=simple, 3=custom
|
||||
public AnimationCurve basicFoam;
|
||||
public AnimationCurve liteFoam;
|
||||
public AnimationCurve mediumFoam;
|
||||
public AnimationCurve denseFoam;
|
||||
|
||||
// Foam curves
|
||||
public FoamSettings()
|
||||
{
|
||||
foamType = 0;
|
||||
basicFoam = new AnimationCurve(new Keyframe[2]{new Keyframe(0.25f, 0f),
|
||||
new Keyframe(1f, 1f)});
|
||||
liteFoam = new AnimationCurve(new Keyframe[3]{new Keyframe(0.2f, 0f),
|
||||
new Keyframe(0.4f, 1f),
|
||||
new Keyframe(0.7f, 0f)});
|
||||
mediumFoam = new AnimationCurve(new Keyframe[3]{new Keyframe(0.4f, 0f),
|
||||
new Keyframe(0.7f, 1f),
|
||||
new Keyframe(1f, 0f)});
|
||||
denseFoam = new AnimationCurve(new Keyframe[2]{new Keyframe(0.7f, 0f),
|
||||
new Keyframe(1f, 1f)});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b17284ebf5e80436eb6089f8c7459dab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,206 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Collections;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using WaterSystem.Data;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// C# Jobs system version of the Gerstner waves implimentation
|
||||
/// </summary>
|
||||
public static class GerstnerWavesJobs
|
||||
{
|
||||
//General variables
|
||||
public static bool Initialized;
|
||||
private static bool _firstFrame = true;
|
||||
private static bool _processing;
|
||||
private static int _waveCount;
|
||||
private static NativeArray<Wave> _waveData; // Wave data from the water system
|
||||
|
||||
//Details for Buoyant Objects
|
||||
private static NativeArray<float3> _positions;
|
||||
private static int _positionCount;
|
||||
private static NativeArray<float3> _wavePos;
|
||||
private static NativeArray<float3> _waveNormal;
|
||||
private static JobHandle _waterHeightHandle;
|
||||
static readonly Dictionary<int, int2> Registry = new Dictionary<int, int2>();
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if(Debug.isDebugBuild)
|
||||
Debug.Log("Initializing Gerstner Waves Jobs");
|
||||
//Wave data
|
||||
_waveCount = Water.Instance._waves.Length;
|
||||
_waveData = new NativeArray<Wave>(_waveCount, Allocator.Persistent);
|
||||
for (var i = 0; i < _waveData.Length; i++)
|
||||
{
|
||||
_waveData[i] = Water.Instance._waves[i];
|
||||
}
|
||||
|
||||
_positions = new NativeArray<float3>(4096, Allocator.Persistent);
|
||||
_wavePos = new NativeArray<float3>(4096, Allocator.Persistent);
|
||||
_waveNormal = new NativeArray<float3>(4096, Allocator.Persistent);
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
if(Debug.isDebugBuild)
|
||||
Debug.Log("Cleaning up Gerstner Wave Jobs");
|
||||
_waterHeightHandle.Complete();
|
||||
|
||||
//Cleanup native arrays
|
||||
_waveData.Dispose();
|
||||
_positions.Dispose();
|
||||
_wavePos.Dispose();
|
||||
_waveNormal.Dispose();
|
||||
}
|
||||
|
||||
public static void UpdateSamplePoints(ref NativeArray<float3> samplePoints, int guid)
|
||||
{
|
||||
CompleteJobs();
|
||||
|
||||
if (Registry.TryGetValue(guid, out var offsets))
|
||||
{
|
||||
for (var i = offsets.x; i < offsets.y; i++) _positions[i] = samplePoints[i - offsets.x];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_positionCount + samplePoints.Length >= _positions.Length) return;
|
||||
|
||||
offsets = new int2(_positionCount, _positionCount + samplePoints.Length);
|
||||
Registry.Add(guid, offsets);
|
||||
_positionCount += samplePoints.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetData(int guid, ref float3[] outPos, ref float3[] outNorm)
|
||||
{
|
||||
if (!Registry.TryGetValue(guid, out var offsets)) return;
|
||||
|
||||
_wavePos.Slice(offsets.x, offsets.y - offsets.x).CopyTo(outPos);
|
||||
if(outNorm != null)
|
||||
_waveNormal.Slice(offsets.x, offsets.y - offsets.x).CopyTo(outNorm);
|
||||
}
|
||||
|
||||
// Height jobs for the next frame
|
||||
public static void UpdateHeights()
|
||||
{
|
||||
if (_processing) return;
|
||||
|
||||
_processing = true;
|
||||
|
||||
#if STATIC_EVERYTHING
|
||||
var t = 0.0f;
|
||||
#else
|
||||
var t = Time.time;
|
||||
#endif
|
||||
|
||||
// Buoyant Object Job
|
||||
var waterHeight = new HeightJob()
|
||||
{
|
||||
WaveData = _waveData,
|
||||
Position = _positions,
|
||||
OffsetLength = new int2(0, _positions.Length),
|
||||
Time = t,
|
||||
OutPosition = _wavePos,
|
||||
OutNormal = _waveNormal
|
||||
};
|
||||
|
||||
_waterHeightHandle = waterHeight.Schedule(_positionCount, 32);
|
||||
|
||||
JobHandle.ScheduleBatchedJobs();
|
||||
|
||||
_firstFrame = false;
|
||||
}
|
||||
|
||||
private static void CompleteJobs()
|
||||
{
|
||||
if (_firstFrame || !_processing) return;
|
||||
|
||||
_waterHeightHandle.Complete();
|
||||
_processing = false;
|
||||
}
|
||||
|
||||
// Gerstner Height C# Job
|
||||
[BurstCompile]
|
||||
private struct HeightJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeArray<Wave> WaveData; // wave data stroed in vec4's like the shader version but packed into one
|
||||
[ReadOnly]
|
||||
public NativeArray<float3> Position;
|
||||
|
||||
[WriteOnly]
|
||||
public NativeArray<float3> OutPosition;
|
||||
[WriteOnly]
|
||||
public NativeArray<float3> OutNormal;
|
||||
|
||||
[ReadOnly]
|
||||
public float Time;
|
||||
[ReadOnly]
|
||||
public int2 OffsetLength;
|
||||
|
||||
// The code actually running on the job
|
||||
public void Execute(int i)
|
||||
{
|
||||
if (i < OffsetLength.x || i >= OffsetLength.y - OffsetLength.x) return;
|
||||
|
||||
var waveCountMulti = 1f / WaveData.Length;
|
||||
var wavePos = new float3(0f, 0f, 0f);
|
||||
var waveNorm = new float3(0f, 0f, 0f);
|
||||
|
||||
for (var wave = 0; wave < WaveData.Length; wave++) // for each wave
|
||||
{
|
||||
// Wave data vars
|
||||
var pos = Position[i].xz;
|
||||
|
||||
var amplitude = WaveData[wave].amplitude;
|
||||
var direction = WaveData[wave].direction;
|
||||
var wavelength = WaveData[wave].wavelength;
|
||||
var omniPos = WaveData[wave].origin;
|
||||
////////////////////////////////wave value calculations//////////////////////////
|
||||
var w = 6.28318f / wavelength; // 2pi over wavelength(hardcoded)
|
||||
var wSpeed = math.sqrt(9.8f * w); // frequency of the wave based off wavelength
|
||||
const float peak = 0.8f; // peak value, 1 is the sharpest peaks
|
||||
var qi = peak / (amplitude * w * WaveData.Length);
|
||||
|
||||
var windDir = new float2(0f, 0f);
|
||||
|
||||
direction = math.radians(direction); // convert the incoming degrees to radians
|
||||
var windDirInput = new float2(math.sin(direction), math.cos(direction)) * (1 - WaveData[wave].onmiDir); // calculate wind direction - TODO - currently radians
|
||||
var windOmniInput = (pos - omniPos) * WaveData[wave].onmiDir;
|
||||
|
||||
windDir += windDirInput;
|
||||
windDir += windOmniInput;
|
||||
windDir = math.normalize(windDir);
|
||||
var dir = math.dot(windDir, pos - (omniPos * WaveData[wave].onmiDir));
|
||||
|
||||
////////////////////////////position output calculations/////////////////////////
|
||||
var calc = dir * w + -Time * wSpeed; // the wave calculation
|
||||
var cosCalc = math.cos(calc); // cosine version(used for horizontal undulation)
|
||||
var sinCalc = math.sin(calc); // sin version(used for vertical undulation)
|
||||
|
||||
// calculate the offsets for the current point
|
||||
wavePos.x += qi * amplitude * windDir.x * cosCalc;
|
||||
wavePos.z += qi * amplitude * windDir.y * cosCalc;
|
||||
wavePos.y += sinCalc * amplitude * waveCountMulti; // the height is divided by the number of waves
|
||||
|
||||
////////////////////////////normal output calculations/////////////////////////
|
||||
var wa = w * amplitude;
|
||||
// normal vector
|
||||
var norm = new float3(-(windDir.xy * wa * cosCalc),
|
||||
1 - (qi * wa * sinCalc));
|
||||
waveNorm += (norm * waveCountMulti) * amplitude;
|
||||
}
|
||||
OutPosition[i] = wavePos;
|
||||
OutNormal[i] = math.normalize(waveNorm.xzy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e09f45dd7e088412e85c5924433ad4ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
using Unity.Mathematics;
|
||||
|
||||
public static class LocalToWorldJob
|
||||
{
|
||||
private static readonly Dictionary<int, TransformLocalToWorld> Data = new Dictionary<int, TransformLocalToWorld>();
|
||||
|
||||
[BurstCompile]
|
||||
struct LocalToWorldConvertJob : IJob
|
||||
{
|
||||
[WriteOnly] public NativeArray<float3> PositionsWorld;
|
||||
[ReadOnly] public Matrix4x4 Matrix;
|
||||
[ReadOnly] public NativeArray<float3> PositionsLocal;
|
||||
|
||||
// The code actually running on the job
|
||||
public void Execute()
|
||||
{
|
||||
for (var i = 0; i < PositionsLocal.Length; i++)
|
||||
{
|
||||
var pos = float4.zero;
|
||||
pos.xyz = PositionsLocal[i];
|
||||
pos.w = 1f;
|
||||
pos = Matrix * pos;
|
||||
PositionsWorld[i] = pos.xyz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupJob(int guid, Vector3[] positions, ref NativeArray<float3> output)
|
||||
{
|
||||
var jobData = new TransformLocalToWorld
|
||||
{
|
||||
PositionsWorld = output,
|
||||
PositionsLocal = new NativeArray<float3>(positions.Length, Allocator.Persistent)
|
||||
};
|
||||
|
||||
for (var i = 0; i < positions.Length; i++)
|
||||
jobData.PositionsLocal[i] = positions[i];
|
||||
|
||||
Data.Add(guid, jobData);
|
||||
}
|
||||
|
||||
public static void ScheduleJob(int guid, Matrix4x4 localToWorld)
|
||||
{
|
||||
if (Data[guid].Processing)
|
||||
return;
|
||||
|
||||
Data[guid].Job = new LocalToWorldConvertJob()
|
||||
{
|
||||
PositionsWorld = Data[guid].PositionsWorld,
|
||||
PositionsLocal = Data[guid].PositionsLocal,
|
||||
Matrix = localToWorld
|
||||
};
|
||||
|
||||
Data[guid].Handle = Data[guid].Job.Schedule();
|
||||
Data[guid].Processing = true;
|
||||
JobHandle.ScheduleBatchedJobs();
|
||||
}
|
||||
|
||||
public static void CompleteJob(int guid)
|
||||
{
|
||||
Data[guid].Handle.Complete();
|
||||
Data[guid].Processing = false;
|
||||
}
|
||||
|
||||
public static void Cleanup(int guid)
|
||||
{
|
||||
if (!Data.ContainsKey(guid)) return;
|
||||
Data[guid].Handle.Complete();
|
||||
Data[guid].PositionsWorld.Dispose();
|
||||
Data[guid].PositionsLocal.Dispose();
|
||||
Data.Remove(guid);
|
||||
}
|
||||
|
||||
class TransformLocalToWorld
|
||||
{
|
||||
public NativeArray<float3> PositionsLocal;
|
||||
public NativeArray<float3> PositionsWorld;
|
||||
public JobHandle Handle;
|
||||
public LocalToWorldConvertJob Job;
|
||||
public bool Processing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3813a158650064768b37058dc089214a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Camera script to align the water mesh with the camera in a quantized manner
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
public class MainCameraAlign : MonoBehaviour
|
||||
{
|
||||
|
||||
public float quantizeValue = 6.25f;
|
||||
public float forwards = 10f;
|
||||
public float yOffset = -0.25f;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering += UpdatePosition;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering -= UpdatePosition;
|
||||
}
|
||||
|
||||
private void UpdatePosition(ScriptableRenderContext src, Camera cam)
|
||||
{
|
||||
if (cam.cameraType == CameraType.Preview) return;
|
||||
|
||||
var newPos = cam.transform.TransformPoint(Vector3.forward * forwards);
|
||||
newPos.y = yOffset;
|
||||
newPos.x = QuantizeValue(newPos.x);
|
||||
newPos.z = QuantizeValue(newPos.z);
|
||||
transform.position = newPos;
|
||||
}
|
||||
|
||||
private float QuantizeValue(float value)
|
||||
{
|
||||
return quantizeValue * (int) (value / quantizeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a6b4e1e324e641ff86ca879104e20de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ceb62181994bc49eb8ac45fd7f5c4634
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Serialization;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
[ExecuteAlways]
|
||||
public class PlanarReflections : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public enum ResolutionMulltiplier
|
||||
{
|
||||
Full,
|
||||
Half,
|
||||
Third,
|
||||
Quarter
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PlanarReflectionSettings
|
||||
{
|
||||
public ResolutionMulltiplier m_ResolutionMultiplier = ResolutionMulltiplier.Third;
|
||||
public float m_ClipPlaneOffset = 0.07f;
|
||||
public LayerMask m_ReflectLayers = -1;
|
||||
public bool m_Shadows;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public PlanarReflectionSettings m_settings = new PlanarReflectionSettings();
|
||||
|
||||
public GameObject target;
|
||||
[FormerlySerializedAs("camOffset")] public float m_planeOffset;
|
||||
|
||||
private static Camera _reflectionCamera;
|
||||
private RenderTexture _reflectionTexture;
|
||||
private readonly int _planarReflectionTextureId = Shader.PropertyToID("_PlanarReflectionTexture");
|
||||
|
||||
private int2 _oldReflectionTextureSize;
|
||||
|
||||
public static event Action<ScriptableRenderContext, Camera> BeginPlanarReflections;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering += ExecutePlanarReflections;
|
||||
}
|
||||
|
||||
// Cleanup all the objects we possibly have created
|
||||
private void OnDisable()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
private void Cleanup()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering -= ExecutePlanarReflections;
|
||||
|
||||
if(_reflectionCamera)
|
||||
{
|
||||
_reflectionCamera.targetTexture = null;
|
||||
SafeDestroy(_reflectionCamera.gameObject);
|
||||
}
|
||||
if (_reflectionTexture)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(_reflectionTexture);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SafeDestroy(Object obj)
|
||||
{
|
||||
if (Application.isEditor)
|
||||
{
|
||||
DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCamera(Camera src, Camera dest)
|
||||
{
|
||||
if (dest == null) return;
|
||||
|
||||
dest.CopyFrom(src);
|
||||
dest.useOcclusionCulling = false;
|
||||
if (dest.gameObject.TryGetComponent(out UniversalAdditionalCameraData camData))
|
||||
{
|
||||
camData.renderShadows = m_settings.m_Shadows; // turn off shadows for the reflection camera
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateReflectionCamera(Camera realCamera)
|
||||
{
|
||||
if (_reflectionCamera == null)
|
||||
_reflectionCamera = CreateMirrorObjects();
|
||||
|
||||
// find out the reflection plane: position and normal in world space
|
||||
Vector3 pos = Vector3.zero;
|
||||
Vector3 normal = Vector3.up;
|
||||
if (target != null)
|
||||
{
|
||||
pos = target.transform.position + Vector3.up * m_planeOffset;
|
||||
normal = target.transform.up;
|
||||
}
|
||||
|
||||
UpdateCamera(realCamera, _reflectionCamera);
|
||||
|
||||
// Render reflection
|
||||
// Reflect camera around reflection plane
|
||||
var d = -Vector3.Dot(normal, pos) - m_settings.m_ClipPlaneOffset;
|
||||
var reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
|
||||
|
||||
var reflection = Matrix4x4.identity;
|
||||
reflection *= Matrix4x4.Scale(new Vector3(1, -1, 1));
|
||||
|
||||
CalculateReflectionMatrix(ref reflection, reflectionPlane);
|
||||
var oldPosition = realCamera.transform.position - new Vector3(0, pos.y * 2, 0);
|
||||
var newPosition = ReflectPosition(oldPosition);
|
||||
_reflectionCamera.transform.forward = Vector3.Scale(realCamera.transform.forward, new Vector3(1, -1, 1));
|
||||
_reflectionCamera.worldToCameraMatrix = realCamera.worldToCameraMatrix * reflection;
|
||||
|
||||
// Setup oblique projection matrix so that near plane is our reflection
|
||||
// plane. This way we clip everything below/above it for free.
|
||||
var clipPlane = CameraSpacePlane(_reflectionCamera, pos - Vector3.up * 0.1f, normal, 1.0f);
|
||||
var projection = realCamera.CalculateObliqueMatrix(clipPlane);
|
||||
_reflectionCamera.projectionMatrix = projection;
|
||||
_reflectionCamera.cullingMask = m_settings.m_ReflectLayers; // never render water layer
|
||||
_reflectionCamera.transform.position = newPosition;
|
||||
}
|
||||
|
||||
// Calculates reflection matrix around the given plane
|
||||
private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
|
||||
{
|
||||
reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
|
||||
reflectionMat.m01 = (-2F * plane[0] * plane[1]);
|
||||
reflectionMat.m02 = (-2F * plane[0] * plane[2]);
|
||||
reflectionMat.m03 = (-2F * plane[3] * plane[0]);
|
||||
|
||||
reflectionMat.m10 = (-2F * plane[1] * plane[0]);
|
||||
reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
|
||||
reflectionMat.m12 = (-2F * plane[1] * plane[2]);
|
||||
reflectionMat.m13 = (-2F * plane[3] * plane[1]);
|
||||
|
||||
reflectionMat.m20 = (-2F * plane[2] * plane[0]);
|
||||
reflectionMat.m21 = (-2F * plane[2] * plane[1]);
|
||||
reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
|
||||
reflectionMat.m23 = (-2F * plane[3] * plane[2]);
|
||||
|
||||
reflectionMat.m30 = 0F;
|
||||
reflectionMat.m31 = 0F;
|
||||
reflectionMat.m32 = 0F;
|
||||
reflectionMat.m33 = 1F;
|
||||
}
|
||||
|
||||
private static Vector3 ReflectPosition(Vector3 pos)
|
||||
{
|
||||
var newPos = new Vector3(pos.x, -pos.y, pos.z);
|
||||
return newPos;
|
||||
}
|
||||
|
||||
private float GetScaleValue()
|
||||
{
|
||||
switch(m_settings.m_ResolutionMultiplier)
|
||||
{
|
||||
case ResolutionMulltiplier.Full:
|
||||
return 1f;
|
||||
case ResolutionMulltiplier.Half:
|
||||
return 0.5f;
|
||||
case ResolutionMulltiplier.Third:
|
||||
return 0.33f;
|
||||
case ResolutionMulltiplier.Quarter:
|
||||
return 0.25f;
|
||||
default:
|
||||
return 0.5f; // default to half res
|
||||
}
|
||||
}
|
||||
|
||||
// Compare two int2
|
||||
private static bool Int2Compare(int2 a, int2 b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
// Given position/normal of the plane, calculates plane in camera space.
|
||||
private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
|
||||
{
|
||||
var offsetPos = pos + normal * m_settings.m_ClipPlaneOffset;
|
||||
var m = cam.worldToCameraMatrix;
|
||||
var cameraPosition = m.MultiplyPoint(offsetPos);
|
||||
var cameraNormal = m.MultiplyVector(normal).normalized * sideSign;
|
||||
return new Vector4(cameraNormal.x, cameraNormal.y, cameraNormal.z, -Vector3.Dot(cameraPosition, cameraNormal));
|
||||
}
|
||||
|
||||
private Camera CreateMirrorObjects()
|
||||
{
|
||||
var go = new GameObject("Planar Reflections",typeof(Camera));
|
||||
var cameraData = go.AddComponent(typeof(UniversalAdditionalCameraData)) as UniversalAdditionalCameraData;
|
||||
|
||||
cameraData.requiresColorOption = CameraOverrideOption.Off;
|
||||
cameraData.requiresDepthOption = CameraOverrideOption.Off;
|
||||
cameraData.SetRenderer(1);
|
||||
|
||||
var t = transform;
|
||||
var reflectionCamera = go.GetComponent<Camera>();
|
||||
reflectionCamera.transform.SetPositionAndRotation(t.position, t.rotation);
|
||||
reflectionCamera.depth = -10;
|
||||
reflectionCamera.enabled = false;
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
return reflectionCamera;
|
||||
}
|
||||
|
||||
private void PlanarReflectionTexture(Camera cam)
|
||||
{
|
||||
if (_reflectionTexture == null)
|
||||
{
|
||||
var res = ReflectionResolution(cam, UniversalRenderPipeline.asset.renderScale);
|
||||
bool useHdr10 = RenderingUtils.SupportsRenderTextureFormat(RenderTextureFormat.RGB111110Float);
|
||||
RenderTextureFormat hdrFormat = useHdr10 ? RenderTextureFormat.RGB111110Float : RenderTextureFormat.DefaultHDR;
|
||||
_reflectionTexture = RenderTexture.GetTemporary(res.x, res.y, 16,
|
||||
GraphicsFormatUtility.GetGraphicsFormat(hdrFormat, true));
|
||||
}
|
||||
_reflectionCamera.targetTexture = _reflectionTexture;
|
||||
}
|
||||
|
||||
private int2 ReflectionResolution(Camera cam, float scale)
|
||||
{
|
||||
var x = (int)(cam.pixelWidth * scale * GetScaleValue());
|
||||
var y = (int)(cam.pixelHeight * scale * GetScaleValue());
|
||||
return new int2(x, y);
|
||||
}
|
||||
|
||||
private void ExecutePlanarReflections(ScriptableRenderContext context, Camera camera)
|
||||
{
|
||||
// we dont want to render planar reflections in reflections or previews
|
||||
if (camera.cameraType == CameraType.Reflection || camera.cameraType == CameraType.Preview)
|
||||
return;
|
||||
|
||||
UpdateReflectionCamera(camera); // create reflected camera
|
||||
PlanarReflectionTexture(camera); // create and assign RenderTexture
|
||||
|
||||
var data = new PlanarReflectionSettingData(); // save quality settings and lower them for the planar reflections
|
||||
data.Set(); // set quality settings
|
||||
|
||||
Shader.EnableKeyword("_PLANAR_REFLECTION_CAMERA");
|
||||
|
||||
BeginPlanarReflections?.Invoke(context, _reflectionCamera); // callback Action for PlanarReflection
|
||||
UniversalRenderPipeline.RenderSingleCamera(context, _reflectionCamera); // render planar reflections
|
||||
|
||||
data.Restore(); // restore the quality settings
|
||||
Shader.SetGlobalTexture(_planarReflectionTextureId, _reflectionTexture); // Assign texture to water shader
|
||||
Shader.DisableKeyword("_PLANAR_REFLECTION_CAMERA");
|
||||
}
|
||||
|
||||
class PlanarReflectionSettingData
|
||||
{
|
||||
private readonly bool _fog;
|
||||
private readonly int _maxLod;
|
||||
private readonly float _lodBias;
|
||||
|
||||
public PlanarReflectionSettingData()
|
||||
{
|
||||
_fog = RenderSettings.fog;
|
||||
_maxLod = QualitySettings.maximumLODLevel;
|
||||
_lodBias = QualitySettings.lodBias;
|
||||
}
|
||||
|
||||
public void Set()
|
||||
{
|
||||
GL.invertCulling = true;
|
||||
RenderSettings.fog = false; // disable fog for now as it's incorrect with projection
|
||||
QualitySettings.maximumLODLevel = 1;
|
||||
QualitySettings.lodBias = _lodBias * 0.5f;
|
||||
}
|
||||
|
||||
public void Restore()
|
||||
{
|
||||
GL.invertCulling = false;
|
||||
RenderSettings.fog = _fog;
|
||||
QualitySettings.maximumLODLevel = _maxLod;
|
||||
QualitySettings.lodBias = _lodBias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3971d89a730cd5846a1944ab0228bf76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,234 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
public class WaterSystemFeature : ScriptableRendererFeature
|
||||
{
|
||||
|
||||
#region Water Effects Pass
|
||||
|
||||
class WaterFxPass : ScriptableRenderPass
|
||||
{
|
||||
private const string k_RenderWaterFXTag = "Render Water FX";
|
||||
private ProfilingSampler m_WaterFX_Profile = new ProfilingSampler(k_RenderWaterFXTag);
|
||||
private readonly ShaderTagId m_WaterFXShaderTag = new ShaderTagId("WaterFX");
|
||||
private readonly Color m_ClearColor = new Color(0.0f, 0.5f, 0.5f, 0.5f); //r = foam mask, g = normal.x, b = normal.z, a = displacement
|
||||
private FilteringSettings m_FilteringSettings;
|
||||
private RTHandle m_WaterFX;
|
||||
|
||||
public WaterFxPass()
|
||||
{
|
||||
m_WaterFX=RTHandles.Alloc("_WaterFXMap", "_WaterFXMap");
|
||||
// only wanting to render transparent objects
|
||||
m_FilteringSettings = new FilteringSettings(RenderQueueRange.transparent);
|
||||
}
|
||||
|
||||
// Calling Configure since we are wanting to render into a RenderTexture and control cleat
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
// no need for a depth buffer
|
||||
cameraTextureDescriptor.depthBufferBits = 0;
|
||||
// Half resolution
|
||||
cameraTextureDescriptor.width /= 2;
|
||||
cameraTextureDescriptor.height /= 2;
|
||||
// default format TODO research usefulness of HDR format
|
||||
cameraTextureDescriptor.colorFormat = RenderTextureFormat.Default;
|
||||
// get a temp RT for rendering into
|
||||
cmd.GetTemporaryRT(Shader.PropertyToID(m_WaterFX.name), cameraTextureDescriptor, FilterMode.Bilinear);
|
||||
ConfigureTarget(m_WaterFX);
|
||||
// clear the screen with a specific color for the packed data
|
||||
ConfigureClear(ClearFlag.Color, m_ClearColor);
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_WaterFX_Profile)) // makes sure we have profiling ability
|
||||
{
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
// here we choose renderers based off the "WaterFX" shader pass and also sort back to front
|
||||
var drawSettings = CreateDrawingSettings(m_WaterFXShaderTag, ref renderingData,
|
||||
SortingCriteria.CommonTransparent);
|
||||
|
||||
// draw all the renderers matching the rules we setup
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
// since the texture is used within the single cameras use we need to cleanup the RT afterwards
|
||||
cmd.ReleaseTemporaryRT(Shader.PropertyToID(m_WaterFX.name));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Caustics Pass
|
||||
|
||||
class WaterCausticsPass : ScriptableRenderPass
|
||||
{
|
||||
private const string k_RenderWaterCausticsTag = "Render Water Caustics";
|
||||
private ProfilingSampler m_WaterCaustics_Profile = new ProfilingSampler(k_RenderWaterCausticsTag);
|
||||
public Material WaterCausticMaterial;
|
||||
private static Mesh m_mesh;
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
var cam = renderingData.cameraData.camera;
|
||||
// Stop the pass rendering in the preview or material missing
|
||||
if (cam.cameraType == CameraType.Preview || !WaterCausticMaterial)
|
||||
return;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_WaterCaustics_Profile))
|
||||
{
|
||||
var sunMatrix = RenderSettings.sun != null
|
||||
? RenderSettings.sun.transform.localToWorldMatrix
|
||||
: Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(-45f, 45f, 0f), Vector3.one);
|
||||
WaterCausticMaterial.SetMatrix("_MainLightDir", sunMatrix);
|
||||
|
||||
|
||||
// Create mesh if needed
|
||||
if (!m_mesh)
|
||||
m_mesh = GenerateCausticsMesh(1000f);
|
||||
|
||||
// Create the matrix to position the caustics mesh.
|
||||
var position = cam.transform.position;
|
||||
position.y = 0; // TODO should read a global 'water height' variable.
|
||||
var matrix = Matrix4x4.TRS(position, Quaternion.identity, Vector3.one);
|
||||
// Setup the CommandBuffer and draw the mesh with the caustic material and matrix
|
||||
cmd.DrawMesh(m_mesh, matrix, WaterCausticMaterial, 0, 0);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
WaterFxPass m_WaterFxPass;
|
||||
WaterCausticsPass m_CausticsPass;
|
||||
|
||||
public WaterSystemSettings settings = new WaterSystemSettings();
|
||||
[HideInInspector][SerializeField] private Shader causticShader;
|
||||
[HideInInspector][SerializeField] private Texture2D causticTexture;
|
||||
|
||||
private Material _causticMaterial;
|
||||
|
||||
private static readonly int SrcBlend = Shader.PropertyToID("_SrcBlend");
|
||||
private static readonly int DstBlend = Shader.PropertyToID("_DstBlend");
|
||||
private static readonly int Size = Shader.PropertyToID("_Size");
|
||||
private static readonly int CausticTexture = Shader.PropertyToID("_CausticMap");
|
||||
|
||||
public override void Create()
|
||||
{
|
||||
// WaterFX Pass
|
||||
m_WaterFxPass = new WaterFxPass {renderPassEvent = RenderPassEvent.BeforeRenderingOpaques};
|
||||
|
||||
// Caustic Pass
|
||||
m_CausticsPass = new WaterCausticsPass();
|
||||
|
||||
causticShader = causticShader ? causticShader : Shader.Find("Hidden/BoatAttack/Caustics");
|
||||
if (causticShader == null) return;
|
||||
if (_causticMaterial)
|
||||
{
|
||||
DestroyImmediate(_causticMaterial);
|
||||
}
|
||||
_causticMaterial = CoreUtils.CreateEngineMaterial(causticShader);
|
||||
_causticMaterial.SetFloat("_BlendDistance", settings.causticBlendDistance);
|
||||
|
||||
if (causticTexture == null)
|
||||
{
|
||||
Debug.Log("Caustics Texture missing, attempting to load.");
|
||||
#if UNITY_EDITOR
|
||||
causticTexture = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.verasl.water-system/Textures/WaterSurface_single.tif");
|
||||
#endif
|
||||
}
|
||||
_causticMaterial.SetTexture(CausticTexture, causticTexture);
|
||||
|
||||
switch (settings.debug)
|
||||
{
|
||||
case WaterSystemSettings.DebugMode.Caustics:
|
||||
_causticMaterial.SetFloat(SrcBlend, 1f);
|
||||
_causticMaterial.SetFloat(DstBlend, 0f);
|
||||
_causticMaterial.EnableKeyword("_DEBUG");
|
||||
m_CausticsPass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
|
||||
break;
|
||||
case WaterSystemSettings.DebugMode.WaterEffects:
|
||||
break;
|
||||
case WaterSystemSettings.DebugMode.Disabled:
|
||||
// Caustics
|
||||
_causticMaterial.SetFloat(SrcBlend, 2f);
|
||||
_causticMaterial.SetFloat(DstBlend, 0f);
|
||||
_causticMaterial.DisableKeyword("_DEBUG");
|
||||
m_CausticsPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox + 1;
|
||||
// WaterEffects
|
||||
break;
|
||||
}
|
||||
|
||||
_causticMaterial.SetFloat(Size, settings.causticScale);
|
||||
m_CausticsPass.WaterCausticMaterial = _causticMaterial;
|
||||
}
|
||||
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
renderer.EnqueuePass(m_WaterFxPass);
|
||||
renderer.EnqueuePass(m_CausticsPass);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function Generates a flat quad for use with the caustics pass.
|
||||
/// </summary>
|
||||
/// <param name="size">The length of the quad.</param>
|
||||
/// <returns></returns>
|
||||
private static Mesh GenerateCausticsMesh(float size)
|
||||
{
|
||||
var m = new Mesh();
|
||||
size *= 0.5f;
|
||||
|
||||
var verts = new[]
|
||||
{
|
||||
new Vector3(-size, 0f, -size),
|
||||
new Vector3(size, 0f, -size),
|
||||
new Vector3(-size, 0f, size),
|
||||
new Vector3(size, 0f, size)
|
||||
};
|
||||
m.vertices = verts;
|
||||
|
||||
var tris = new[]
|
||||
{
|
||||
0, 2, 1,
|
||||
2, 3, 1
|
||||
};
|
||||
m.triangles = tris;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class WaterSystemSettings
|
||||
{
|
||||
[Header("Caustics Settings")] [Range(0.1f, 1f)]
|
||||
public float causticScale = 0.25f;
|
||||
|
||||
public float causticBlendDistance = 3f;
|
||||
|
||||
[Header("Advanced Settings")] public DebugMode debug = DebugMode.Disabled;
|
||||
|
||||
public enum DebugMode
|
||||
{
|
||||
Disabled,
|
||||
WaterEffects,
|
||||
Caustics
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adbe377f4f72c497faee64bc3f0580b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- causticShader: {fileID: 4800000, guid: 90939d2e4b62841d29c136c866715501, type: 3}
|
||||
- causticTexture: {fileID: 2800000, guid: f395b7d64f44848c6a3cba1f1173fe90, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,402 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using WaterSystem.Data;
|
||||
using Object = UnityEngine.Object;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace WaterSystem
|
||||
{
|
||||
[ExecuteAlways]
|
||||
public class Water : MonoBehaviour
|
||||
{
|
||||
// Singleton
|
||||
private static Water _instance;
|
||||
public static Water Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = (Water)FindObjectOfType(typeof(Water));
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
// Script references
|
||||
private PlanarReflections _planarReflections;
|
||||
|
||||
private bool _useComputeBuffer;
|
||||
public bool computeOverride;
|
||||
|
||||
[SerializeField] RenderTexture _depthTex;
|
||||
public Texture bakedDepthTex;
|
||||
private Camera _depthCam;
|
||||
private Texture2D _rampTexture;
|
||||
[SerializeField]
|
||||
public Wave[] _waves;
|
||||
[SerializeField]
|
||||
private ComputeBuffer waveBuffer;
|
||||
private float _maxWaveHeight;
|
||||
private float _waveHeight;
|
||||
|
||||
[SerializeField]
|
||||
public WaterSettingsData settingsData;
|
||||
[SerializeField]
|
||||
public WaterSurfaceData surfaceData;
|
||||
[SerializeField]
|
||||
private WaterResources resources;
|
||||
|
||||
private static readonly int CameraRoll = Shader.PropertyToID("_CameraRoll");
|
||||
private static readonly int InvViewProjection = Shader.PropertyToID("_InvViewProjection");
|
||||
private static readonly int WaterDepthMap = Shader.PropertyToID("_WaterDepthMap");
|
||||
private static readonly int FoamMap = Shader.PropertyToID("_FoamMap");
|
||||
private static readonly int SurfaceMap = Shader.PropertyToID("_SurfaceMap");
|
||||
private static readonly int WaveHeight = Shader.PropertyToID("_WaveHeight");
|
||||
private static readonly int MaxWaveHeight = Shader.PropertyToID("_MaxWaveHeight");
|
||||
private static readonly int MaxDepth = Shader.PropertyToID("_MaxDepth");
|
||||
private static readonly int WaveCount = Shader.PropertyToID("_WaveCount");
|
||||
private static readonly int CubemapTexture = Shader.PropertyToID("_CubemapTexture");
|
||||
private static readonly int WaveDataBuffer = Shader.PropertyToID("_WaveDataBuffer");
|
||||
private static readonly int WaveData = Shader.PropertyToID("waveData");
|
||||
private static readonly int AbsorptionScatteringRamp = Shader.PropertyToID("_AbsorptionScatteringRamp");
|
||||
private static readonly int DepthCamZParams = Shader.PropertyToID("_VeraslWater_DepthCamParams");
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (!computeOverride)
|
||||
_useComputeBuffer = SystemInfo.supportsComputeShaders &&
|
||||
Application.platform != RuntimePlatform.WebGLPlayer &&
|
||||
Application.platform != RuntimePlatform.Android;
|
||||
else
|
||||
_useComputeBuffer = false;
|
||||
Init();
|
||||
RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
|
||||
|
||||
if(resources == null)
|
||||
{
|
||||
resources = Resources.Load("WaterResources") as WaterResources;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable() {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
GerstnerWavesJobs.Cleanup();
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
|
||||
if (_depthCam)
|
||||
{
|
||||
_depthCam.targetTexture = null;
|
||||
SafeDestroy(_depthCam.gameObject);
|
||||
}
|
||||
if (_depthTex)
|
||||
{
|
||||
SafeDestroy(_depthTex);
|
||||
}
|
||||
|
||||
waveBuffer?.Dispose();
|
||||
}
|
||||
|
||||
private void BeginCameraRendering(ScriptableRenderContext src, Camera cam)
|
||||
{
|
||||
if (cam.cameraType == CameraType.Preview) return;
|
||||
|
||||
var roll = cam.transform.localEulerAngles.z;
|
||||
Shader.SetGlobalFloat(CameraRoll, roll);
|
||||
Shader.SetGlobalMatrix(InvViewProjection,
|
||||
(GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix).inverse);
|
||||
|
||||
// Water matrix
|
||||
const float quantizeValue = 6.25f;
|
||||
const float forwards = 10f;
|
||||
const float yOffset = -0.25f;
|
||||
|
||||
var newPos = cam.transform.TransformPoint(Vector3.forward * forwards);
|
||||
newPos.y = yOffset;
|
||||
newPos.x = quantizeValue * (int) (newPos.x / quantizeValue);
|
||||
newPos.z = quantizeValue * (int) (newPos.z / quantizeValue);
|
||||
|
||||
var matrix = Matrix4x4.TRS(newPos + transform.position, Quaternion.identity, transform.localScale); // transform.localToWorldMatrix;
|
||||
|
||||
foreach (var mesh in resources.defaultWaterMeshes)
|
||||
{
|
||||
Graphics.DrawMesh(mesh,
|
||||
matrix,
|
||||
resources.defaultSeaMaterial,
|
||||
gameObject.layer,
|
||||
cam,
|
||||
0,
|
||||
null,
|
||||
ShadowCastingMode.Off,
|
||||
true,
|
||||
null,
|
||||
LightProbeUsage.Off,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SafeDestroy(Object o)
|
||||
{
|
||||
if(Application.isPlaying)
|
||||
Destroy(o);
|
||||
else
|
||||
DestroyImmediate(o);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
SetWaves();
|
||||
GenerateColorRamp();
|
||||
if (bakedDepthTex)
|
||||
{
|
||||
Shader.SetGlobalTexture(WaterDepthMap, bakedDepthTex);
|
||||
}
|
||||
|
||||
if (!gameObject.TryGetComponent(out _planarReflections))
|
||||
{
|
||||
_planarReflections = gameObject.AddComponent<PlanarReflections>();
|
||||
}
|
||||
_planarReflections.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector;
|
||||
_planarReflections.m_settings = settingsData.planarSettings;
|
||||
_planarReflections.enabled = settingsData.refType == ReflectionType.PlanarReflection;
|
||||
|
||||
if(resources == null)
|
||||
{
|
||||
resources = Resources.Load("WaterResources") as WaterResources;
|
||||
}
|
||||
if(Application.platform != RuntimePlatform.WebGLPlayer) // TODO - bug with Opengl depth
|
||||
CaptureDepthMap();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
GerstnerWavesJobs.UpdateHeights();
|
||||
}
|
||||
|
||||
public void FragWaveNormals(bool toggle)
|
||||
{
|
||||
var mat = GetComponent<Renderer>().sharedMaterial;
|
||||
if (toggle)
|
||||
mat.EnableKeyword("GERSTNER_WAVES");
|
||||
else
|
||||
mat.DisableKeyword("GERSTNER_WAVES");
|
||||
}
|
||||
|
||||
private void SetWaves()
|
||||
{
|
||||
SetupWaves(surfaceData._customWaves);
|
||||
|
||||
// set default resources
|
||||
Shader.SetGlobalTexture(FoamMap, resources.defaultFoamMap);
|
||||
Shader.SetGlobalTexture(SurfaceMap, resources.defaultSurfaceMap);
|
||||
|
||||
_maxWaveHeight = 0f;
|
||||
foreach (var w in _waves)
|
||||
{
|
||||
_maxWaveHeight += w.amplitude;
|
||||
}
|
||||
_maxWaveHeight /= _waves.Length;
|
||||
|
||||
_waveHeight = transform.position.y;
|
||||
|
||||
Shader.SetGlobalFloat(WaveHeight, _waveHeight);
|
||||
Shader.SetGlobalFloat(MaxWaveHeight, _maxWaveHeight);
|
||||
Shader.SetGlobalFloat(MaxDepth, surfaceData._waterMaxVisibility);
|
||||
|
||||
switch(settingsData.refType)
|
||||
{
|
||||
case ReflectionType.Cubemap:
|
||||
Shader.EnableKeyword("_REFLECTION_CUBEMAP");
|
||||
Shader.DisableKeyword("_REFLECTION_PROBES");
|
||||
Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION");
|
||||
Shader.SetGlobalTexture(CubemapTexture, settingsData.cubemapRefType);
|
||||
break;
|
||||
case ReflectionType.ReflectionProbe:
|
||||
Shader.DisableKeyword("_REFLECTION_CUBEMAP");
|
||||
Shader.EnableKeyword("_REFLECTION_PROBES");
|
||||
Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION");
|
||||
break;
|
||||
case ReflectionType.PlanarReflection:
|
||||
Shader.DisableKeyword("_REFLECTION_CUBEMAP");
|
||||
Shader.DisableKeyword("_REFLECTION_PROBES");
|
||||
Shader.EnableKeyword("_REFLECTION_PLANARREFLECTION");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
Shader.SetGlobalInt(WaveCount, _waves.Length);
|
||||
|
||||
//GPU side
|
||||
if (_useComputeBuffer)
|
||||
{
|
||||
Shader.EnableKeyword("USE_STRUCTURED_BUFFER");
|
||||
waveBuffer?.Dispose();
|
||||
waveBuffer = new ComputeBuffer(10, (sizeof(float) * 6));
|
||||
waveBuffer.SetData(_waves);
|
||||
Shader.SetGlobalBuffer(WaveDataBuffer, waveBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Shader.DisableKeyword("USE_STRUCTURED_BUFFER");
|
||||
Shader.SetGlobalVectorArray(WaveData, GetWaveData());
|
||||
}
|
||||
//CPU side
|
||||
if(GerstnerWavesJobs.Initialized == false && Application.isPlaying)
|
||||
GerstnerWavesJobs.Init();
|
||||
}
|
||||
|
||||
private Vector4[] GetWaveData()
|
||||
{
|
||||
var waveData = new Vector4[20];
|
||||
for (var i = 0; i < _waves.Length; i++)
|
||||
{
|
||||
waveData[i] = new Vector4(_waves[i].amplitude, _waves[i].direction, _waves[i].wavelength, _waves[i].onmiDir);
|
||||
waveData[i+10] = new Vector4(_waves[i].origin.x, _waves[i].origin.y, 0, 0);
|
||||
}
|
||||
return waveData;
|
||||
}
|
||||
|
||||
private void SetupWaves(bool custom)
|
||||
{
|
||||
if(!custom)
|
||||
{
|
||||
//create basic waves based off basic wave settings
|
||||
var backupSeed = Random.state;
|
||||
Random.InitState(surfaceData.randomSeed);
|
||||
var basicWaves = surfaceData._basicWaveSettings;
|
||||
var a = basicWaves.amplitude;
|
||||
var d = basicWaves.direction;
|
||||
var l = basicWaves.wavelength;
|
||||
var numWave = basicWaves.numWaves;
|
||||
_waves = new Wave[numWave];
|
||||
|
||||
var r = 1f / numWave;
|
||||
|
||||
for (var i = 0; i < numWave; i++)
|
||||
{
|
||||
var p = Mathf.Lerp(0.5f, 1.5f, i * r);
|
||||
var amp = a * p * Random.Range(0.8f, 1.2f);
|
||||
var dir = d + Random.Range(-90f, 90f);
|
||||
var len = l * p * Random.Range(0.6f, 1.4f);
|
||||
_waves[i] = new Wave(amp, dir, len, Vector2.zero, false);
|
||||
Random.InitState(surfaceData.randomSeed + i + 1);
|
||||
}
|
||||
Random.state = backupSeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
_waves = surfaceData._waves.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateColorRamp()
|
||||
{
|
||||
if(_rampTexture == null)
|
||||
_rampTexture = new Texture2D(128, 4, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None);
|
||||
_rampTexture.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
var defaultFoamRamp = resources.defaultFoamRamp;
|
||||
|
||||
var cols = new Color[512];
|
||||
for (var i = 0; i < 128; i++)
|
||||
{
|
||||
cols[i] = surfaceData._absorptionRamp.Evaluate(i / 128f);
|
||||
}
|
||||
for (var i = 0; i < 128; i++)
|
||||
{
|
||||
cols[i + 128] = surfaceData._scatterRamp.Evaluate(i / 128f);
|
||||
}
|
||||
for (var i = 0; i < 128; i++)
|
||||
{
|
||||
switch(surfaceData._foamSettings.foamType)
|
||||
{
|
||||
case 0: // default
|
||||
cols[i + 256] = defaultFoamRamp.GetPixelBilinear(i / 128f , 0.5f);
|
||||
break;
|
||||
case 1: // simple
|
||||
cols[i + 256] = defaultFoamRamp.GetPixelBilinear(surfaceData._foamSettings.basicFoam.Evaluate(i / 128f) , 0.5f);
|
||||
break;
|
||||
case 2: // custom
|
||||
cols[i + 256] = Color.black;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_rampTexture.SetPixels(cols);
|
||||
_rampTexture.Apply();
|
||||
Shader.SetGlobalTexture(AbsorptionScatteringRamp, _rampTexture);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////Shoreline Depth Texture/////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[ContextMenu("Capture Depth")]
|
||||
public void CaptureDepthMap()
|
||||
{
|
||||
//Generate the camera
|
||||
if(_depthCam == null)
|
||||
{
|
||||
var go =
|
||||
new GameObject("depthCamera") {hideFlags = HideFlags.HideAndDontSave}; //create the cameraObject
|
||||
_depthCam = go.AddComponent<Camera>();
|
||||
}
|
||||
|
||||
var additionalCamData = _depthCam.GetUniversalAdditionalCameraData();
|
||||
additionalCamData.renderShadows = false;
|
||||
additionalCamData.requiresColorOption = CameraOverrideOption.Off;
|
||||
additionalCamData.requiresDepthOption = CameraOverrideOption.Off;
|
||||
|
||||
var t = _depthCam.transform;
|
||||
var depthExtra = 4.0f;
|
||||
t.position = Vector3.up * (transform.position.y + depthExtra);//center the camera on this water plane height
|
||||
t.up = Vector3.forward;//face the camera down
|
||||
|
||||
_depthCam.enabled = true;
|
||||
_depthCam.orthographic = true;
|
||||
_depthCam.orthographicSize = 250;//hardcoded = 1k area - TODO
|
||||
_depthCam.nearClipPlane =0.01f;
|
||||
_depthCam.farClipPlane = surfaceData._waterMaxVisibility + depthExtra;
|
||||
_depthCam.allowHDR = false;
|
||||
_depthCam.allowMSAA = false;
|
||||
_depthCam.cullingMask = (1 << 10);
|
||||
//Generate RT
|
||||
if (!_depthTex)
|
||||
_depthTex = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear);
|
||||
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3)
|
||||
{
|
||||
_depthTex.filterMode = FilterMode.Point;
|
||||
}
|
||||
_depthTex.wrapMode = TextureWrapMode.Clamp;
|
||||
_depthTex.name = "WaterDepthMap";
|
||||
//do depth capture
|
||||
_depthCam.targetTexture = _depthTex;
|
||||
_depthCam.Render();
|
||||
Shader.SetGlobalTexture(WaterDepthMap, _depthTex);
|
||||
// set depth bufferParams for depth cam(since it doesnt exist and only temporary)
|
||||
var _params = new Vector4(t.position.y, 250, 0, 0);
|
||||
//Vector4 zParams = new Vector4(1-f/n, f/n, (1-f/n)/f, (f/n)/f);//2015
|
||||
Shader.SetGlobalVector(DepthCamZParams, _params);
|
||||
|
||||
/* #if UNITY_EDITOR
|
||||
Texture2D tex2D = new Texture2D(1024, 1024, TextureFormat.Alpha8, false);
|
||||
Graphics.CopyTexture(_depthTex, tex2D);
|
||||
byte[] image = tex2D.EncodeToPNG();
|
||||
System.IO.File.WriteAllBytes(Application.dataPath + "/WaterDepth.png", image);
|
||||
#endif*/
|
||||
|
||||
_depthCam.enabled = false;
|
||||
_depthCam.targetTexture = null;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public enum DebugMode { none, stationary, screen };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6edb8448c9b94f018953ca2a6fa5891
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- _rampTexture: {instanceID: 0}
|
||||
- settingsData: {fileID: 11400000, guid: a4cacf74654e54b35b9eaea74fd20b4e, type: 2}
|
||||
- surfaceData: {fileID: 11400000, guid: 846f3dc3b5341447a86687211f741bc7, type: 2}
|
||||
- resources: {fileID: 11400000, guid: 40615c805d6e84ce7800418367985fe1, type: 2}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "WaterSystem",
|
||||
"references": [
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:15fc0a57446b3144c949da3e2b9737a9",
|
||||
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795",
|
||||
"GUID:9e24947de15b9834991c9d8411ea37cf"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eee6090a63a1e48e6857869e0cbe74dc
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user