Hi there, I'm trying to make a low-poly water for my game. Right now I have this: Which is kinda good but not great. I made it in Shader Forge, but I'm stuck. I don't know enough about shaders and I really cannot go further than this. So I guess I'm open to try different ways to do it. What I'd like is a shader that could move the vertices of my water mesh in a way that resembles ripples (right now my mesh just expand/contract altogether, and I cannot have a real wave effect). The reflection is not important (if I cannot go further than this, I may as well get rid of it), but I'd like to have a nice, stylized water which can go well with the low-poly non-textured style of the scenario. So, what I'm asking you is simple: ideas, advice, directions. Both to ready-made shaders (but I've looked at the asset store and didn't find anything useful) and to tutorials, documentation, anything that could help me improve what I've done. As you may have understood, I'm a total newbie with shaders, so any help is useful. Thank you so much!
If you want to move the vertices, your water can't really be low poly. It needs to be fairly high poly to prevent artifacts. You can however add a few normal maps to the water and slide those in various directions. This will make the water appear to have ripples, without actually having to introduce ripples into the model.
You can write a shader, which has a vertex function that moves the vertices. then you can do stuff like v.vertex.xyz = float3(0,1,0)*Sin(v.vertex.x*_Time.x); (I'm writing that from the top of my head, it might not work). You should do more complex math than that obviously, but it's a place to start.
I ended up just using a reflective rim-shaded material and moving the whole texture rather than single vertices. It has not the ripple effect I wanted, but I really couldn't manage to write the shader the way I wanted to (again, total newbie) and the normal map solution totally clashed with the flat-shaded style of the game. Maybe I can try again sometime, but right now that's the best I can do You can see it on the game webpage, and in motion probably on this let's play video. If you have any kind of advice on how improve it, it's very welcome!
Your graphics are very nice, here is my advice on water. Instead of normal maps, you could use a cartoony white foam texture and move that around your water to give it more life.
That's another interesting solution. But I really would like to get an effect like the one on the image below (with moving ripples via vertex displacement) Any advice on that? I particularly like the subtle reflection
That also is easy if you had a high poly water mesh, so you could move the vertices. But that is alot of polygons. On the other hand, it could still be possible to get a smiliar effect with a nice texture(with those triangles and changing their colors) but i have no idea how to animate it.
I don't think there's reflection in that. I think it's just a bit transparent and the model goes below the sea a bit.
Now that I look better, you're right. It seems the polygons are more (or less) transparent depending on the normal angle with the camera. Does it make sense? Hmmm... got to experiment a bit on that. I'd like to try moving vertices with shaders for the ripple effect, can someone point me to some simple documentation about that? Going step by step, here, but I really would like to obtain an effect similar to the pic I posted before
Well, this is for surface shaders, but still: http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html Scroll down to Normal Extrusion with Vertex Modifier. The lines that are most interesting are Code (csharp): void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } You need to add Time somehow to that equation. As I said in my first reply something like Sin(v.vertex.x*_Time.x); will create a sinewave across the x axis.
Sorry to dig out an old topic but I was just wondering if you found a good, fast solution.. I'm pretty much on the same boat, trying to create a low poly water and I'm pretty satisfied with the look of my solution but not quite happy with the performance of it (I want to develop for mobile). Here's a video to demonstrate what I have: What I did is: 1) create a plane in blender and bring it to unity 2) With the NoSharedVertices.cs script I split said plane into separate triangles (first problem: This makes the number of vertices to animate explode, since no vertice will be shared among the various triangles, but I need to separate them in order to obtain the low-poly look) 3) With the Waves.cs script I look through each vertices and add a generic wave and a pseudo random noise (I say pseudo random because the value for each vertex depends from its position) Again, this looks good but is a performance killer, has anyone any advice on that? Some code review? Is there a way to accomplish this without splitting the plane? What would be the fastest solution? Sorry for all the questions, I'm a beginner. Also, I'm sharing the scripts I'm using so maybe you guys could give some advice, or maybe someone wants to try them out. NoSharedVertices.cs (found on the web) this is basically the same as the "split edges" modifier in blender: Code (CSharp): using UnityEngine; using UnityEditor; public class NoSharedVertices : EditorWindow { private string error = ""; [MenuItem("Window/No Shared Vertices")] public static void ShowWindow() { EditorWindow.GetWindow(typeof(NoSharedVertices)); } void OnGUI() { //Transform curr = Selection.activeTransform; GUILayout.Label ("Creates a clone of the game object where the triangles\n" + "do not share vertices"); GUILayout.Space(20); if (GUILayout.Button ("Process")) { error = ""; NoShared(); } GUILayout.Space(20); GUILayout.Label(error); } void NoShared() { Transform curr = Selection.activeTransform; if (curr == null) { error = "No appropriate object selected."; Debug.Log (error); return; } MeshFilter mf; mf = curr.GetComponent<MeshFilter>(); if (mf == null || mf.sharedMesh == null) { error = "No mesh on the selected object"; Debug.Log (error); return; } // Create the duplicate game object GameObject go = Instantiate (curr.gameObject) as GameObject; mf = go.GetComponent<MeshFilter>(); Mesh mesh = Instantiate (mf.sharedMesh) as Mesh; mf.sharedMesh = mesh; Selection.activeObject = go.transform; //Process the triangles Vector3[] oldVerts = mesh.vertices; int[] triangles = mesh.triangles; Vector3[] vertices = new Vector3[triangles.Length]; for (int i = 0; i < triangles.Length; i++) { vertices[i] = oldVerts[triangles[i]]; triangles[i] = i; } mesh.vertices = vertices; mesh.triangles = triangles; mesh.RecalculateBounds(); mesh.RecalculateNormals(); // Save a copy to disk string name = "Assets/Editor/"+go.name+Random.Range (0, int.MaxValue).ToString()+".asset"; AssetDatabase.CreateAsset(mf.sharedMesh, name); AssetDatabase.SaveAssets(); } } And the animated waves Waves.cs: Code (CSharp): using UnityEngine; using System.Collections; using System.Collections.Generic; public class Waves : MonoBehaviour { public float waveHeight = 10.0f; public float speed = 1.0f; public float waveLength = 1.0f; public float noiseStrength = 4.0f; public float noiseWalk = 1.0f; public float randomHeight = 0.2f; public float randomSpeed = 5.0f; public float noiseOffset = 20.0f; private Vector3[] baseHeight; private Vector3[] vertices; private List<float> perVertexRandoms = new List<float>(); private Mesh mesh; void Awake() { mesh = GetComponent<MeshFilter>().mesh; if (baseHeight == null) { baseHeight = mesh.vertices; } for(int i=0; i < baseHeight.Length; i++) { perVertexRandoms.Add(Random.value * randomHeight); } } void Update () { if (vertices == null) { vertices = new Vector3[baseHeight.Length]; } for (int i=0;i<vertices.Length;i++) { Vector3 vertex = baseHeight[i]; Random.seed = (int)((vertex.x + noiseOffset) * (vertex.x + noiseOffset) + (vertex.z + noiseOffset) * (vertex.z + noiseOffset)); vertex.y += Mathf.Sin(Time.time * speed + baseHeight[i].x * waveLength + baseHeight[i].y * waveLength) * waveHeight; vertex.y += Mathf.Sin(Mathf.Cos(Random.value * 1.0f) * randomHeight * Mathf.Cos (Time.time * randomSpeed * Mathf.Sin(Random.value * 1.0f))); //vertex.y += Mathf.PerlinNoise(baseHeight[i].x + Mathf.Cos(Time.time * 0.1f) + noiseWalk, baseHeight[i].y + Mathf.Sin(Time.time * 0.1f)) * noiseStrength; vertices[i] = vertex; } mesh.vertices = vertices; mesh.RecalculateNormals(); } }
I think the biggest performance killer in your code is actively deforming the mesh every frame. Your code is basically recreating the entire mesh every frame which won't perform well (and really bad on mobile / lower end). The better way to do it would be by moving your method into a custom shader and moving the vertices via the shader instead of recreating the mesh in the engine. I'm not a shader expert so can't help you there but have experimented with procedural mesh generation a lot in code so know how badly it performs if you're doing it every frame.
I tried that path and wrote in this forum thread http://forum.unity3d.com/threads/flat-shaded-ocean.323808/#post-2103510 . As you can see it looks promising but I can't get the flat-shaded thing working. Any advice?
That flat shading you're seeing is basically what the polyworld assets give you. i own a polyworld asset and it comes with a water shader that looks like the example you posted. Heck, that might even be a screenshot from a polyworld generated mesh. Its not particularly cheap, but if that's the style you're going for then it will probably be well worth the 50$ it will cost you. It also includes scripts to convert any mesh to a polyworld style mesh.
Looks great! I recently implemented this myself. Moving it off to a shader is nearly impossible if you are targeting mobile (especially OpenGLES 2 and 3). The reason why it looks flat is because of one thing: normals. Deforming the mesh vertices is extremely easy in the vertex shader. You can literally copy and paste code from C#. The normals however aren't so easy. Lighting at any point on a surface depend on the normals, and the normals depend on the neighboring vertices of a particular vertex. So except you have geometry shader with the ability to synthesize this information, the closest you can get is an approximation which looks terrible (I tested it myself). I managed to get my seas with almost 2k verts waving nicely at 60FPS on an iPhone 4S. On an iPhone 4S. The secret is quite simple: Threads. If you animate the vertices on the main thread, you'll be allotting precious CPU frame time. Instead of doing this, move the vertex deformation to a worker thread. This will usually run faster than 60FPS, so you can slow it down by using an EventWaitHandle that is set in the main thread's Update. Oh, and to avoid re-killing performance, stay away from Monitor/lock when updating the mesh filter on the main thread (Unity's API isn't thread safe). Instead, double buffer the vertices and ping pong between them. This becomes memory consuming, but I believe it's more optimized. A user won't see how much memory your application is consuming, but they will definitely see the stuttering on the screen. I guess it's all about UX.
I'm not sure if you need physics for floating a boat. You can get the closest vertex to the boat, extract its normal, and align the boat's up axis with the normal. This will create a nice-looking boat that floats and jiggles.
You can still calculate the normals if your mesh is uniform. Just use the UV to store additional vertice index information. Since you are not using any textures anyways. ;-) That way you can calculate the other 2 vertice-positions in your code to do the normal calculation. I've done something like this. You can see the result here. The example uses a noise texture but you could also just calculate the wave patterns. Do note that if you use a noise texture that it won't work on all android device. As not all devices support texture access in vertex function :'(
Interesting approach, you could encode neighbor vertex positions as TEXCOORD1 for instance and use it to calculate world positions in the vertex shader. For my game, the mesh is non-uniform (it is displaced on the XZ axes) to look more interesting. Thanks for letting me know of this technique!
joni.giuro, I kind of landed here. I havent tried yet but this low poly water is also a effect I wish to archive. Have you tried create a plane outside of unity, import it like a normal 3d model asset, go to the plane import settings and change the smooting angle to 0? Does not it gives you the "flat normal" for each vertex. I mean, it works for static models, doew it works with dynamic meshes?
Hey Jildert, The video is down but I'm still interested in what you are suggesting. Can you show us your shader code as I'm not sure how to accomplish what you are saying.