mob-sakai / SoftMaskForUGUI

Enhance Unity UI (uGUI) with advanced soft-masking features to create more visually appealing effects!
https://github.com/mob-sakai/SoftMaskForUGUI
MIT License
1.91k stars 257 forks source link

[v1] Screen Space - Camera, URP, Stacked overlay camera. #127

Closed inertiave closed 15 hours ago

inertiave commented 2 years ago

I think when the canvas is the screen space camera mode and the camera is stacked overlay camera on URP, it cannot draw a maskable image properly. Just masking the shape in runtime and draw well in scene view.

image

Is there anything I missed to set up before? the canvas camera for ui is urp overlay camera and added properly to stack camera list. My URP is 12.0.0.

Thanks.

Metroslim commented 2 years ago

@inertiave & @mob-sakai I've faced the exact same problem in URP while the Camera that display the SoftMask/maskable was in overlay mode. I've managed to saw the mask but it wasn't weel placed and was verticaly fliped.

I guess it's a king of Pipeline/Shader way of stacking the Camera but I didn't managed to customize the shader to make it right.

An easy way to test this bug is to start from the Samples and add a MainCamera as the Base then switch the exisiting Camera to Overlay and Stack it.

MaxMoussalli commented 1 year ago

@inertiave & @Metroslim & @mob-sakai Same issue here. Has anyone found a way to solve this problem?

image

What I have noticed is that the mask is aligned badly on the Y axis, on the center of the screen no problem and the more the UI is far from center the more the mask decals.

MaxMoussalli commented 1 year ago

After some investigation, I found that forcing SOFTMASK_EDITOR in SoftMask.cginc solve the problem for canvas with screen space mode (but cause the problem for overlay mode instead). Sadly it's working only in editor and not in build.

If this can help to find a proper way to fix it...

#ifndef UI_SOFTMASK_INCLUDED
#define UI_SOFTMASK_INCLUDED

sampler2D _SoftMaskTex;
float _Stencil;
float4x4 _GameVP;
float4x4 _GameTVP;
half4 _MaskInteraction;

float CustomStep(float a, float x)
{
    return x >= a;
}

