I'm looking to create an effect like this I was thinking about using "projection matrices" or something like that but I have no clue how those work or what they are in detail. I know you can rotate the elements in the UI but that doesn't give a nice smooth cylinder-ish effect, and messes up when using worldspace mode. EDIT: I apologize if the image is inappropriate due to the background, but it was the best image I could find which showed what I was looking for in detail.
Hmm I would definately say you need some sort of post-process effect here. If you don't get an answer perhaps ask the shader lab forum, those guys know their stuff.
Lol... unity releases advanced, highly streamlined visual UI editor and then the first thing people do is try to make it do things beyond it's original intended functionality.
It's not the first thing I've done with it though. And part of it's intended functionality is to be highly customizable.
Sadly I do not own Unity Pro. Any other ideas? Or how I might get the equivalent of a rendertexture on Free.
Just a quick note that this only really works for non-interactible elements, as the hitboxes for buttons etc. don't warp along. Unless you write your own wrapper for this of course. =)
You seriously could have just use a flat menu though? Do you absolutely 100% have to copy this game exactly? You could achieve some really interesting effects with the built-in system
Could you perhaps do this for menus that are meant to be controlled by key presses instead of mouse clicks? Likewise, you could always fake it being shaped that way - merely use graphics which give the impression of it being bent like SAO.
@Midnitte: It would definitely work for keypress-controlled UIs! You could probably even make it work for mouse clicks relatively easy. I'm guessing there's likely a way to get the UV coordinates (or fake them) of wherever the mouse or touch is hovering over, which you could then use to send the appropriate event to the correct UI element through an Event System. Using bent graphics is all fine and well until you need something dynamic, like a moving element, a masked HP bar, or even dynamic text. I'm really curious if a non-pro solution for this exists; following this thread ancipitatedly!
It's not this game perse I just like the visor-like effect it has. I could have posted a screenshot of Borderlands, Dead Space w/e here. It's just that this screenshot shows best what I want.
I actually have a system like what your describing in my current UI. I actually use several masked "segments" and make a copy of each canvas and place them on the tangent angle of the canvas. This gives a very nice illusion of being curved. The quality of this curved is based on the amount of segments and the angle they produced. Of course, you will need to probably write your own UI code like I had to with Listeners. Because you need to have interaction of each segment's copy of the same UI element such as a Button. So if the user hovers over said button, all copies of that button will correctly update. I also wrote plenty of code that helps optimize this solution so you don't have needless copies of a element that weren't in that segment. Even with this solution, I have maintained a very high level of FPS. I think the biggest hindered of this solution was the issue of the Text.
You can't expect an effect like this in Unity free. Most of the ultra spectacular/complex/special visual effects can only be achieved from pro. One alternative to consider for free is building the UI with physics colliders, then implementing all of the UI components you want with physics. You can use the same interfaces and events system. In pro the RenderTexture method would not be too hard to implement. For the interaction you could simply get the point that the user clicked on the texture, convert that back to canvas space, then ray cast into your actual UI and pass the event through there. Unfortunately I don't have pro to play around with this and write you the code. If anyone is willing to pay for my pro licence I can write you a curvy UI script.
I had a go at doing this without using render textures and came up with something that works. In theory this will work in non-pro Unity but I haven't tested it since I have pro installed. These are the problems that need solving: 1) Mapping of input positions so they line up correctly with graphics. 2) Mapping of veritices so they appear in the correct place. 3) Tessellation of UI elements so non-linear transforms can be approximated. This is an essential stage if you cannot use render textures. If you don't do this then the UI will not correctly wrap around curved surfaces. Here is how I solved each stage: 1) Unity UI already has a GraphicRaycaster class. This implements a Raycast method that takes an input event and populates a list of RaycastResults based on things the ray hits. I wrote a class that inherits from GraphicRaycaster and overrides the Raycast method. In the overridden method I pass eventData.position through a mapping and write back the result into the same event. I then call the base class implementation of Raycast to do all the default raycasting work, but with the modified position. 2) Mapping of vertices is all done in a vertex shader. For each type of mapping you need to write a new shader (actually two, fonts are handled separately but they can share code). You don't need to do everything from scratch. Use the default UI shaders available in the built in shaders download as a starting point. Unity UI also provides the IMaterialModifier interface, you can use this to implement a behaviour that sets up all the parameters your vertex shader needs. I actually have a single mapping behaviour that lives on the canvas object. All the UI graphic components have modifiers that fetch the mapping using GetComponentInParent and ask the mapping to configure the material for them. 3) Unity UI has the IVertexModifier interface and the BaseVertexEffect behaviour that can be used to modify geometry it generates. As far as I can tell all the geometry that gets passed to the ModifyVertices method is arranged as quads. Using this assumption you can subdivide the quads based on their size and append the results to the vertex list. You then delete all the old quads from the list. Producing subdivided vertices just involves lots of bilinear interpolation of vertex attributes. A couple more things to mention: As far as I can tell you can't merge stages 2 and 3 into a single stage just using a vertex modifier. This is because vertices are kept in object space for each graphic then merged into big canvas space buffers. Tessellation of the UI elements can introduce a lot of vertices, so be careful, this may impact performance. Here is a cylinder mapped UI using this technique: And a UI distorted by a sin wave: There should also be a demo package attached... HTH
@Gibbonator Did you try adding bending to .z property of uivertex.position? I modified your solution and this work for me. You only have to check data bending (as anim curve or as progammatic equation) on root (usually canvas go) and check canvas position (so you have to convert local position to local position for whole canvas) of generated uivertex and calculate proper bending thanks to this canvas position. What is even better because i dont need custom shader nor raycaster and imaterialmodifier Anyway thanks for sharing solution! without your work i probably never develop this version
@Breyer I did notice that the example in the Unity scripting reference shows that the vertex z component can be changed (http://docs.unity3d.com/ScriptReference/UI.BaseVertexEffect.html). I decided on the shader approach since I wanted to work in canvas space. If you're keeping everything in object space as you describe, then you'll need to call SetVerticesDirty on any graphic when it moves. If your UI isn't moving much then this is probably a better way of doing things. I also like your idea of using an animation curve to define the curvature of the UI
i think this can be also useable for uneven&complex line drawing like in skill tree in many games (PoE for instance). maybe this wont be super effective since this isnt built-in feature but Unity havent line drawing feature for uGUI (only simple line in runtime and complex in editor only but without any support for uGUI) There is need for some changes like better editor script (3x anim curves/programmatic equations for all dimension - optional 1 for .z bending, 1 for .y curvature and optionally 1 for .x offsetting)
Couldn't you do the UI in worldspace and use a camera with a really strong perspective (ie: a fisheye lens)?
Hi @Gibbonator, You have done an awesome job by making these warp/bend curve shaders. I am using your package and I have set everything according to your example scenes but I am facing an issue here. When I assign UI-Cyclinder material to any of UI element the image or sprite disappears and stays disappeared. Please help me in this regard. @SimonDarksideJ Please contribute and help me resolving this issue. Thanks in advance.
Hello, I import your assets to my scene and have error: error CS0117: `UnityEngine.RenderMode' does not contain a definition for `ScreenSpaceCamera'. What is it? and how can i fix? Thanks
Hi, Sorry for the late replies. I've not been on the forums in a while. @asadsohail My guess is the parameters that define the cylinder are not being set on the materials. To ensure this happens you need to do two things: 1) Make sure there is a CylinderMapping script added to the Canvas object at the root of your UI. This holds the parameters that define the cylinder. You should be able to change the parameters using the inspector. 2) Add a MappedGraphicMaterialModifier script to the Graphic object (e.g. Image, Text). This will take the Material that the Graphic uses and ask the CylinderMapping to set the parameters on it. You can also try resetting the CylinderMapping (right click menu in the inspector). This will force all the materials that the graphics use to be marked as dirty. @quenhoai2003 I'm not sure what's going on here. I've tried this in Unity 4.6.4 and it compiles fine. Are you using Unity 5? AFAICT 5 still has ScreenSpaceCamera in the RenderMode enum. As a last resort you can just remove that if statement: Code (csharp): if (m_canvas.renderMode != RenderMode.ScreenSpaceCamera) { Debug.LogWarning("Cylinder mapping works best in ScreenSpaceCamera mode", this); } It's just a warning, not a critical part of the code.
sorry i stopped this project. Im completely sure (especially since UI is mostly open sourced) this is possible but lost interest myself in this project.
@Gibbonator Thanks for this fantastic example, it has really helped me on my VR project I'm having one issue now, which is that I can't seem to locate the distorted ui in world-space to draw my cursor over it: I guess what is happening is the UI mesh geometry is being distorted by the UI-Cylinder material and shader but it's collider remains in non-distorted world space (?) Will experiment and report back if I can get everything working! UPDATE #1: I realized that I wasn't finding the right "screen" space value where my virtual PointerEvent data should be placed. Below is a screenshot where the Yellow line is my new calculation of a custom PointerEventData hitting the warped ui (and the hit is correctly detected): However this calculation is still wrong, the yellow line should start at where the green line (VR focus ray) intersects the canvas plane and stay perpendicular to the plane... will keep at it ^^
hey @Gibbonator This looks really awesome, my question, how would I make the cylinder effect go top to bottom, instead of left to tight?
hi! i m facing following issue in curved ui pkg Assets/CurvedUI/Scripts/TessellationVertexEffect.cs(6,14): error CS0619: `UnityEngine.UI.BaseVertexEffect' is obsolete: `Use BaseMeshEffect instead'
aammfee, you need the core, VertexEffect must inherit from BaseMeshEffect instead of BaseVertexEffect. It doesnt work out of the box anymore
I was giving it a try but i dropped it and implemented another custom solution. If you are developing for windows, if i remember right, you can use tesselation on the GPU and forget about the VertexEffect. (I'm talking from memory, i worked this solution only for a couple of days)
Also notice that when you warp a canvas element your shader will receive the whole canvas, that means that UV coordinate 0,0 is the top left of your canvas, not the element you are bending.
Hey there! Ive made my own solution to the warped canvas problem. It works in unity 5.1 and above and uses object space so you can use it from any direction. If you are wondering if it will suit your needs just ask here or reach me at curvedui[at]chisely.com. I'll be glad to help. Check it out on asset store: http://u3d.as/nmE
Chisely, that is exactly what I'm looking for! Thanks for this. I'm just learning Unity from scratch while learning to program a UI for VR.
I changed the TessallationVertexEffect class to work in Unity 5.4 using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; public class TessellationVertexEffect : MonoBehaviour { #region BaseMeshEffect public void ModifyVertices(Mesh verts) { List<UIVertex> list = new List<UIVertex>(); using (VertexHelper vertexHelper = new VertexHelper(verts)) { vertexHelper.GetUIVertexStream(list); } if (verts.vertexCount == 0) { // Nothing to do... return; } // Assume we are getting quads if ((verts.vertexCount % 4) != 0) { Debug.LogError("Modifier expects vertices to be arranged as quads"); return; } // Tessellate int startingVertexCount = verts.vertexCount; for (int i = 0; i < startingVertexCount; i += 4) { TessellateQuad(list, i); } // Remove old quads from the start of the list list.RemoveRange(0, startingVertexCount); } #endregion void TessellateQuad(List<UIVertex> verts, int vertexIndex) { // Read the existing quad vertices UIVertex v0 = verts[vertexIndex]; UIVertex v1 = verts[vertexIndex + 1]; UIVertex v2 = verts[vertexIndex + 2]; UIVertex v3 = verts[vertexIndex + 3]; // Position deltas, A and B are the local quad up and right axes Vector3 dPdA = v2.position - v1.position; Vector3 dPdB = v1.position - v0.position; // Determine how many tiles there should be float rcpTessSize = 1.0f / Mathf.Max(1.0f, m_tessellationSize); int aQuads = Mathf.CeilToInt(dPdA.magnitude * rcpTessSize); int bQuads = Mathf.CeilToInt(dPdB.magnitude * rcpTessSize); // Build the sub quads float rcpAQuads = 1.0f / (float)aQuads; float rcpBQuads = 1.0f / (float)bQuads; float startBProp = 0.0f; for (int b = 0; b < bQuads; ++b) { float endBProp = (float)(b + 1) * rcpBQuads; float startAProp = 0.0f; for (int a = 0; a < aQuads; ++a) { float endAProp = (float)(a + 1) * rcpAQuads; // Append new quad to list verts.Add(Bilerp(v0, v1, v2, v3, startAProp, startBProp)); verts.Add(Bilerp(v0, v1, v2, v3, startAProp, endBProp)); verts.Add(Bilerp(v0, v1, v2, v3, endAProp, endBProp)); verts.Add(Bilerp(v0, v1, v2, v3, endAProp, startBProp)); startAProp = endAProp; } startBProp = endBProp; } } #region Interpolation // TODO: This could all be optimised by calculating the four weighting factors once // and re-using the result for all attributes UIVertex Bilerp(UIVertex v0, UIVertex v1, UIVertex v2, UIVertex v3, float a, float b) { UIVertex output; output.position = Bilerp(v0.position, v1.position, v2.position, v3.position, a, b); output.normal = Bilerp(v0.normal, v1.normal, v2.normal, v3.normal, a, b); // Bilerping w is almost certainly not the right thing to do here output.tangent = Bilerp(v0.tangent, v1.tangent, v2.tangent, v3.tangent, a, b); output.uv0 = Bilerp(v0.uv0, v1.uv0, v2.uv0, v3.uv0, a, b); output.uv1 = Bilerp(v0.uv1, v1.uv1, v2.uv1, v3.uv1, a, b); output.color = Bilerp(v0.color, v1.color, v2.color, v3.color, a, b); return output; } float Bilerp(float v0, float v1, float v2, float v3, float a, float b) { float top = Mathf.Lerp(v1, v2, a); float bottom = Mathf.Lerp(v0, v3, a); return Mathf.Lerp(bottom, top, b); } Vector2 Bilerp(Vector2 v0, Vector2 v1, Vector2 v2, Vector2 v3, float a, float b) { Vector2 top = Vector2.Lerp(v1, v2, a); Vector2 bottom = Vector2.Lerp(v0, v3, a); return Vector2.Lerp(bottom, top, b); } Vector3 Bilerp(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, float a, float b) { Vector3 top = Vector3.Lerp(v1, v2, a); Vector3 bottom = Vector3.Lerp(v0, v3, a); return Vector3.Lerp(bottom, top, b); } Vector4 Bilerp(Vector4 v0, Vector4 v1, Vector4 v2, Vector4 v3, float a, float b) { Vector4 top = Vector4.Lerp(v1, v2, a); Vector4 bottom = Vector4.Lerp(v0, v3, a); return Vector4.Lerp(bottom, top, b); } Color Bilerp(Color v0, Color v1, Color v2, Color v3, float a, float b) { Color top = Color.Lerp(v1, v2, a); Color bottom = Color.Lerp(v0, v3, a); return Color.Lerp(bottom, top, b); } #endregion [SerializeField] float m_tessellationSize = 10.0f; }
@chisely I bought this assetCurved UI - VR Ready Solution To Bend / Warp Your Canvas! to work with Oculus Gear VR and found a problem. As It only bends Graphics (and displace them from actual UI elements position), so interaction doesn't work well with Oculus Gaze input for certain UI items. Because UI interaction detects rect transform instead of displayed image. If user is click at button graphic, nothing happens, if user clicks at position where rect transform is placed it works. creates annoying behaviour as sometimes I point and click on one button and next button receives clicks. Please fix it asap
Seems like the output variable needs to be assigned. UIVertex output = new UIVertex(); But sliders and buttons are not bent?
Hello all, Curve your UI or Canvas I have made a solution to curve your canvas. It is highly optimized for both pc and mobile devices. Curve your canvas with in seconds. Lowest cost solution on asset store. Works with both unity personal and pro versions. Check it out on asset store: https://www.assetstore.unity3d.com/en/#!/content/104725 Check it out on YouTube also: For any query drop email to helicalstudio@gmail.com. I'll be glad to help. Thanks.