Search Unity

Wanted: Ocean shader

Discussion in 'Works In Progress - Archive' started by bigkahuna, Jan 8, 2009.

  1. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
  2. immFX

    immFX

    Joined:
    Mar 20, 2010
    Posts:
    110
    @Eki -
    I must agree with bigkahuna, we need more details or a concrete example.

    Well, based on quick tests, the new Blob Shadow (or Light) Projector in Standard Assets/Projectors package provides a projection on water surface that at least follows the boat. If the texture of the Projector is substituted with a believable wake texture, maybe this will work. Still investigating...
     
  3. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    Hey guys,

    would it be possible in principle to bake the ocean mesh animation from the beginning of the scene?
    So i won't need to recalculate it all the time? Maybe something like this is already done?
     
  4. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    @immFX - I'm getting close to having something that works. My approach is slightly different from Eki's as I'm only using one RenderTexture and I haven't been able to get the trailrender to work like I'd like, so I'm sticking to particles for now.

    @Nikolay - Is it possible? Probably. How you'd do it is way over my head. You might have better luck using a 3D modeling app's built in ocean animation tool and then baking that, although I'm not sure Unity would be able to import the resulting mesh animation. Another approach would be to animate the surface of a flat mesh using bones, but the overhead on that might be kind of high also.
     
    Last edited: Nov 19, 2010
  5. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Here's a couple of screenshots to show what I'm talking about. It's not perfect, but it's working. I still need to investigate the different trail renderer scripts on the wiki.

    $WakeSetup01.jpg $WakeSetup02.jpg
     
  6. immFX

    immFX

    Joined:
    Mar 20, 2010
    Posts:
    110
    The screenshots give a clue but it's not 100% clear what you're doing. Would you release a demo project with this once you have a particle emitter that works?
     
  7. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    I've gotten it to work with particles but I'm not happy with the result, so I'm working on a script similar to the trail renderer scripts on the wiki (what Eki recommended). The problem with the existing trail renderers is that it either shifts the texture or stretches it. I'll post an update once I get this figured out.
     
  8. blueice

    blueice

    Joined:
    Dec 9, 2009
    Posts:
    51
    any good ocean shader for a flight simulator? we need a shader that looks good in high altitud. We are willing to pay for it.
     
  9. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    ah high altitudes you can use unity built in pro water, looks ok
     
  10. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Right, that's what I would use. You can also use a projector to add whitecaps (white horses) and cloud shadows to make it even more realistic looking.
     
  11. Eki

    Eki

    Joined:
    Feb 2, 2010
    Posts:
    7
    @ bigkahuna

    I edited my previous post , I actually used the built-in Trail-renderer.
     
  12. blueice

    blueice

    Joined:
    Dec 9, 2009
    Posts:
    51
    well, it seems you know what you are talking about, would you make us a shader like that? the game is an Arcade flight game, and we are pointing to high-quality, so it is very important to have a good ocean effect..
     
  13. blueice

    blueice

    Joined:
    Dec 9, 2009
    Posts:
    51
    btw, I don't think that the built in pro water is enough..
     
  14. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Pro Water gives you an animated / scrolling normal map plus reflection and refraction. I agree, from a distance that by itself probably doesn't look very realistic. What I'm suggesting could probably be done without any custom shaders using particles on a plane rendered to a rendertexture for the shadows and whitecaps and a projector that projects it onto the water's surface. I don't think Pro Water picks up shadows normally so that's why I'd use a projector for this. I haven't tried this yet so can't say how well it will work, but it's similar to the method I'm using for a boat wake.
     
  15. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    Hey bigkahuna,

    I am gonna release this soon, 2.6.1 only so far, you will get a free copy :)
    Scene runs at 60 fps without clouds, with about 40 clouds, each has around 1000 billboards, it drops to 45. Lens flare is affected by clouds now. I just realise the whole scene has 1M polys, O_O, you gota do something with the water :)



     
    Last edited: Nov 21, 2010
  16. blueice

    blueice

    Joined:
    Dec 9, 2009
    Posts:
    51
    oh, thanks!, looks very good, have you tested it from high altitudes?
     
    Last edited: Nov 22, 2010
  17. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    hey bigkahuna,

    did you read about this? http://fileadmin.cs.lth.se/graphics/theses/projects/projgrid/
    I read recently about render in crysis. From what I understood, they had a oceean mesh in a viewport space, so if I got it correctly, there is no need to really make a ocean object in the world space. I guess, this is how they make huge seemless ocean. I am justing getting into shaders, I want to try this approach.
     
    Last edited: Nov 26, 2010
  18. immFX

    immFX

    Joined:
    Mar 20, 2010
    Posts:
    110
    Any luck with the wake issue? Did you find time to sort this out? If you released a demo project, I could possibly help with the trail renderer...
     
  19. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    @immFX - I sent you a PM
     
  20. obviousjim

    obviousjim

    Joined:
    Oct 11, 2009
    Posts:
    29
    Hey everyone,

    First I just want to thank everyone for collaborating on such a great project. We've been working with the ocean shader for a few months now and am really impressed.

    We've been trying to augment it in a number of ways, one of the main things is adding the ability to have really big waves. We're almost there but have a few glitches to work out.

    Here's how we're doing it. in addition to the normal parameters (wave height, choppiness) we also generate a fractal noise texture. This is passed to the ocean shader and use as a pushmap to modify the entire ocean surface via texture fetches in the vertex shader. This allows each tile to look different across the whole ocean, but still retain the fine water details from the wave simulation.

    It works for the most part, but there are some strange side effects that I don't know how to get rid of.

    First, when we modify the wave heights, the tiles closest to the camera start to get culled at certain camera positions leaving big gaps in the ocean. I've tried changing the clipping planes, and evening turning Cull Off in the ocean shader, but they still start disappearing as soon as the noise offset starts to go into effect.

    The second is the reflections start to leave the underside of our little boats as soon as we deform the ocean mesh substantially. I don't totally understand how the reflections are working, so i'm not sure how to approach fixing this.

    I'll clean up an example project and post it so people can see how it's working, but if anyone has any quick thoughts in the meantime or has done something similar any help would be appreciated!

    I've added a few screenshots to illustrate what we are doing. Example project to follow,
     

    Attached Files:

  21. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    not bad! keep it up, I posted above about the viewport grid, I think this could be a solution to the current ocean. And just noise texture alone would be fine from what I see in the example demo.
     
  22. ToreTank

    ToreTank

    Joined:
    Jun 23, 2008
    Posts:
    165
    Regarding the mesh culling, I don't think it is a shader issue, but rather that Unity culls the mesh itself because the bounding box of the mesh isn't updated each frame when the vertices are updated. There are, as far as I know, two ways to fight this (if my suspicion about the problem being mesh culling is correct):

    1) Set the bounds of the mesh after each mesh update - I haven't tried to test how expensive this is, but it can be quickly tested by adding a "meshLOD.RecalculateBounds();" at the bottom of this loop, which in my version of the ocean script is around line 830.

    Code (csharp):
    1.  
    2. for (k=0;k< (tiles_LOD[LOD] as Array).length ;k++) {
    3.     meshLOD = (tiles_LOD[LOD] as Array)[k] as Mesh;
    4.     meshLOD.vertices = verticesLOD;
    5.     meshLOD.normals = normalsLOD;
    6. //  meshLOD.uv = uvLOD;
    7.     meshLOD.tangents = tangentsLOD;
    8.  
    9. //Test recalc bounds here.
    10. }
    11.  
    12.  
    2) Set a fake bounding box at the start of the simulation that encloses the possible movement of all vertices. This is most likely the best thing to do performance-wise, but then again might not be necessary if the RecalculateBounds-thing is fast enough to do each frame. This has been done already in the script for the invisible mesh that controls the reflection/refraction rendering to fire the OnWillRenderObject event. Check out line 482 in SetupOffscreenRendering - here the bounds for the mesh are calculated prior to making it zero-sized to "hack" invisibility.


    Your reflection offset might be very challenging to fix, although you *might* be able to do some clever stuff in shaders to match reflection offset with actual water height at a given point (just a thought, don't have anything to back this up with). The reflections are planar, so they are only somewhat correct on a completely calm ocean. The more waves, the more offset/wrong they will become, unfortunately.


    @nikolai: The paper you posted above is indeed very interesting, and would be very cool to try out some day :) It has been mentioned in this thread before, but noone has so far picked up on it, I think. I don't think it will help on the offset reflection issue, though, will it?
     
  23. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    I am not ab big specialist in shaders, just started. From what saw in their demo - no problem with reflections, and what is more important, the ocean looks ok from all heights.
     
  24. obviousjim

    obviousjim

    Joined:
    Oct 11, 2009
    Posts:
    29
    Thanks for the tip ToreTank! It worked like a charm.

    All I added was this:

    Code (csharp):
    1.  
    2. meshLOD.bounds = Bounds(meshLOD.bounds.center, Vector3(meshLOD.bounds.size.x, currentConditions.swellHeight*2, meshLOD.bounds.size.z));
    3.  
    As I think about the reflections, when it's so stormy I don't think they are really necessary. I think i'll come up with a way to fade them out as the water gets choppy. They look the best on very calm water anyway. It'll work for now at least =)
     
  25. obviousjim

    obviousjim

    Joined:
    Oct 11, 2009
    Posts:
    29
    Hey everyone,

    We've developed a method for buoyancy that lets even small objects float very accurately on the surface of the water.

    We had to take into account the wave choppiness, as this also adds distortions to the mesh, and will cause small objects to jump out of the water if it's not considered.

    We also had to incorporate an interpolation scheme on the GetWaterHeightAtLocation function since we are sampling this position directly. I had seen a few todo items in previous scripts to do this, so I hope this is some extra help

    To work with this function, first add these two functions to Ocean.js:

    Code (csharp):
    1.  
    2. function GetWaterHeightAtLocation (x : float, y : float)
    3. {
    4.     x = x / size.x;
    5.     x = (x-Mathf.Floor (x)) * width;
    6.    
    7.     y = y / size.z;
    8.     y = (y-Mathf.Floor (y)) * height;
    9.  
    10.     //do quad interp
    11.     var fx = Mathf.FloorToInt(x);
    12.     var fy = Mathf.FloorToInt(y);
    13.     var cx = Mathf.CeilToInt(x)%width;
    14.     var cy = Mathf.CeilToInt(y)%height;
    15.    
    16.     //find data points for all four points
    17.     var FFd = data[width * fy + fx].Re * scale / (width * height);
    18.     var CFd = data[width * fy + cx].Re * scale / (width * height);
    19.     var CCd = data[width * cy + cx].Re * scale / (width * height);
    20.     var FCd = data[width * cy + fx].Re * scale / (width * height);
    21.    
    22.     //interp across x's
    23.     var h1 = Mathf.Lerp(FFd, CFd, x - fx);
    24.     var h2 = Mathf.Lerp(FCd, CCd, x - fx);
    25.    
    26.     //interp across y
    27.     var oceanheight = Mathf.Lerp(h1, h2, y - fy);
    28.     return oceanheight;
    29. }
    30.  
    31. function GetChoppyOffsetAtLocation (x : float, y : float) : float
    32. {
    33.     x = x / size.x;
    34.     x = (x-Mathf.Floor (x)) * width;
    35.    
    36.     y = y / size.z;
    37.     y = (y-Mathf.Floor (y)) * height;  
    38.    
    39.     //do quad interp
    40.     var fx = Mathf.FloorToInt(x);
    41.     var fy = Mathf.FloorToInt(y);
    42.     var cx = Mathf.CeilToInt(x)%width;
    43.     var cy = Mathf.CeilToInt(y)%height;
    44.    
    45.     //find data points for all four points
    46.     var FFd = data[width * fy + fx].Im * choppy_scale / (width * height);
    47.     var CFd = data[width * fy + cx].Im * choppy_scale / (width * height);
    48.     var CCd = data[width * cy + cx].Im * choppy_scale / (width * height);
    49.     var FCd = data[width * cy + fx].Im * choppy_scale / (width * height);
    50.    
    51.     //interp across x's
    52.     var h1 = Mathf.Lerp(FFd, CFd, x - fx);
    53.     var h2 = Mathf.Lerp(FCd, CCd, x - fx);
    54.    
    55.     //interp across y
    56.     return Mathf.Lerp(h1, h2, y - fy);     
    57. }
    58.  
    Then attach this script to any objects you want to float:

    Code (csharp):
    1.  
    2. /**
    3.  * Buoyant Object Script
    4.  *
    5.  * For use with the Ocean Shader project in Unity3d
    6.  * http://forum.unity3d.com/threads/16540-Wanted-Ocean-shader/
    7.  *
    8.  * Created @ FlightPhase by James George 2010
    9.  *
    10.  */
    11. public var ocean : Ocean;
    12. public var buoyancy : float; //use this var to adjust the level the object floats, 0 is neutral
    13. public var reactsToChoppiness : boolean;
    14. public var printDebug : boolean;
    15. private var basePosition : Vector3;
    16.  
    17. function Start()
    18. {
    19.     //used for choppy offset
    20.     basePosition = transform.position;
    21.    
    22.     if(ocean == null){
    23.         ocean = GameObject.Find("/Ocean").GetComponent("Ocean");
    24.         if(ocean == null){
    25.             Debug.LogWarning("Missing Ocean on Buoyant Object");
    26.         }
    27.     }
    28. }
    29.  
    30. function Update ()
    31. {
    32.     if(ocean == null){
    33.         return;
    34.     }
    35.    
    36.     //in the case we aren't reacting to choppiness let the position change the normal way
    37.     if(!reactsToChoppiness){
    38.         basePosition = transform.position;
    39.     }
    40.    
    41.     var currentTile : GameObject;
    42.     var choppyTranslation = 0.0;
    43.     var targetY = ocean.GetWaterHeightAtLocation(basePosition.x, basePosition.z) + buoyancy;
    44.     var targetX = basePosition.x;
    45.     if(reactsToChoppiness){
    46.         choppyTranslation = ocean.GetChoppyOffsetAtLocation(basePosition.x, basePosition.z);
    47.         targetX += choppyTranslation;
    48.     }
    49.     basePosition.y = targetY;
    50.     transform.position = Vector3(targetX , targetY, basePosition.z);
    51.     if(printDebug){
    52.         Debug.Log("Base Transform " + basePosition + " global space " + transform.position + " choppy translation " + choppyTranslation + " ");
    53.     }
    54. }
    55.  
    56. //use this function if your object reacts to chopiness
    57. function translateBy(translation : Vector3)
    58. {
    59.     basePosition += translation;
    60. }
    61.  
    62. function OnDrawGizmos()
    63. {
    64.     if(printDebug){
    65.         Gizmos.color = Color.blue;
    66.         Gizmos.DrawWireSphere( basePosition, 3 );  
    67.    
    68.         Gizmos.color = Color.red;
    69.         Gizmos.DrawWireSphere( transform.position, 2 );
    70.     }
    71. }
    72.  
    Thanks again everyone for all the great code. Hope this bit can help someone out!
     

    Attached Files:

    Last edited: Dec 13, 2010
  26. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    That' awesome, thanks!
     
  27. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
  28. obviousjim

    obviousjim

    Joined:
    Oct 11, 2009
    Posts:
    29
    @bigkahuna yeah, would love to post it on GitHub. I'll make a fork of Trond's repo on my github (https://github.com/obviousjim) and get both the big waves + buoyancy added in. Since the big waves also affect the buoyancy script, i just posted here a version that works for the standard ocean shader.

    Still tweaking out the bugs. I'll post it when it's a bit more stable.
     
    Last edited: Dec 14, 2010
  29. RHD

    RHD

    Joined:
    Mar 30, 2009
    Posts:
    719
    It's looking awesome guys!
     
  30. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    @obviousjim - Awesome stuff. Thanks very much for your contribution!
     
  31. dakka

    dakka

    Joined:
    Jun 25, 2010
    Posts:
    113
    Very, very nice. Keep up the good work.
     
  32. obviousjim

    obviousjim

    Joined:
    Oct 11, 2009
    Posts:
    29
    ahoy crew,

    We've been working on getting the big waves feature added to the ocean - so far so good - now most of the challenge now is with making reflections work. For our application, we only need to be able to reflect individual boats and clouds (no landmasses) so it makes it easier.

    As ToreTank pointed out, the reflections really work best with mostly flat water, since it's essentially assuming the water is on a plane. once we add swells everything goes down the drain.

    We've come up with an alternate approach to flipping the camera around during the reflection pass. Instead we flip all the boats in the scene upside down. This is really close to working well - but still has a few caveats.

    First, we need the boats in the distance that are occluded by swells to not be reflected. I have thought something like adding a material/shader to the ocean during the pass that renders occlusion masking via the backfacing polygons of the wave may be an approach - but we're not quite sure how to do it.

    The other challenge is the parts of the boat that are underwater need to also be occluded/masked out during the render pass. In the screenshot below, the oar is underwater but still reflects since the entire boat geometry is rendered in the reflection texture.

    My idea here was to somehow have a material that shows *only* the geometry under the waves for the flipped boats during the reflection pass, but have the same challenge in thinking about how to structure that in a shader.

    Any ideas or pointers are really appreciated. thanks!!

    here are some screenshots to illustrate what we're talking about:
     

    Attached Files:

    Last edited: Jan 3, 2011
  33. monkeybrother

    monkeybrother

    Joined:
    Oct 14, 2010
    Posts:
    83
    Hi. First of all, thanks for this, it's a really nice script.

    I have a question: Where in the script/shader do I change what direction the "wind" has? Now the waves (or just one layer of the waves?) change direction after a while and I want the waves to just have one direction that doesn't change.
     
  34. VIC20

    VIC20

    Joined:
    Jan 19, 2008
    Posts:
    2,689
    Thank you, I will compare this to my own floating script. I made something similar in August for my iPhone version of the waves.
    I feared lots of floating objects would be a total calculation overkill on mobile devices, but it even runs at 60 fps when I let 60 cubes float on the surface.

    http://www.hessburg.com/60cubes.mov

    When "WOTA - Wolves of the Atlantic" is finally released (which will still take months) I will also provide a download of an iPhone/iPad solution based on the Ocean Waves. (here on the forums or on the unity asset store)

    I directly filmed the iPad screen with my iPhone. It runs at awesome 58-60 fps.

    http://www.youtube.com/user/markhessburg#p/u/0/TlPwNNAGFss

    I also had the bounding box problem, thank you ToreTank, I'll try your solution.
     
  35. VIC20

    VIC20

    Joined:
    Jan 19, 2008
    Posts:
    2,689
    Changed some things on my own iphone floating system. This movie shows it much better.

     
  36. itech

    itech

    Joined:
    Jul 28, 2010
    Posts:
    139
    Hi
    Where I can find your Ocean script.

    Thanks
     
  37. ratamorph

    ratamorph

    Joined:
    Sep 2, 2007
    Posts:
    458
  38. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    The game looks very cool, but it looks like your ocean shader is having some issues (Macbook Pro X1600 graphics, OS X 10.6.6, Firefox 3.6.13):

    $Screen shot 2011-01-27 at 1.32.35 PM.png
     
  39. ratamorph

    ratamorph

    Joined:
    Sep 2, 2007
    Posts:
    458
    Do you happen to be running the game on a secondary screen of a dual screen setup? I have issues when I do that.
     
  40. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Nope, just the Macbook Pro, no external screen.
     
  41. ratamorph

    ratamorph

    Joined:
    Sep 2, 2007
    Posts:
    458
    Strange I'll have to test that on my mbp when I get home. Did you tried to lower the graphics quality on the settings menu? that might help.
     
  42. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Just gave that a try, no difference.
     
  43. ratamorph

    ratamorph

    Joined:
    Sep 2, 2007
    Posts:
    458
  44. KillerSneak

    KillerSneak

    Joined:
    Jan 9, 2011
    Posts:
    38
    Uhh okay I'm a bit lost now. Where's the latest download to the ocean shader? I'm up to page 18 and still can't find an all in one pack...? Thanks in advance.
     
  45. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
  46. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    All the work on this thread is amazing. I keep trying to find an excuse to use it. Beautiful stuff and really clever solutions. Great community. Great effort.
     
  47. U2

    U2

    Joined:
    Aug 12, 2008
    Posts:
    216
    Just because I love you guys so much I'm going to throw in on this project some of the work arounds and changes I'm making in order for it to fit my own project. First of all let me share something that works for me.. is it the most elegant solution? I have no idea but I hope this helps...

    This is the depth mask shader fix for Unity 3.0 which works with my boat. So basically say you have a row boat and as your buoyant boat bobs up and down in the water the water mesh enters the boat looking like you are about to sink. How rude. Well this is how I got rid of it...

    Instructions:
    Create an inner hull mesh.. can be as simple as a cube covering the top of your deck. Then create a material and assign it the depth mask shader below and assign that to the cube. Once this is done create another material and assign it the boat hull shader below. Then assign this material to the hull of your boat. Should work fine after this.

    In a nutshell the Depth Mask shader provided here is the same as in the Unity Wiki except it is "Queue" = "Geometry-101" instead of "Geometry+10" and the boat hull shader is just the default defuse shader with "Geometry-102" just one below the depth shader added to the tags line. So if you are partial to your own bump etc.. boat hull shader just add the -102 and you should be good to go. Now I should mention if the water goes over your depth mask mesh that you have over your hull then the water will show up again submerging your boat. This is because your boat is sinking and you should probably mess around with your buoyancy settings some more.

    Hope this helps someone =). If you have a more elegant solution please let me know :cool: peace out
     

    Attached Files:

  48. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    I've run into an issue with the current version of the shader (the one posted on Trond's website) when using non-ocean related rendertextures. What happens is that the additional rendertexture is added to the ocean's refraction camera. I've tried separating the non-ocean rendertexture to a separate layer and then modifying ocean.js to not render those layers, but for some reason it still ends up in the refraction camera's view. The only fix has been to turn off refraction. Anyone else run into this and found a way to fix it?
     
  49. U2

    U2

    Joined:
    Aug 12, 2008
    Posts:
    216
    Hey just wondering does anyone have an old version of this that still includes the wind direction code? I have been trying to add it back in but has been a horrible mess. I even tried rotating the entire ocean object slowly on a Lerp but then it breaks the LOD tiling system not to mention finding position for buoyancy etc..
     
  50. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    @rutecht - You're reading my mind, adding a wind direction function is on my to-do list as well. If anyone's done this already I'd like to sneak a peak at it as well. As a back up plan, I was thinking I might rotate the entire world if I had to (although that would be a lot of work :p ). I don't have the code in front of me but while I'm thinking about it, isn't there an x / y component to the waves direction already? If so, couldn't we use some basic trigonometry and turn that into a wind direction?