Low Poly Water with Proper Buoyancy
Hi all,
So first, let me start off by saying I'm super new to Unity. I have a background in video production and a specialization in After Effects with expressions, so I'm some what familiar with using code to relate objects.
As a first foray, I'm trying to make a low- poly duck float in low poly water.
I have found a shader online that is essentially this: http://www.battlemaze.com/?p=153 (I think in my hunt a found a newer version somewhere). From what I can tell, I guess it's manipulating vertices to form the waves.
My question is how to make my duck float on the top of the waves. I've seen some people do something or another with rigid body wheels, but I'm unclear on how to even make the ducks gravity respect the waves so that it sits on top (or just a little bit under it).
Any help would be greatly appreciated!
Thanks!
George
Also, from further research, it seems like my approach may be fundamentally flawed. If I'm understanding correctly, the shader is only making it look like its moving, but the object isn't. Therefore, any collision with it won't work.
I've also read that actually manipulating a mesh is super render intensive, and I do want this to eventually run on an oculus.
It depends. If your scene is moderately small and is low poly, then you could probably get away with actual code based mesh displacement. It is less straight forward than shader displacement however.
Is this essentially what you're suggesting?
http://catlikecoding.com/unity/tutorials/mesh-deformation/
I really don't have a reference of what small is since this is my first project. It will basically be a small low poly pond surrounded by a lot of other static objects with a bunch of ducks instantiating. It is also being made for Oculus so the assumption is that any use would have a pretty beefy computer. Any suggestions on the best way to figure out if I can get away with it?
@namey5 For whatever reason, I can't reply to your most recent reply.
First off, thanks! I'm glad to see this forum is a responsive community and I really appreciate your help! If you have seen the video that @conman79 posted above, that looks like any interesting way to handle buoyancy (thanks to you as well!)
I'd love to see what you come up with! At this point, I definitely need some hand holding, but rest assured I spend my time researching to make sure I really understand whats going on and don't just rely on people to solve all my problems ;)
Answer by conman79 · Nov 09, 2016 at 01:20 AM
Realistic buoyancy is quite difficult to simulate. Even more difficult if your water is based on a shader. Since you specifically mentioned a duck, here's a talk about buoyancy using a duck as an example!
Answer by Namey5 · Nov 09, 2016 at 05:58 AM
Ok, so after a bit I've developed the system I was talking about, so you should be able to test this on whatever setup you want.
The first script is for the actual water object. I've set it up with static variables, and as such it will only work with one water object in the scene. Basically, you attach this to, say, a plane or whatever water object you want and it will deform the mesh based on Unity's Perlin Noise algorithm. I'd suggest removing the collider from the water object.
using UnityEngine;
using System.Collections;
public class WaterDeformation : MonoBehaviour
{
public static Mesh mesh; //The water mesh
public static Transform water; //The water transform
public float deformAmount = 1; //Amount to deform the water
public float scale = 2.5f; //How fine the displacement is
public float speed = 1; //The speed of waves
private Vector2 time = Vector2.zero; //The actual speed offset
// Use this for initialization
void Start ()
{
//Set the water and mesh variables at start
water = transform;
mesh = GetComponent<MeshFilter>().mesh;
}
// Update is called once per frame
void Update ()
{
time = new Vector2 (Time.time, Time.time) * speed; //Set up speed offset for deformation
Vector3[] vertices = mesh.vertices; //Create a variable for the vertices beforehand
for (int i = 0; i < vertices.Length; i++) {
vertices[i] = Deform (vertices[i]); //For every vertice, deform the Y position
}
mesh.vertices = vertices; //Re-assign the vertices
mesh.RecalculateNormals (); //Recalculate the normals so the object doesn't look flat
GetComponent<MeshFilter>().mesh = mesh; //Re-assign the mesh to the filter
}
Vector3 Deform (Vector3 v) //Takes a Vector3
{
v.y = Mathf.PerlinNoise (v.x / scale + time.x, v.z / scale + time.y) * deformAmount; //Distort the vertice's Y position based off its X and Z positions + time
return v; //Return the offset vertice position
}
}
The second script you attach to any objects you want to have buoyancy. The thing about this is, I've never tried to calculate buoyancy myself, so instead what it does is it adds force upwards if the object is below the water at the nearest point.
using UnityEngine;
using System.Collections;
public class Buoyancy : MonoBehaviour
{
public float buoyancy = 20; //Buoyancy force
public float viscosity = 20; //How easily an object can move through the water
private Rigidbody rb; //Rigidbody attached to this object
// Use this for initialization
void Start ()
{
rb = GetComponent<Rigidbody>(); //Set the rigidbody at startup
}
// Update is called once per frame
void FixedUpdate ()
{
Vector3[] vertices = WaterDeformation.mesh.vertices; //Find the water's vertices
Vector3[] worldVerts = new Vector3[vertices.Length]; //Create a new array to store world space vertex positions
for (int i = 0; i < vertices.Length; i++) {
worldVerts[i] = WaterDeformation.water.TransformPoint (vertices[i]); //For every vertex, transform the position into world space
}
Vector3 nearestVert = NearestVertice (transform.position, worldVerts); //Find the nearest vertice to this object
if (transform.position.y < nearestVert.y) { //If this object is below the nearest vertice
rb.AddForce (Vector3.up * buoyancy); //Apply force upwards
rb.velocity /= ((viscosity / 100) + 1); //Slow the objects movements when in water
}
}
Vector3 NearestVertice (Vector3 pos, Vector3[] verts) //Takes a position and a position array
{
Vector3 nearestVert = Vector3.zero; //Create the initial nearestVert variable and initialise it
float minDist = 100; //Declare the min dist (can be whatever you want, something large though)
for (int i = 0; i < verts.Length; i++) { //For every vertice
if (Vector3.Distance (pos, verts[i]) < minDist) { //If the vertice is closer than the one before it
nearestVert = verts[i]; //Set the nearest vertice variable
minDist = Vector3.Distance (pos, verts[i]); //Update the minDist
}
}
return nearestVert; //Return the nearest vertice
}
}
This is awesome @namey5 !!!
I think I'll do some work on the buoyancy over time, but this is definitely a great start!
Thanks a ton!