fixed Approximately(float4x4 a, float4x4 b)
{
    float4x4 d = a - b;
    d = float4x4(
            abs(d[0]),
            abs(d[1]),
            abs(d[2]),
            abs(d[3])
        );

    return step(
        max(d._m00,max(d._m01,max(d._m02,max(d._m03,
        max(d._m10,max(d._m11,max(d._m12,max(d._m13,
        max(d._m20,max(d._m21,max(d._m22,max(d._m23,
        max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
        0.5);
}

//#if SOFTMASK_EDITOR
float SoftMaskInternal(float4 clipPos, float4 wpos)
//#else
//float SoftMaskInternal(float4 clipPos)
//#endif
{
    half2 view = clipPos.xy/_ScreenParams.xy;
    //#if SOFTMASK_EDITOR
        fixed isSceneView = 1 - Approximately(UNITY_MATRIX_VP, _GameVP);
        float4 cpos = mul(_GameTVP, mul(UNITY_MATRIX_M, wpos));
        view = lerp(view, cpos.xy / cpos.w * 0.5 + 0.5, isSceneView);
        #if UNITY_UV_STARTS_AT_TOP
            view.y = lerp(view.y, 1 - view.y, CustomStep(0, _ProjectionParams.x));
        #endif
    //#elif UNITY_UV_STARTS_AT_TOP
    #if UNITY_UV_STARTS_AT_TOP
        view.y = lerp(view.y, 1 - view.y, CustomStep(0, _ProjectionParams.x));
    #endif

    fixed4 mask = tex2D(_SoftMaskTex, view);
    half4 alpha = saturate(lerp(fixed4(1, 1, 1, 1), lerp(mask, 1 - mask, _MaskInteraction - 1), _MaskInteraction));
    //#if SOFTMASK_EDITOR
    alpha *= CustomStep(0, view.x) * CustomStep(view.x, 1) * CustomStep(0, view.y) * CustomStep(view.y, 1);
    //#endif

    return alpha.x * alpha.y * alpha.z * alpha.w;
}

//#if SOFTMASK_EDITOR
    #define SOFTMASK_EDITOR_ONLY(x) x
    #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos, worldPosition)
//#else
//  #define SOFTMASK_EDITOR_ONLY(x)
//  #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos)
//#endif

#endif // UI_SOFTMASK_INCLUDED
MaxMoussalli commented 1 year ago

So now I fixed it to works in build too but I still having the issues to not working anymore with canvas overlay mode.

#ifndef UI_SOFTMASK_INCLUDED
#define UI_SOFTMASK_INCLUDED

sampler2D _SoftMaskTex;
float _Stencil;
float4x4 _GameVP;
float4x4 _GameTVP;
half4 _MaskInteraction;

float CustomStep(float a, float x)
{
    return x >= a;
}

fixed Approximately(float4x4 a, float4x4 b)
{
    float4x4 d = a - b;
    d = float4x4(
            abs(d[0]),
            abs(d[1]),
            abs(d[2]),
            abs(d[3])
        );

    return step(
        max(d._m00,max(d._m01,max(d._m02,max(d._m03,
        max(d._m10,max(d._m11,max(d._m12,max(d._m13,
        max(d._m20,max(d._m21,max(d._m22,max(d._m23,
        max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
        0.5);
}

#if SOFTMASK_EDITOR
float SoftMaskInternal(float4 clipPos, float4 wpos)
#else
float SoftMaskInternal(float4 clipPos)
#endif
{
    half2 view = clipPos.xy/_ScreenParams.xy;
    #if SOFTMASK_EDITOR
        fixed isSceneView = 1 - Approximately(UNITY_MATRIX_VP, _GameVP);
        float4 cpos = mul(_GameTVP, mul(UNITY_MATRIX_M, wpos));
        view = lerp(view, cpos.xy / cpos.w * 0.5 + 0.5, isSceneView);
    /*#if UNITY_UV_STARTS_AT_TOP
            view.y = lerp(view.y, 1 - view.y, CustomStep(0, _ProjectionParams.x));
        #endif
    #elif UNITY_UV_STARTS_AT_TOP
        view.y = lerp(view.y, 1 - view.y, CustomStep(0, _ProjectionParams.x));*/
    #endif

    fixed4 mask = tex2D(_SoftMaskTex, view);
    half4 alpha = saturate(lerp(fixed4(1, 1, 1, 1), lerp(mask, 1 - mask, _MaskInteraction - 1), _MaskInteraction));
    #if SOFTMASK_EDITOR
    alpha *= CustomStep(0, view.x) * CustomStep(view.x, 1) * CustomStep(0, view.y) * CustomStep(view.y, 1);
    #endif

    return alpha.x * alpha.y * alpha.z * alpha.w;
}

#if SOFTMASK_EDITOR
    #define SOFTMASK_EDITOR_ONLY(x) x
    #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos, worldPosition)
#else
    #define SOFTMASK_EDITOR_ONLY(x)
    #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos)
#endif

#endif // UI_SOFTMASK_INCLUDED

So my conclusion is that UNITY_UV_STARTS_AT_TOP is wrong with camera stack. If anyone have an idea on how to fix that, then it should work in all cases.

mob-sakai commented 1 year ago

Thank you for your reporting! Could you please attach a minimal project (included Assets, Packages and ProjectSettings directories) that reproduces the issue?

repos

Rekun commented 1 year ago

I am having the same problem with stack camera, when rotating a mask it doesn't work properly, also, i realized that activating post-process in the stacked camera somehow fixes the issue, but it's not a desired solution. I attach a project and a video

Unity Version 2021.3.0f1

TestSoftMask.zip

https://user-images.githubusercontent.com/36939401/189869238-0b0ae50b-8911-4604-9f52-c2f9079be2a1.mp4

jackson1991123 commented 1 year ago

ifndef UI_SOFTMASK_INCLUDED

define UI_SOFTMASK_INCLUDED

sampler2D _SoftMaskTex; float _Stencil; float4x4 _GameVP; float4x4 _GameTVP; half4 _MaskInteraction; half4 _MainTex_TexelSize; //this one is filled by unity

float CustomStep(float a, float x) { return x >= a; }

fixed Approximately(float4x4 a, float4x4 b) { float4x4 d = a - b; d = float4x4( abs(d[0]), abs(d[1]), abs(d[2]), abs(d[3]) );

return step(
    max(d._m00,max(d._m01,max(d._m02,max(d._m03,
    max(d._m10,max(d._m11,max(d._m12,max(d._m13,
    max(d._m20,max(d._m21,max(d._m22,max(d._m23,
    max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
    0.5);

}

if SOFTMASK_EDITOR

float SoftMaskInternal(float4 clipPos, float4 wpos)

else

float SoftMaskInternal(float4 clipPos)

endif

{ half2 view = clipPos.xy/_ScreenParams.xy;

if SOFTMASK_EDITOR

    fixed isSceneView = 1 - Approximately(UNITY_MATRIX_VP, _GameVP);
    float4 cpos = mul(_GameTVP, mul(UNITY_MATRIX_M, wpos));
    view = lerp(view, cpos.xy / cpos.w * 0.5 + 0.5, isSceneView);
    #if UNITY_UV_STARTS_AT_TOP
        view.y = lerp(view.y, 1 - view.y, CustomStep( _MainTex_TexelSize.y,0));
    #endif
#elif UNITY_UV_STARTS_AT_TOP ///aim to check DX on device
    ///CustomStep(_MainTex_TexelSize.y, 0) aim to check if unity has converted the agly uv :< now the uv is view
    view.y = lerp(view.y, 1 - view.y, CustomStep(_MainTex_TexelSize.y, 0));
#endif

fixed4 mask = tex2D(_SoftMaskTex, view);
half4 alpha = saturate(lerp(fixed4(1, 1, 1, 1), lerp(mask, 1 - mask, _MaskInteraction - 1), _MaskInteraction));
#if SOFTMASK_EDITOR
alpha *= CustomStep(0, view.x) * CustomStep(view.x, 1) * CustomStep(0, view.y) * CustomStep(view.y, 1);
#endif

return alpha.x * alpha.y * alpha.z * alpha.w;

}

if SOFTMASK_EDITOR

#define SOFTMASK_EDITOR_ONLY(x) x
#define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos, worldPosition)

else

#define SOFTMASK_EDITOR_ONLY(x)
#define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos)

endif

endif // UI_SOFTMASK_INCLUDED

///this may work, SoftMask.cginc ///可以试试这个

MaxMoussalli commented 1 year ago

ifndef UI_SOFTMASK_INCLUDED #define UI_SOFTMASK_INCLUDED

sampler2D _SoftMaskTex; float _Stencil; float4x4 _GameVP; float4x4 _GameTVP; half4 _MaskInteraction; half4 _MainTex_TexelSize; //this one is filled by unity

float CustomStep(float a, float x) { return x >= a; }

fixed Approximately(float4x4 a, float4x4 b) { float4x4 d = a - b; d = float4x4( abs(d[0]), abs(d[1]), abs(d[2]), abs(d[3]) );

return step(
  max(d._m00,max(d._m01,max(d._m02,max(d._m03,
  max(d._m10,max(d._m11,max(d._m12,max(d._m13,
  max(d._m20,max(d._m21,max(d._m22,max(d._m23,
  max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
  0.5);

}

if SOFTMASK_EDITOR float SoftMaskInternal(float4 clipPos, float4 wpos) #else float SoftMaskInternal(float4 clipPos) #endif { half2 view = clipPos.xy/_ScreenParams.xy; #if SOFTMASK_EDITOR fixed isSceneView = 1 - Approximately(UNITY_MATRIX_VP, _GameVP); float4 cpos = mul(_GameTVP, mul(UNITY_MATRIX_M, wpos)); view = lerp(view, cpos.xy / cpos.w * 0.5 + 0.5, isSceneView); #if UNITY_UV_STARTS_AT_TOP view.y = lerp(view.y, 1 - view.y, CustomStep( _MainTex_TexelSize.y,0)); #endif #elif UNITY_UV_STARTS_AT_TOP ///aim to check DX on device ///CustomStep(_MainTex_TexelSize.y, 0) aim to check if unity has converted the agly uv :< now the uv is view view.y = lerp(view.y, 1 - view.y, CustomStep(_MainTex_TexelSize.y, 0)); #endif

fixed4 mask = tex2D(_SoftMaskTex, view);
half4 alpha = saturate(lerp(fixed4(1, 1, 1, 1), lerp(mask, 1 - mask, _MaskInteraction - 1), _MaskInteraction));
#if SOFTMASK_EDITOR
alpha *= CustomStep(0, view.x) * CustomStep(view.x, 1) * CustomStep(0, view.y) * CustomStep(view.y, 1);
#endif

return alpha.x * alpha.y * alpha.z * alpha.w;

}

if SOFTMASK_EDITOR #define SOFTMASK_EDITOR_ONLY(x) x #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos, worldPosition) #else #define SOFTMASK_EDITOR_ONLY(x) #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos) #endif

endif // UI_SOFTMASK_INCLUDED

///this may work, SoftMask.cginc ///可以试试这个

This still not fix for the overlay mode in canvas

Rekun commented 1 year ago

ifndef UI_SOFTMASK_INCLUDED #define UI_SOFTMASK_INCLUDED

sampler2D _SoftMaskTex; float _Stencil; float4x4 _GameVP; float4x4 _GameTVP; half4 _MaskInteraction; half4 _MainTex_TexelSize; //this one is filled by unity

float CustomStep(float a, float x) { return x >= a; }

fixed Approximately(float4x4 a, float4x4 b) { float4x4 d = a - b; d = float4x4( abs(d[0]), abs(d[1]), abs(d[2]), abs(d[3]) );

return step(
  max(d._m00,max(d._m01,max(d._m02,max(d._m03,
  max(d._m10,max(d._m11,max(d._m12,max(d._m13,
  max(d._m20,max(d._m21,max(d._m22,max(d._m23,
  max(d._m30,max(d._m31,max(d._m32,d._m33))))))))))))))),
  0.5);

}

if SOFTMASK_EDITOR float SoftMaskInternal(float4 clipPos, float4 wpos) #else float SoftMaskInternal(float4 clipPos) #endif { half2 view = clipPos.xy/_ScreenParams.xy; #if SOFTMASK_EDITOR fixed isSceneView = 1 - Approximately(UNITY_MATRIX_VP, _GameVP); float4 cpos = mul(_GameTVP, mul(UNITY_MATRIX_M, wpos)); view = lerp(view, cpos.xy / cpos.w * 0.5 + 0.5, isSceneView); #if UNITY_UV_STARTS_AT_TOP view.y = lerp(view.y, 1 - view.y, CustomStep( _MainTex_TexelSize.y,0)); #endif #elif UNITY_UV_STARTS_AT_TOP ///aim to check DX on device ///CustomStep(_MainTex_TexelSize.y, 0) aim to check if unity has converted the agly uv :< now the uv is view view.y = lerp(view.y, 1 - view.y, CustomStep(_MainTex_TexelSize.y, 0)); #endif

fixed4 mask = tex2D(_SoftMaskTex, view);
half4 alpha = saturate(lerp(fixed4(1, 1, 1, 1), lerp(mask, 1 - mask, _MaskInteraction - 1), _MaskInteraction));
#if SOFTMASK_EDITOR
alpha *= CustomStep(0, view.x) * CustomStep(view.x, 1) * CustomStep(0, view.y) * CustomStep(view.y, 1);
#endif

return alpha.x * alpha.y * alpha.z * alpha.w;

}

if SOFTMASK_EDITOR #define SOFTMASK_EDITOR_ONLY(x) x #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos, worldPosition) #else #define SOFTMASK_EDITOR_ONLY(x) #define SoftMask(clipPos, worldPosition) SoftMaskInternal(clipPos) #endif

endif // UI_SOFTMASK_INCLUDED

///this may work, SoftMask.cginc ///可以试试这个

this has worked with the bug i had

MaxMoussalli commented 1 year ago

yes this is working for camera space but not for overlay anymore

Gitburner commented 1 year ago

Hi. I had the same problem with a overlay camera stack. I have a "Main Camera" (Base, Perspective) and a "UI Camera" (Overlay, Orthographic). It seems to be a scale problem. My fix was to change the Projection>Size of the "UI Camera" to 1. You can also change the Quality>Render Scale to 1 in the Render Pipeline Asset, but it looks bad.^^

Jens-roesing commented 1 year ago

Hi. I had the same problem with a overlay camera stack. I have a "Main Camera" (Base, Perspective) and a "UI Camera" (Overlay, Orthographic). It seems to be a scale problem. My fix was to change the Projection>Size of the "UI Camera" to 1. You can also change the Quality>Render Scale to 1 in the Render Pipeline Asset, but it looks bad.^^

I have similar setup like Gitburner, but my error was that i hadn't enabled "post processing" in my UICamera grafik

mob-sakai commented 15 hours ago

Please try v2: https://github.com/mob-sakai/SoftMaskForUGUI/releases/tag/v2.0.0