Set object draw layer within Immediate Mode

Avatar
  • updated
  • Declined

I think it'd be super useful if we could set what layer an Immediate Mode Draw happens on with something like: `Draw.Layer = LayerMask.NameToLayer("UI");'

Or if it just draws to the layer of the object the script is attached to, like components already do:

Image 3

Image 4

Reporting a bug? please specify Unity version:
Reporting a bug? please specify Shapes version:
Reporting a bug? please specify Render Pipeline:
Pinned replies
Avatar
Freya Holmér creator
  • Answer
  • Declined

Render layers are a camera property, and immediate mode isn't tied to cameras or even game objects, so layers don't always exist in these contexts. You can, for instance use immediate mode to draw to render textures, completely cameraless

so, in order for immediate mode to do this, it would have to:
A) know what layer to use for each draw call
B) know what layer mask the current camera is using
C) check layers in every draw call

A would be your proposed Draw.Layer
B is harder. It could be another property like Draw.LayerMaskContext, but it's a little esoteric and would have to be assigned manually

C means there will have to be a mask check in every Draw.X call. I don't think it would be expensive, but I feel like it goes against the idea of immediate mode always drawing when you tell it to draw. As in, it's not like they're TryDraw.X functions, they always draw, it's the, bottom line, final actual real drawing thingy!

Ideally it would auto-detect what context you're in, but, that's not really possible, given that you could be rendering into a render target while, say, Camera.current is also not null. Plus, nullchecking Camera.current would be a big overhead for something like immediate mode that should be as thin of a layer as possible

so uh, I think that's a way too long way of saying that I'm leaning towards no

you can, however, do this in your own code. If you're in, say, OnPostRender, you generally know what camera context it's currently rendering with

int myLayer = LayerMask.NameToLayer( "UI" );

if( ( currentCamera.cullingMask & myLayer ) != 0 ){
    // draw functions here
}

Avatar
Freya Holmér creator
Quote from JohannesMP

Nope, the bitwise shift operators have higher precedence than bitwise logical operators in C# https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/ (and most C-style languages), but it doesn't hurt to be safe!

oh weird, my IDE didn't show the parens I added as unnecessary. maybe it was a coding style preference or something~

Avatar
JohannesMP
Quote from Freya Holmér

might want to add parens around those operations: cullingMask & (1 << gameObject.layer)

(I think this will now execute as (cullingMask & 1) << myLayer)

Nope, the bitwise shift operators have higher precedence than bitwise logical operators in C# https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/ (and most C-style languages), but it doesn't hurt to be safe!

Avatar
Freya Holmér creator
Quote from JohannesMP

Just in case anyone is looking here for reference, since the layer value represents the bit in the mask, for it to work as expected the code is missing a bit shift.

It should be:

int myLayer = LayerMask.NameToLayer( "UI" );
if( ( currentCamera.cullingMask & 1 << myLayer ) != 0 ){
    // draw functions here
}

Or if using the new ImmediateModeShapeDrawer where you can use the layer of your own GameObject, it can be simplified to just adding this to the top of DrawShapes:

if ( (cam.cullingMask & 1 << gameObject.layer) == 0 ) 
    return;

// draw functions here

When using ImmediateModeShapeDrawer to draw stuff relative to the GameObject it is attached to, having it respect the GameObject's layer would make a lot of sense.

might want to add parens around those operations: cullingMask & (1 << gameObject.layer)

(I think this will now execute as (cullingMask & 1) << myLayer)

Avatar
JohannesMP

Just in case anyone is looking here for reference, since the layer value represents the bit in the mask, for it to work as expected the code is missing a bit shift.

It should be:

int myLayer = LayerMask.NameToLayer( "UI" );
if( ( currentCamera.cullingMask & 1 << myLayer ) != 0 ){
    // draw functions here
}

Or if using the new ImmediateModeShapeDrawer where you can use the layer of your own GameObject, it can be simplified to just adding this to the top of DrawShapes:

if ( (cam.cullingMask & 1 << gameObject.layer) == 0 ) 
    return;

// draw functions here

When using ImmediateModeShapeDrawer to draw stuff relative to the GameObject it is attached to, having it respect the GameObject's layer would make a lot of sense.

Avatar
Freya Holmér creator

super, glad it worked out!

Avatar
ryan trawick

That makes sense, your code snippet is elegant and more than sufficient to get the functionality that I want. Thank you.

Avatar
Freya Holmér creator
  • Answer
  • Declined

Render layers are a camera property, and immediate mode isn't tied to cameras or even game objects, so layers don't always exist in these contexts. You can, for instance use immediate mode to draw to render textures, completely cameraless

so, in order for immediate mode to do this, it would have to:
A) know what layer to use for each draw call
B) know what layer mask the current camera is using
C) check layers in every draw call

A would be your proposed Draw.Layer
B is harder. It could be another property like Draw.LayerMaskContext, but it's a little esoteric and would have to be assigned manually

C means there will have to be a mask check in every Draw.X call. I don't think it would be expensive, but I feel like it goes against the idea of immediate mode always drawing when you tell it to draw. As in, it's not like they're TryDraw.X functions, they always draw, it's the, bottom line, final actual real drawing thingy!

Ideally it would auto-detect what context you're in, but, that's not really possible, given that you could be rendering into a render target while, say, Camera.current is also not null. Plus, nullchecking Camera.current would be a big overhead for something like immediate mode that should be as thin of a layer as possible

so uh, I think that's a way too long way of saying that I'm leaning towards no

you can, however, do this in your own code. If you're in, say, OnPostRender, you generally know what camera context it's currently rendering with

int myLayer = LayerMask.NameToLayer( "UI" );

if( ( currentCamera.cullingMask & myLayer ) != 0 ){
    // draw functions here
}