Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

matrix rotation for uvs

Discussion in 'Shaders' started by Jessy, Oct 18, 2009.

  1. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I'm learning about matrices currently using this cool video series. However, I have not gotten far enough along yet to know if what I am asking about is the right way of going about things.

    What I want is a texture to rotate. I found this other thread, but I think it would be way faster to set up a rotation matrix, and multiply the UVs by it. What I don't know is whether or not it it possible to do this efficiently and still make it framerate-independent. That is, I can pre-create a rotation matrix that rotates by a given amount, but in order to keep the rotating consistent, I think I will need to compute sines and cosines each frame. Is this incorrect?

    Another problem is that the non-iPhone doucmentation doesn't even mention the matrix command, whereas the iPhone docs do.

    Any advice is appreciated, and the more code you can offer, the better, considering I was only introduced to rotation matrices yesterday, so I am not knowledgeable enough to formulate them myself yet.


    Edit:
    Wow, how completely relevant this page is. Should I report a bug about the 2.5 documentation not including the command, then? Also, is this the fastest method of computation?
     

    Attached Files:

  2. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    I don't really see a big problem in constructing a matrix from code each frame, even if that involves a sine and cosine. Would that actually be a bottleneck?


    The website docs will be update with Unity 2.6 release soon.

    Shameless plug: transformation matrices explained.
     
  3. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I'm way too inexperienced to know! :eek: Your lack of belief in that encourages me, however!

    Thanks! That is a good companion to the third lecture of that video series to which I linked.
     
  4. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Jessy, nice find on that course material!!

    IByte
     
  5. Per

    Per

    Joined:
    Jun 25, 2009
    Posts:
    460
  6. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I have yet to absorb all of this information. Can someone tell me if it is possible to have a matrix that rotates the UVs around their center, and if so, how to construct that matrix?
     
  8. apple_motion

    apple_motion

    Joined:
    Jul 2, 2009
    Posts:
    169
    Don't know is it helpful to you. I use this code to do all kind of rotation in Cg sharder.

    Code (csharp):
    1. float4 v = somewhere;
    2. //v = mul(_Object2World, v); // enable this ...    
    3.   v = mul(rotate(ws_rot, ws_pos), v);
    4. //v = mul(_World2Object, v); // ... and this for rotate in world space, if needed
    Code (csharp):
    1. float4x4 rotate(float3 r, float4 d)
    2. {
    3.     float cx, cy, cz, sx, sy, sz;
    4.     sincos(r.x, sx, cx);
    5.     sincos(r.y, sy, cy);
    6.     sincos(r.z, sz, cz);                   
    7.     return float4x4( cy*cz,   -sz,    sy, d.x,
    8.                        sz, cx*cz,   -sx, d.y,
    9.                       -sy,    sx, cx*cy, d.z,
    10.                         0,     0,     0, d.w );                
    11. }
    12.  
    Since GPU always using float4 internally, I guess, it will not cost more even only process float2 object. ie UV :)

    Antonio Hui
     
  9. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Yes, it's possible. Start at Q26 on the page that Per linked you.
     
  10. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    That hint is not helping me. There's a ton of information in that PDF, and I hardly understand any of it yet.

    Antonio, I'm not sure how to incorporate that into a fixed function pipeline.
     
  11. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Did you read from Q26? It's entitled "What is a rotation matrix?" and it explains them in two dimensions, then in three, and shows you where to plug your values in for rotations about the three base axes. Because you want your coordinates in the XY plane, you should be generating a matrix that rotates around the Z axis. If the terms aren't clear, start from Q1. It's a very good document, and while it is long because it covers a lot of different things, you should be able to get everything you need out of it by reading the appropriate sections.

    If you don't want to understand rotation matrices, just use the ready-made example on the page you already linked: Material.SetMatrix(), which uses the matrix command for fixed function that you already found. You can put a non-zero vector as the first argument to Matrix.TRS() if you want an offset. If you want to rotate about another point, you'll have to generate three matrices: one to translate to the rotation point, another to rotate, and a third to translate back from the rotation point. Matrix.TRS() is a really easy-to-use function for creating transformation matrices.
     
  12. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Daniel, I'm sorry! I misread this statement:

    I didn't realize that "Per" was a person who posted in this thread, and thought you were just using an odd phrasing "per linked you", to say, "that you linked". I was trying to glean information from the PDF to which I linked, starting on page 26! I didn't find anything useful there! :oops:

    Of course I do! :D I got my first taste of matrix rotation from that iTunes U video, and I think I at least understand rotating in 2D, about the origin, fairly well, now.

    The fixed function matrix command only allows for multiplication by a single matrix, as far as I can tell. I took what you answered before to mean that there is a way to use a single matrix to rotate a point about an arbitrary origin, not just the actual origin. Please let me know if you know if that is true or false.

    As for now, I've come up with a hack. I set up a matrix in the Inspector, as shown below. I set up UVs in my modeling program, and after I'm happy with them, translate them by (-.5, -.5). Then I use this code, and get the result I want. The translation is built into the rotation matrix, so I only need to use one in total.

    I'd love some advice about how to improve things, without having to modify UVs on the CPU. I really only use Unity iPhone, so CG/GLSL is not an option.

    Code (csharp):
    1. void Update ()
    2. {  
    3.     timeXMagicRate = Time.time * magicRate;
    4.     sine = Mathf.Sin(timeXMagicRate);
    5.     cosine = Mathf.Cos(timeXMagicRate);
    6.    
    7.     pulsateMatrix[0] = pulsateMatrix[5] = cosine;
    8.     pulsateMatrix[4] = -sine;
    9.     pulsateMatrix[1] = sine;
    10.     material.SetMatrix("_PulsateMatrix", pulsateMatrix);
    11. }
     

    Attached Files:

  13. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    You can definitely represent a rotation about an arbitrary point with just one matrix, but the way I would do it is to create those three matrices and multiply them together. There is probably a way to generate the final matrix all at once, without using the intermediate matrices. I just don't know how to do that. However, you're not generating the matrix per-vertex or per-fragment, so the amount of work you do to generate it every frame doesn't really matter. Multiplying three matrices together sixty times a second or whatever is pretty cheap.
     
  14. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I tried doing that a couple days ago and had no success. I'll keep doing what I'm doing unless someone can teach me how to construct it.
     
  15. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    This code shows how to make a translation matrix, a rotation matrix, and the inverse translation. We multiply them right-to-left to perform the steps in order. Note that the translation is negative because we are moving everything such that this point is now at the origin, about which the rotation will occur. The inverse translation is just the original vector, as we move everything back.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class RotateUVs : MonoBehaviour {
    5.     public float rotateSpeed = 10f;
    6.     public Vector2 pivot = new Vector2(0.5f, 0.5f);
    7.    
    8.     protected void Update() {
    9.         // Construct a rotation matrix and set it for the shader
    10.         Matrix4x4 t = Matrix4x4.TRS(-pivot, Quaternion.identity, Vector3.one);
    11.        
    12.         Quaternion rotation = Quaternion.Euler(0, 0, Time.time * rotateSpeed);
    13.         Matrix4x4 r = Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one);
    14.        
    15.         Matrix4x4 tInv = Matrix4x4.TRS(pivot, Quaternion.identity, Vector3.one);
    16.         renderer.material.SetMatrix("_Rotation", tInv*r*t);
    17.     }
    18. }
     
  16. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Very cool. Thank you. However, I would like to understand it better. I observed that the values for the translation of x and y in the final matrix are clamped by .5 plus or minus the square root of .5, which makes some intuitive sense to me, considering that's the farthest that any corner in 0 to 1 space can get. However, I do not yet understand the relationship that they have to the sin and cosine waves. It seems like it might take twice as long for their "waves" to propagate as do the "rotation waves"?

    Also, what does "protected" do for Update()? I've never used protected before, and from reading the MSDN page, it wasn't apparent to me how it would be useful.

    Also, the reason that my previous efforts failed is that even though I constructed the matrices the same way you did, I multiplied them in the reverse order, because I still don't have a wonderful grasp of them.
     
  17. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Okay, so I just multiplied out the matrices using a pencil and paper.

    matrix[12] = -.5 * (cosine - sine) + .5;
    and
    matrix[13] = -.5 * (cosine + sine) - .5;

    Either one of those equals -cosine minus the other. Handy.

    So, if I don't want to offset my UVs to start off with, I can do a couple calculations, but by just offsetting the UVs to begin with, I can keep the code cleaner and have it execute a little faster. So I'll stick with that for now, and use this kind of thing if I want to have the center of rotation be dynamic. Thanks for the enlightenment!

    (I actually tried modulating the center of rotation by a sine wave, and that almost made me throw up from motion sickness! :eek:)
     
  18. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I really don't try and understand concatenated matrices except as products of rigid transforms. They can get arbitrarily complicated, and breaking them down will usually make a lot more sense.

    That's just a standard thing in my Update macro. It hardly ever matters, but in cases where you're inheriting from that script it makes sure you don't accidentally override the parent's function. The compiler requires that you make the override explicit, so you'll get an error if you do it unintentionally. The take-away is that functions should be protected unless they have to be something else.
     
  19. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    This wasn't the case, by the way. The waves I listed in my last post were just out of phase with sine cosine, so it was hard to figure out what was going on, just by watching floats update in the Editor.
     
  20. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    If you're using a Mac, then a very useful programming tool is Grapher (in Applications/Utilities). You can type in one or more functions and see the graph plotted. This is very handy for sanity-checking formulae you are unsure about, seeing the relationship between trig functions, etc. Highly recommended!
     
  21. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Thanks - I actually use it all the time! Including some testing for this thread.
     
  22. Spinex

    Spinex

    Joined:
    Jul 7, 2009
    Posts:
    23
    So what would I have todo to make a simple scroll to use on a diffuse transparent texture?
     
  23. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Material.SetTextureOffset
     
  24. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I tried implementing the texture rotation from the Unity page, as below:

    Code (csharp):
    1.         var rot = Quaternion.Euler (0, 0, Time.time * 5);
    2.         var m = Matrix4x4.TRS (Vector3.zero, rot, Vector3(1,1,1) );
    3.         drawingMaterials[drawing].SetMatrix ("_Rotation", m);
    This is within an update function. I added the matrix [_Rotation] line in my shader as follows:

    Code (csharp):
    1.         SetTexture [_BaseTex] {
    2.             matrix [_Rotation]
    3.             Combine previous lerp (previous alpha) texture
    4.         }
    However, nothing rotates. drawingMaterials is an array of materials that are being used for visible objects in the scene. They just render as normal with no rotation? There are no error messages. ???

    I modify these materials in other ways, e.g. I change the texture offset every frame which works fine. I just can't seem to get anything to rotate?
     
  25. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Are you sure that you spelled _Rotation exactly the same in both places? If not, an error won't be generated (I wish it were and will probably file a bug report now that I think about it), and it won't work.

    Also, here's something you may find handy, based on that code.
    http://unity3d.com/support/documentation/ScriptReference/Vector3-one.html

    Also, are you sure previous alpha is not 1? If so, that may cause this. (I believe the matrix command trickles down into later texture stages but doesn't permeate into the previous ones. Not sure on that though, and the behavior may have changed over time as well, as it was never documented.)
     
  26. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Hey. Well, the code above is a direct copy paste, the spelling and use of capitalization is exactly the same.

    This is the final texture stage after 3 other textures - the alpha is known to contain content. What I should be seeing is most of the screen content drawn with a rotating texture a bunch of rectangles showing a different texture. The shader works, just not the rotation.

    I'll check out that link, thanks.

    I also tried going straight to the meshes and accessing their renderer.material.SetMatrix but it still doesn't rotate.

    It might be that I am creating the matrix, assigning it to a material, then overwriting it with a new one for the next material... unless Unity copies the matrix into the material I would be destroying all but the last matrix. I'll have to check that.
     
  27. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    1 (white) is "content". When you just use this, do you still see no rotation, then?

    Code (csharp):
    1.         SetTexture [_BaseTex] {
    2.             matrix [_Rotation]
    3.             Combine previous alpha
    4.         }
     
  28. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Well, I don't know what is up with my other computer but on this one it's working.

    At first I thought it was doing something odd because the entire texture-covered environment seemed to have its texture scrolling in one direction.

    However, I navigated to one corner of the environment and saw that the entire environment's textures are rotating around that corner. I couldn't figure this out at first. I couldn't wrap my head around why, when my environment comprises 256 meshes, with 16 different materials each containing the rotation code, that it would all seem to be rotating around the single corner point.

    Then I thought, maybe I was accidentally trashing all but the last matrix, but that was not the case. Adding some randomness per material indicated that each material section is in fact rotating individually with its own rotation.

    So the strange thing is that you can make it appear as though every instance of the texture is rotating around a single point, even if that texture is used across completely separate meshes and materials, simply by using that above code which rotates around the point 0,0.

    And that's when it hit me .. of course, a rotation all by itself will be rotating around whatever is the 0,0 coordinate, regardless. To rotate around a local point, or a center point, per texture usage, you have to also add a `translate` operation to the final matrix, ie translate to the desired center of rotation and then rotate.

    It's good to know though that if you want every texture instance/mesh to share the same appearance of rotation, you can just make them all rotate around the same center coordinate - which is actually what I might want to do :) Just gotta figure out how to add a translation to move the center.
     
  29. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    ImaginaryHuman, just read through this thread. It's primarily dealing with exactly the issue you just mentioned.

    Start Here.
     
  30. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Thanks, I think I got it working.
     
  31. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,817
    Hi there,
    Just checking out this thread, and it is extremely helpful for what I'm working on now, so thanks!
    I have a question though, does anyone know a way to allow the rotation to be pivoted from arbitrary points without popping when you change the pivot values? I was trying to build up a script where you rotate/scale transform a texture based on touch input (Like the photo library on iPad), but i can't figure a good way to pin that rotation dynamically. This has moved my script from "just graspable" to "total brain meltdown" for me so if anyone knows a solution it would be great. Otherwise I'll just settle with what I have :)
    Thanks
    Pete

    Here's my script -

    Code (csharp):
    1. var NewRotation : float;
    2. var NewScale : float;
    3. var NewTransform : Vector3;
    4. var Offset : float = 1;
    5.  
    6. var UpdateTexture : boolean;
    7.  
    8. function Start(){
    9.     InvokeRepeating("RotatateTexture2",0,.1);
    10. }
    11.  
    12. var pivot : Vector2 = Vector2(0, 0);
    13.  
    14. function RotateTexture2(){
    15.     var MT : Matrix4x4 = Matrix4x4.TRS(-pivot*NewScale, Quaternion.identity, Vector3(NewScale,NewScale,NewScale));
    16.     var rot = Quaternion.Euler (0,0,NewRotation);
    17.     var MR : Matrix4x4 = Matrix4x4.TRS(Vector3.zero, rot, Vector3.one);
    18.  
    19.     var Minv : Matrix4x4 = Matrix4x4.TRS(pivot, Quaternion.identity, Vector3.one);
    20.     renderer.material.SetMatrix ("_RotateMatrix", Minv*MR*MT);
    21. }