Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by nyahoon · Nov 29, 2014 at 05:10 PM · shaderscalerenderermatrix

How to make a localToWorld matrix for shader parameters?

Hi, I would like to make a matrix which is passed to a shader like "_Projector" matrix. It was not so difficult if localToWorld matrix had no scale. However, if localToWorld matrix had scale, it seemed like the scale parameters were applied twice to the vertices. To check this problem, I tested the following script:

 using UnityEngine;
 public class ManualTransform : MonoBehaviour {
     void OnWillRenderObject()
     {
         //Matrix4x4 m = renderer.localToWorldMatrix;
         Matrix4x4 m = Matrix4x4.identity;
         m.SetColumn(3, renderer.localToWorldMatrix.GetColumn(3));
         renderer.material.SetMatrix("mL2W", m);
     }
 }

And draw a sphere with this shader:

 Shader "Custom/ManualTransform" {
     SubShader {
         Pass {
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             float4x4 mL2W;
             float4 vert(float4 vertex : POSITION) : SV_POSITION
             {
                 return mul(UNITY_MATRIX_VP, mul(mL2W, vertex));
             }
             fixed4 frag() : COLOR
             {
                 return fixed4(1,0,0,1);
             }
             ENDCG
         }
     } 
 }

Even though, the matrix "mL2W" was identity, the sphere was deformed when I set some scale. It seems like the vertices are already scaled before they are passed to the vertex shader. So, if I use the code in the line comment in OnWillRenderObject, scale parameters will be applied twice.

Here is a workaround:

 public class ManualTransform : MonoBehaviour {
     void OnWillRenderObject()
     {
         Vector3 scale = transform.localScale;
         Matrix4x4 m = renderer.localToWorldMatrix * Matrix4x4.Scale(new Vector3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z));
         renderer.material.SetMatrix("mL2W", m);
     }
 }

However, it doesn't look a good idea, because I'm not sure if the vertices are always scaled before vertex shader.

So, my questions are:

  1. Is it true that vertices of a mesh are scaled before they are passed to a vertex shader?

  2. If so, does it happen always, or under some conditions? Is there any way to check if the vertices are already scaled or not, like renderer.isPartOfStaticBatch?

  3. Shouldn't renderer.localToWorldMatrix have no scale, if vertices are already scaled?

  4. Is there a better way to make a matrix which contains localToWorld matrix with scale?

It would be appreciated if I could get answers for any of them. Thanks in advance!

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by _dns_ · Nov 29, 2014 at 06:10 PM

Hi, those vertices are pre-scaled because you must be using non-uniform scale on the mesh. Unity applies the scale before passing vertices to the shader. Try setting the same scale for all axis to see if this changes something. Also, note that Unity 5 will remove this behavior (info about this in the Unity blog)

You already have access to some transform matrices in the shader, check this page: http://docs.unity3d.com/Manual/SL-BuiltinValues.html.

Also, another effect you may see is the merge of meshes due to dynamic batching: this will change the origin & coordinates of the vertices of batched meshes.

Lastly, if you still have to pass parameters to a shader during play, you may want to check http://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html. It's faster than using the material and is done on one object/renderer only. Using renderer.material like you do duplicates the material for this object, leading to un-batchable meshes, more memory usage etc...

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image nyahoon · Nov 30, 2014 at 08:52 AM 0
Share

Thank you for the answer! I tried uniform scale, then vertices were not pre-scaled! Also, I tried nearly uniform scale, like (10, 10, 9.99999), and vertices were not pre-scaled either. If uniform scale was negative, vertices were pre-scaled. So, I might need to do like this:

     $$anonymous$$aterialPropertyBlock m_propertyBlock;
     void Awake()
     {
         m_propertyBlock = new $$anonymous$$aterialPropertyBlock();
     }
     void LateUpdate()
     {
         $$anonymous$$atrix4x4 m = renderer.localToWorld$$anonymous$$atrix;
         if (IsVertexPrescaled()) {
             Vector3 scale = transform.localScale;
             m = m * $$anonymous$$atrix4x4.Scale(new Vector3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z));
         }
         m_propertyBlock.Clear();
         m_propertyBlock.Add$$anonymous$$atrix("mL2W", m);
         renderer.SetPropertyBlock(m_propertyBlock);
     }
     bool IsVertexPrescaled()
     {
 #if (UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6) // support Unity 4.3 or later
         Vector3 scale = transform.localScale;
         float max = $$anonymous$$athf.$$anonymous$$ax (scale.x, $$anonymous$$athf.$$anonymous$$ax (scale.y, scale.z));
         float $$anonymous$$ = $$anonymous$$athf.$$anonymous$$in (scale.x, $$anonymous$$athf.$$anonymous$$in (scale.y, scale.z));
         return 0.00001f * max < (max - $$anonymous$$);
 #else
         return false;
 #endif
     }

Of course, it is safer to use builtin matrices, like Object2World and UNITY$$anonymous$$ATRIX_$$anonymous$$VP, but I believe using localToWorld matrix is more efficient. What I actually want to do is making a uv projection matrix, like

 $$anonymous$$atrix4x4 m = uvProjection * renderer.localToWorld$$anonymous$$atrix;

I can do this matrix multiplication in vertex shader, but that has GPU overheads. Also I can use UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, and make the matrix m like this:

 $$anonymous$$atrix4x4 m = Camera.current.projection$$anonymous$$atrix * Camera.current.worldToCamera$$anonymous$$atrix;
 m = uvProjection * m.inverse;

It won't have GPU overheads, but needs to update the matrix property for each camera.

I also worried about dynamic batching as you mentioned, and I think, batching is the reason why renderer.localToWorld$$anonymous$$atrix must be used ins$$anonymous$$d of transform.localToWorld$$anonymous$$atrix. I wonder why renderer.localToWorld$$anonymous$$atrix doesn't take into account vertex pre-scale.

Anyway, your answer made the things clearer. Thanks!

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

3 People are following this question.

avatar image avatar image avatar image

Related Questions

How to replicate renderer.worldToLocalMatrix 0 Answers

Depth map from a second camera at a light position 1 Answer

Seamless Water Material on a Plane using Shader Graph 3 Answers

Shader Outline scale problem since U5 1 Answer

Transformation matrix with surface shader 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges