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

_Object2World is zero when cross compiling to glsl

Discussion in 'Shaders' started by goldbug, Dec 20, 2011.

  1. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    When using cg to glsl cross compile in windows, and starting the standalone player with -force-opengl, the matrix _Object2World is all zeroes at the vertex shader, regardless of the object's transformation.

    This is in Unity 3.4.2 on windows 7 32 bit on an built in Intel(R) HD Graphics 3000.

    Is this a known problem?
     
  2. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    I've seen this before and would appreciate a solution to the problem.

    If you have another graphics card in your computer, you should activate it manually (if possible). This usually solves the problem.

    As a work-around you can provide the matrix in a uniform via scripting.
     
  3. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    It is a laptop with built in graphic card. Can not activate second graphic card.

    Every object has a different _Object2World matrix, but if I pass it in a uniform, then all the objects would have the same matrix. Or is there a way to set a uniform per object?
     
  4. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    Try Renderer.localToWorldMatrix instead. That is what _Object2World is doing anyways.

    EDIT: i have the same graphics card here on my laptop, but never tried to force opengl so i am not aware of your problem.
     
  5. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    ok, but the question is how to make Renderer.localToWorldMatrix available inside the shader.
     
  6. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    915
    Per object?

    You can't. The graphics card is rendering multiple pixels in parallel so there's no way to stop in-between drawing each individual object and update variables from the application side, it's a fire and forget kinda deal. At least as far as I know.

    Trying to deal with a similar issue myself (not with GLSL) with dynamic batching, I was using _Object2World as a key transform in certain kinds of effects but when batched it ends up being identity.
     
    Last edited: Dec 21, 2011
  7. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    Why cant you do it per object?

    Just add a script to your object and call;
    this.material.SetMatrix(myfancymatrix, this.renderer.localToWorldMatrix) ??
     
  8. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    The thing is: _Object2World is a uniform like other uniforms; thus, whenever a different value is needed, Unity has to issue a new render call. Batching means that the transformation is done in software and _Object2World is set to the identity.

    What paulp describes is a bug that sets the matrix to all 0s.
     
  9. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    915
    The major problem with this is that every time you do it you're creating a new instance of the material so you break all batching and the amount of draw-calls suddenly skyrockets.

    Martin, I understand that, was just saying the outcome was similarly frustrating to deal with in my particular case, sorry if I didn't make that clear. I probably shouldn't have mentioned it as I don't want to obscure the real issue the OP has here which is a genuine GLSL bug.
     
  10. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Actually, I assume it is a bug in Intel's OpenGL driver. Or at least a very strange behavior.
     
  11. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    Eh,
    In your funky custom shader, define the below float4x4;

    uniform (or not uniform doesnt matter at all) float4x4 _MUYCUSTOMOBJECT2WORLD;

    then, just add this generic script to every one of your funky custom shader.
    void OnRenderObject() {
    this.sharedMaterial.SetMatrix(_MUYCUSTOMOBJECT2WORLD, this.renderer.localToWorldMatrix);
    }
    try and see if it works or not.

    EDIT3: if you use the sharedmaterial, you wont break batching or anything, just for the note.

    Just note that it must be RENDERER.matrix, not TRANSFORM.matrix.

    EDIT: If you want to go nuts with it, you can also try the GL class too.

    EDIT2: If you reeeallly but reallly want to go nuts, just construct your very own custom matrix by the objects angles and/or lookat vector, here are the maths for it:
    angles to axis
    lookat to axis

    EDIT5: Now i just went nuts myself to test this out with the exact conditions as stated above and with the exact same graphics card and when debugging the matrix values, i see nothing wrong with it with -force-opengl. and it works as how it should.
     
    Last edited: Dec 21, 2011
  12. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    I distilled the bug to a small package.

    Build the attached package for standalone, and start it like this:
    Code (csharp):
    1.  
    2. BugDemo.exe
    3.  
    You will see a bunch of colored cubes

    Now start it like this:
    Code (csharp):
    1.  
    2. BugDemo.exe -force-opengl
    3.  
    The cubes are all black now.

    Thank you very much for your other ideas, I will try them.
     

    Attached Files:

  13. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    I didnt test it but at first sight, you should remove this line:
    #pragma only_renderers d3d9

    EDIT: Okay i tested on my laptop too and it works when you remove that line.
     
  14. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    That's only if you have a (particular?) integrated Intel graphics chip and/or Intel OpenGL driver. Usually, it will work just fine.
     
  15. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    I put that there on purpose to make the first subshader unelectable when using -force-opengl. Currently when you do the -force-opengl you are forcing it to pick the second subshader. (Unless there is something I ignore).

    If I remove that line, then the first subshader would be picked for -force-opengl, and it would be compiled to ARB (the default) instead of GlSL, which works. Thus hiding the specific bug that I am trying to show.

    The only difference between the 2 subshaders is that the first one is compiled for direct3d and the second one is compiled for opengl using glsl. This is done with the pragmas.

    In my real game, I do need the extra features of #pragma glsl or #pragma only_renderers d3d9, which is why I cannot simply remove them.
     
    Last edited: Dec 21, 2011
  16. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    766
    That very well may be. That is why I provided my specs at the original posting. If someone else with different hardware can try out my example, it could tell us if the problem is only for my specific hardware/driver.
     
    Last edited: Dec 21, 2011
  17. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    Uhmm..Okay now i see _Object2World comes empty or missing a row or something when using glsl renderer.

    Anyhow, what i suggested first works though.

    Test Shader:
    Code (csharp):
    1. Shader "Custom/MyShader" {
    2.     SubShader {
    3.         Pass {
    4.             CGPROGRAM
    5.             #pragma vertex vert
    6.             #pragma fragment frag
    7.             #pragma only_renderers d3d9
    8.             #include "UnityCG.cginc"
    9.    
    10.             struct a2v {
    11.                 float4 vertex : POSITION;
    12.             };
    13.            
    14.             struct v2f {
    15.                 float4 pos : SV_POSITION;
    16.                 fixed4 color : COLOR;
    17.             };
    18.            
    19.             uniform float4x4 _O2W;
    20.  
    21.             v2f vert (a2v v) {
    22.                 v2f o;
    23.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    24.                 o.color = mul(_O2W, v.vertex);
    25.                 return o;
    26.             }
    27.            
    28.             half4 frag (v2f i) : COLOR {
    29.                 return i.color;
    30.             }
    31.             ENDCG
    32.         }
    33.     }
    34.     SubShader {
    35.         Pass {
    36.             CGPROGRAM
    37.             #pragma vertex vert
    38.             #pragma fragment frag
    39.             #pragma glsl
    40.             #include "UnityCG.cginc"
    41.    
    42.             struct a2v {
    43.                 float4 vertex : POSITION;
    44.             };
    45.            
    46.             struct v2f {
    47.                 float4 pos : SV_POSITION;
    48.                 fixed4 color : COLOR;
    49.             };
    50.            
    51.             uniform float4x4 _O2W;
    52.  
    53.             v2f vert (a2v v)
    54.             {
    55.                 v2f o;
    56.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    57.                 o.color = mul(_O2W, v.vertex);
    58.                 return o;
    59.             }
    60.            
    61.             half4 frag (v2f i) : COLOR {
    62.                 return i.color;
    63.             }
    64.             ENDCG
    65.         }
    66.     }
    67. }
    Script to add to your objects:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cMatrix : MonoBehaviour {
    5.     void OnWillRenderObject() {
    6.         renderer.sharedMaterial.SetMatrix("_O2W", transform.renderer.localToWorldMatrix);
    7.     }
    8. }