Search Unity

Flat shaded ocean

Discussion in 'General Graphics' started by joni-giuro, May 5, 2015.

  1. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    Hi all, I'm trying to create a flat shaded ocean (something like https://d13yacurqjgara.cloudfront.net/users/112473/screenshots/963968/attachments/110353/HighRes.jpg ). After a bit of research I stumbled upon an article saying I just need to tell unity to calculate normals and set the angle to 0 but that didn't work (I found topic on here saying that it is broken in Unity5, is this the case?). Other articles are saying that I need to break my mesh down and duplicate vertices so that each face is disconnected from the others and that it's otherwise impossible. I did that and it worked just fine but I'm kinda concerned about performance because to create the ocean waves I need to animate each one of those vertices. So my questions are:
    1) Is it true that there is no other way of achieving that effect without splitting up the mesh?
    2) If no, what are other possibilities?
    3) Is the import really broken in Unity5?

    PS: I also tried setting the edges to hard in blender, no changes.

    Thanks
     
  2. KingMatthew

    KingMatthew

    Joined:
    Jul 7, 2013
    Posts:
    166
    I have made some flat shaded objects in unity5 and it works for me. I just changed the angle to 0. I don't know why it wouldn't work for anyone else.

    I have no skill in this, but couldn't you just deform the mesh of the ocean? I figured that's what every dynamic ocean uses to look cool.
     
  3. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    That's what i do, but if a face of the ocean plane shares vertices with it's neighbour face, unity will smooth them out by calculating a median normal. That's why I need to duplicate all the vertices, leading to a performance drop.
    Don't know if it's needed but here's my waves script:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class Waves : MonoBehaviour {
    6.    
    7.     public float waveHeight = 10.0f;
    8.     public float speed = 1.0f;
    9.     public float waveLength = 1.0f;
    10.     public float noiseStrength = 4.0f;
    11.     public float noiseWalk = 1.0f;
    12.     public float randomHeight = 0.2f;
    13.     public float randomSpeed = 5.0f;
    14.    
    15.     private Vector3[] baseHeight;
    16.     private Vector3[] vertices;
    17.     private List<float> perVertexRandoms = new List<float>();
    18.     private Mesh mesh;
    19.    
    20.     void Awake() {
    21.         mesh = GetComponent<MeshFilter>().mesh;
    22.         if (baseHeight == null) {
    23.             baseHeight = mesh.vertices;
    24.         }
    25.  
    26.         for(int i=0; i < baseHeight.Length; i++) {
    27.             perVertexRandoms.Add(Random.value * randomHeight);
    28.         }
    29.     }
    30.    
    31.     void Update () {
    32.         if (vertices == null) {
    33.             vertices = new Vector3[baseHeight.Length];
    34.         }
    35.        
    36.         for (int i=0;i<vertices.Length;i++) {
    37.             Vector3 vertex = baseHeight[i];
    38.             Random.seed = (int)(vertex.x * vertex.x + vertex.z * vertex.z);
    39.             vertex.y += Mathf.Sin(Time.time * speed + baseHeight[i].x * waveLength + baseHeight[i].y * waveLength) * waveHeight;
    40.             vertex.y += Mathf.Sin(Mathf.Cos(Random.value * 1.0f) * randomHeight * Mathf.Cos (Time.time * randomSpeed * Mathf.Sin(Random.value * 1.0f)));
    41.             //vertex.y += Mathf.PerlinNoise(baseHeight[i].x + Mathf.Cos(Time.time * 0.1f) + noiseWalk, baseHeight[i].y + Mathf.Sin(Time.time * 0.1f)) * noiseStrength;
    42.             vertices[i] = vertex;
    43.         }
    44.         mesh.vertices = vertices;
    45.         mesh.RecalculateNormals();
    46.     }
    47. }
     
  4. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,670
    If you want flat shaded you either have to get rid of surface normals entirely, or have a completely separate set of 3 vertices for each triangle - with normals all pointing in the same direction.
     
  5. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
  6. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    How do I get rid of normals?
     
  7. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
  8. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    As far as I know removing the normal information in any 3D app will import the model into Unity with duplicated vertices.

    Hey @joni.giuro
    I just had an out of the box thought.
    Could the effect you desire be controlled by a normal map without duplicated vertices?

    i.e. In any 3D modeling package clear all the smoothing groups (normals) off a model. Generate a normal map (however you prefer, bake, xnormal, etc) with the model having different smoothing groups (normals) per face.
    Apply any diffuse texture you want along with the normal map.
    I'd suggest smoothing the model back to 1 unified smoothing group before export or Unity may import with duplicated verts/edges.
    Once in Unity apply the diffuse and normal maps. The normal map will give the effect you desire, to the smoothed model that it is faceted.

    Untested theory, but I will test later. Update with results if you try it out. I think that would show the faceted effect you want without duplicating the vertices.
     
  9. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    Thanks @theANIMATOR2b
    That could work, but how would I animate it believably then? That should be done in the 3d software and I wouldn't have much control about the final result, but yeah I'll have to try it out. Here's a video of the kind of control I'd like to achieve (it's the duplicated-vertices version):


    Here's a screenshot of another approach I tried and honestly I think it looks promising as I get around 900fps.
    Basically it's doing the exact same thing as my C# script except it's a shader (found on youtube), now if I only could find a way to give each triangle's normal direction the one of one single vertex and not a cross product of all three vertices I think I should get the result I want. Here's the shader:

    Code (CSharp):
    1. Shader "Custom/PlaneDeformer"
    2. {
    3.  
    4.     Properties
    5.     {
    6.         _Color ("Color", Color) = (1, 1, 1, 1)
    7.         _PhaseOffset ("PhaseOffset", Range(0,1)) = 0
    8.         _Speed ("Speed", Range(0.0,10)) = 1.0
    9.         _Depth ("Depth", Range(0.0,1)) = 0.2
    10.         _Smoothing ("Smoothing", Range(0,1)) = 0.0
    11.         _XDrift ("X Drift", Range(0.0,2.0)) = 0.05
    12.         _ZDrift ("Z Drift", Range(0.0,2.0)) = 0.12
    13.         _Scale ("Scale", Range(0.1,10)) = 1.0
    14.         //_Scale ("Scale", Float) = 1
    15.     }
    16.  
    17.  
    18.  
    19.  
    20.     SubShader
    21.     {
    22.      
    23.         Tags
    24.         {
    25.             "Queue" = "Geometry"
    26.             "RenderType" = "Opaque"
    27.             "IgnoreProjector" = "True"
    28.         }
    29.      
    30.         CGPROGRAM
    31.         #pragma surface surf Lambert vertex:vert
    32.         //#pragma surface surf Lambert finalcolor:showNormals vertex:vert noforwardadd
    33.         #pragma target 3.0
    34.      
    35.      
    36.         half3 _Color;
    37.         float _PhaseOffset;
    38.         float _Speed;
    39.         float _Depth;
    40.         float _Smoothing;
    41.         float _XDrift;
    42.         float _ZDrift;
    43.         float _Scale;
    44.      
    45.         struct Input
    46.         {
    47.             half3 debugColor;
    48.         };
    49.      
    50.      
    51.      
    52.         // I ran into an enormous, annoying error where the Cg sin() function was causing some geometry mayhem,
    53.         //   altering properties it shouldn't, if it was given a value of exactly 1.
    54.         // Any modulation fixed it.  No other sin() implementation has the same problem.
    55.         // No idea what the problem is!  Using this as a workaround, may go to the forums to see what's up later.
    56.         float sine( float x )
    57.         {
    58.             float b = (1-x)<0.001 ? 1.0001 : 1.0;
    59.             return sin(x) * b;
    60.         }
    61.      
    62.      
    63.      
    64.         void vert( inout appdata_full v, out Input o )
    65.         {
    66.             // Note that, to start off, all work is in object (local) space.
    67.             // We will eventually move normals to world space to handle arbitrary object orientation.
    68.             // There is no real need for tangent space in this case.
    69.          
    70.             // Do all work in world space
    71.             float3 v0 = mul( _Object2World, v.vertex ).xyz;
    72.          
    73.             // Create two fake neighbor vertices.
    74.             // The important thing is that they be distorted in the same way that a real vertex in their location would.
    75.             // This is pretty easy since we're just going to do some trig based on position, so really any samples will do.
    76.             float3 v1 = v0 + float3( 0.05, 0, 0 ); // +X
    77.             float3 v2 = v0 + float3( 0, 0, 0.05 ); // +Z
    78.          
    79.          
    80.             // Some animation values
    81.             float phase = _PhaseOffset * (3.14 * 2);
    82.             float phase2 = _PhaseOffset * (3.14 * 1.123);
    83.             float speed = _Time.y * _Speed;
    84.             float speed2 = _Time.y * (_Speed * 0.33 );
    85.             float _Depth2 = _Depth * 1.0;
    86.             float v0alt = v0.x * _XDrift + v0.z * _ZDrift;
    87.             float v1alt = v1.x * _XDrift + v1.z * _ZDrift;
    88.             float v2alt = v2.x * _XDrift + v2.z * _ZDrift;
    89.          
    90.             // Modify the real vertex and two theoretical samples by the distortion algorithm (here a simple sine wave on Y, driven by local X pos)
    91.             v0.y += sin( phase  + speed  + ( v0.x  * _Scale ) ) * _Depth;
    92.             v0.y += sin( phase2 + speed2 + ( v0alt * _Scale ) ) * _Depth2; // This is just another wave being applied for a bit more complexity.
    93.          
    94.             v1.y += sin( phase  + speed  + ( v1.x  * _Scale ) ) * _Depth;
    95.             v1.y += sin( phase2 + speed2 + ( v1alt * _Scale ) ) * _Depth2;
    96.          
    97.             v2.y += sin( phase  + speed  + ( v2.x  * _Scale ) ) * _Depth;
    98.             v2.y += sin( phase2 + speed2 + ( v2alt * _Scale ) ) * _Depth2;
    99.          
    100.             // By reducing the delta on Y, we effectively restrict the amout of variation the normals will exhibit.
    101.             // This appears like a smoothing effect, separate from the actual displacement depth.
    102.             // It's basically undoing the change to the normals, leaving them straight on Y.
    103.             v1.y -= (v1.y - v0.y) * _Smoothing;
    104.             v2.y -= (v2.y - v0.y) * _Smoothing;
    105.          
    106.             // Solve worldspace normal
    107.             float3 vna = cross( v2-v0, v1-v0 );
    108.          
    109.             // OPTIONAL worldspace normal out to a custom value.  Uncomment the showNormals finalcolor profile option above to see the result
    110.             //o.debugColor = ( normalize( vna ) * 0.5 ) + 0.5;
    111.             //o.debugColor = ( normalize( vna )  );
    112.          
    113.             // Put normals back in object space
    114.             float3 vn = mul( float3x3(_World2Object), vna );
    115.          
    116.             // Normalize
    117.             v.normal = normalize( vn );
    118.          
    119.             // Put vertex back in object space, Unity will automatically do the MVP projection
    120.             v.vertex.xyz = mul( float3x3(_World2Object), v0 );
    121.         }
    122.      
    123.      
    124.      
    125.         // Optional normal debug function, unccoment profile option to invoke
    126.         void showNormals( Input IN, SurfaceOutput o, inout fixed4 color )
    127.         {
    128.             color.rgb = IN.debugColor.rgb;
    129.             color.a = 1;
    130.         }
    131.      
    132.      
    133.      
    134.         // Regular old surface shader
    135.         void surf (Input IN, inout SurfaceOutput o)
    136.         {
    137.             o.Albedo = _Color.rgb;
    138.         }
    139.      
    140.         ENDCG
    141.      
    142.     }
    143.  
    144. }
     

    Attached Files:

    Last edited: May 7, 2015
  10. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    I think going with the duplicated vertex way is probably the best for an animated surface. I think the normal map process I suggested isn't viable for an animated surface.
     
  11. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    Sadly, that's not a path I can go down because it's performance impact is too heavy. What do you think about the sahder thing? Are you experienced with shaders?
     
  12. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    Sadly I know zed about shaders.
    That's why I suggested the normal mapping idea. :)
    I just saw a kit mentioned over in the asset store thread, and they were using a polygonal style for it. They mentioned no textures, only materials were applied to the assets.
    May want to hit up the kit author in pm.
     
  13. DiscoFever

    DiscoFever

    Joined:
    Nov 16, 2014
    Posts:
    286
    Looks awesome; is there a way to make the water transparent ?
     
  14. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    Sure, I didn't try it though, try playing around with Unity's transparent shader
     
  15. DiscoFever

    DiscoFever

    Joined:
    Nov 16, 2014
    Posts:
    286
    Ok here's my try (i'm a total n00b sorry); i think i lost some color. Can you check and maybe 'fix' ? :)


    Code (CSharp):
    1. Shader "Custom/AlphaPlaneDeformer"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1, 1, 1, 1)
    6.         _PhaseOffset ("PhaseOffset", Range(0,1)) = 0
    7.         _Speed ("Speed", Range(0.0,10)) = 1.0
    8.         _Depth ("Depth", Range(0.0,1)) = 0.2
    9.         _Smoothing ("Smoothing", Range(0,1)) = 0.0
    10.         _XDrift ("X Drift", Range(0.0,2.0)) = 0.05
    11.         _ZDrift ("Z Drift", Range(0.0,2.0)) = 0.12
    12.         _Scale ("Scale", Range(0.1,10)) = 1.0
    13.         _Alpha ("Alpha", Range(0,1)) = 0.2
    14.         //_Scale ("Scale", Float) = 1
    15.     }
    16.     SubShader
    17.     {
    18.    
    19.         Tags
    20.         {
    21.             "Queue" = "Transparent"
    22.             "RenderType" = "Transparent"
    23.             "IgnoreProjector" = "True"
    24.         }
    25.    
    26.         CGPROGRAM
    27.         #pragma surface surf Lambert alpha vertex:vert
    28.         //#pragma surface surf Lambert finalcolor:showNormals vertex:vert noforwardadd
    29.         #pragma target 3.0
    30.    
    31.    
    32.         half3 _Color;
    33.         float _PhaseOffset;
    34.         float _Speed;
    35.         float _Depth;
    36.         float _Smoothing;
    37.         float _XDrift;
    38.         float _ZDrift;
    39.         float _Scale;
    40.          float _Alpha;
    41.        
    42.         struct Input
    43.         {
    44.             half3 debugColor;
    45.         };
    46.    
    47.    
    48.    
    49.         // I ran into an enormous, annoying error where the Cg sin() function was causing some geometry mayhem,
    50.         //   altering properties it shouldn't, if it was given a value of exactly 1.
    51.         // Any modulation fixed it.  No other sin() implementation has the same problem.
    52.         // No idea what the problem is!  Using this as a workaround, may go to the forums to see what's up later.
    53.         float sine( float x )
    54.         {
    55.             float b = (1-x)<0.001 ? 1.0001 : 1.0;
    56.             return sin(x) * b;
    57.         }
    58.    
    59.    
    60.    
    61.         void vert( inout appdata_full v, out Input o )
    62.         {
    63.             // Note that, to start off, all work is in object (local) space.
    64.             // We will eventually move normals to world space to handle arbitrary object orientation.
    65.             // There is no real need for tangent space in this case.
    66.        
    67.             // Do all work in world space
    68.             float3 v0 = mul( _Object2World, v.vertex ).xyz;
    69.        
    70.             // Create two fake neighbor vertices.
    71.             // The important thing is that they be distorted in the same way that a real vertex in their location would.
    72.             // This is pretty easy since we're just going to do some trig based on position, so really any samples will do.
    73.             float3 v1 = v0 + float3( 0.05, 0, 0 ); // +X
    74.             float3 v2 = v0 + float3( 0, 0, 0.05 ); // +Z
    75.        
    76.        
    77.             // Some animation values
    78.             float phase = _PhaseOffset * (3.14 * 2);
    79.             float phase2 = _PhaseOffset * (3.14 * 1.123);
    80.             float speed = _Time.y * _Speed;
    81.             float speed2 = _Time.y * (_Speed * 0.33 );
    82.             float _Depth2 = _Depth * 1.0;
    83.             float v0alt = v0.x * _XDrift + v0.z * _ZDrift;
    84.             float v1alt = v1.x * _XDrift + v1.z * _ZDrift;
    85.             float v2alt = v2.x * _XDrift + v2.z * _ZDrift;
    86.        
    87.             // Modify the real vertex and two theoretical samples by the distortion algorithm (here a simple sine wave on Y, driven by local X pos)
    88.             v0.y += sin( phase  + speed  + ( v0.x  * _Scale ) ) * _Depth;
    89.             v0.y += sin( phase2 + speed2 + ( v0alt * _Scale ) ) * _Depth2; // This is just another wave being applied for a bit more complexity.
    90.        
    91.             v1.y += sin( phase  + speed  + ( v1.x  * _Scale ) ) * _Depth;
    92.             v1.y += sin( phase2 + speed2 + ( v1alt * _Scale ) ) * _Depth2;
    93.        
    94.             v2.y += sin( phase  + speed  + ( v2.x  * _Scale ) ) * _Depth;
    95.             v2.y += sin( phase2 + speed2 + ( v2alt * _Scale ) ) * _Depth2;
    96.        
    97.             // By reducing the delta on Y, we effectively restrict the amout of variation the normals will exhibit.
    98.             // This appears like a smoothing effect, separate from the actual displacement depth.
    99.             // It's basically undoing the change to the normals, leaving them straight on Y.
    100.             v1.y -= (v1.y - v0.y) * _Smoothing;
    101.             v2.y -= (v2.y - v0.y) * _Smoothing;
    102.        
    103.             // Solve worldspace normal
    104.             float3 vna = cross( v2-v0, v1-v0 );
    105.        
    106.             // OPTIONAL worldspace normal out to a custom value.  Uncomment the showNormals finalcolor profile option above to see the result
    107.             //o.debugColor = ( normalize( vna ) * 0.5 ) + 0.5;
    108.             //o.debugColor = ( normalize( vna )  );
    109.        
    110.             // Put normals back in object space
    111.             float3 vn = mul( float3x3(_World2Object), vna );
    112.        
    113.             // Normalize
    114.             v.normal = normalize( vn );
    115.        
    116.             // Put vertex back in object space, Unity will automatically do the MVP projection
    117.             v.vertex.xyz = mul( float3x3(_World2Object), v0 );
    118.         }
    119.    
    120.    
    121.    
    122.         // Optional normal debug function, unccoment profile option to invoke
    123.         void showNormals( Input IN, SurfaceOutput o, inout fixed4 color )
    124.         {
    125.             color.rgb = IN.debugColor.rgb;
    126.             color.a = 1;
    127.         }
    128.    
    129.    
    130.    
    131.         // Regular old surface shader
    132.         void surf (Input IN, inout SurfaceOutput o)
    133.         {
    134.             o.Albedo = _Color.rgb;
    135.             o.Alpha = _Alpha;
    136.         }
    137.    
    138.         ENDCG
    139.    
    140.     }
    141. }
     
  16. joni-giuro

    joni-giuro

    Joined:
    Nov 21, 2013
    Posts:
    434
    Sorry I'm on the noobish side of the moon as well. Did you get it to work?
     
  17. DiscoFever

    DiscoFever

    Joined:
    Nov 16, 2014
    Posts:
    286
    Well it works like this; what's the problem ?