Search Unity

How to get an absolute object-space position when draw calls are batched?

Discussion in 'Shaders' started by drbn, Mar 9, 2021.

  1. drbn

    drbn

    Joined:
    Oct 4, 2020
    Posts:
    5
    My goal right now is to sample a light texture in urp (in a material used by a 2d sprite renderer) relative to the pivot position of an object as opposed to the fragment position. To get the "pivotUV" I have something like this in my vertex function:

    Code (CSharp):
    1. float4 pivotPositionCS = TransformObjectToHClip(float3(0,0,0));
    2. float4 normalizedPivotPositionCS = pivotPositionCS / pivotPositionCS.w;
    3. o.pivotUV = ComputeScreenPos(normalizedPivotPositionCS).xy;
    However, when I output that value in my fragment function like
    Code (CSharp):
    1. return(SAMPLE_TEXTURE2D(_ShapeLightTexture0, sampler_ShapeLightTexture0, i.pivotUV));
    Everything works as expected with some cases, but other instances seem to end up having their output tied together and are sampling relative to the world space coordinate (0,0,0) as opposed to the object-space coordinate (0,0,0). I took a look at the frame debugger and the objects that are drawing incorrectly are the ones that are getting batched together in a "Draw Dynamic" call.

    This makes some sense after reading about how unity implements dynamic batching:
    What's the best way for me to achieve what I want? I can't actually think of a way to make this work that doesn't involve disabling batching. If there's a way to make the shader aware of if it's being batched, I could pass in the world-space coordinate of it's origin point, but then if I understand correctly, that would require enabling gpu instancing which would ultimately disable any batching.

    Is there any way to make my shader aware of when it's being batched so that I can transform my (0,0,0) point to world-space when it's batching draw calls?
     
    Last edited: Mar 9, 2021
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
  3. drbn

    drbn

    Joined:
    Oct 4, 2020
    Posts:
    5
    hey @bgolus thanks for pointing me at that thread, looks like that would easily solve my problem of passing data down to my shader. However it seems like there's no way to make the shader aware of when it's being batched (tbh I'm not fully confident this is a sensible question)?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    The shader has no knowledge of if it’s being used on a batched mesh or not, no. That’s sort of the whole point of batching is to be able to render “multiple” objects as a single mesh without having to do anything special to the shader. That is of course assuming the shader doesn’t need to know the object space transform of the individual objects that are now combined into a single mesh with the transform matrix set to “nothing” (more specifically an identity matrix, which has no rotation, scale, or translation). To use the above technique you have to have the shader and your code assume you’re always baking the relevant data into the vertices.
     
  5. drbn

    drbn

    Joined:
    Oct 4, 2020
    Posts:
    5
    okay so now I"m just asking for my own education, but doesn't it still make sense for a shader to be able to be "batch-aware"? If a shader was aware that it was being batched, it would still behave uniformly. Seems like it would work well with a multi_compile directive?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    Unity certainly could have added a multi compile to special case out when something was being batched, but they built the whole batching system to not need it, so they didn’t. None of Unity’s official shaders require it knowing the object’s position, and generally you want to have as few multi compiles as possible as they can quickly increase the number of variants required (each new multi compile doubles the number of variants) so there usually has to be a darn good reason for it.