Transparent blending with gradient texture leads to color banding (RealityKit 2)

Hello,

I have a cylinder with an Unlit Material and a base color set. Now I want to apply a gradient as the alpha mask so I get this kind of halo GTA-like checkpoint look. The code:

var baseMaterial = UnlitMaterial(color: UIColor.red)
baseMaterial.blending = .transparent(opacity: .init(scale: 100, texture: .init(maskTextureResource))) // maskTextureResource is the gradient mask
baseMaterial.opacityThreshold = 0

This works but unfortunately leads to some ugly visible gradient banding. I've also tried to play with the scale of the blending texture but that did not help.

As an alternative approach I tried to solve this via a custom surface shader. Code below:

[[visible]]

void gradientShader(realitykit::surface_parameters params) {

    auto surface = params.surface();
    float2 uv = params.geometry().uv0();

    float h = 0.5; // adjust position of middleColor
    half startAlpha = 0.001;
    half middleAlpha = 1;
    half endAlpha = 0.001;
    half alpha = mix(mix(startAlpha, middleAlpha, half(uv.y/h)), mix(middleAlpha, endAlpha, half((uv.y - h)/(1.0 - h))), half(step(h, uv.y)));

surface.set_emissive_color(half3(params.material_constants().emissive_color()));

surface.set_base_color(half3(params.material_constants().base_color_tint()));
    surface.set_opacity(alpha);
}

The result looks really nice and smooth but unfortunately this now also culls the inner part of the cylinder. Even on the semitransparent parts. What I want is a having the effect applied on both the outer and inner part of the cylinder being visible. So the transparent part of the outside allows you to seethrough to the inside. I've got this working by using a PhysicallyBasedMaterial instead of an UnlitMaterial (which does not support blending out of the box) but again had to issue with the banding.

On my Custom Material faceCulling is set to .none.

Here is how it currently looks – as you can see in the left one the alpha mask is not smooth and has banding artefacts:

Thank you for any help!

Replies

Hi, can you try removing the baseMaterial.opacityThreshold = 0 line?

Thanks for the tip but unfortunately that does not make any difference. I've already tried a lot of variants. Should I create a sample project? I'm starting to think this issue might be exclusive to CustomMaterial. Based on these docs: https://developer.apple.com/metal/Metal-RealityKit-APIs.pdf […] If there are two entities, one in front of the other, for example, and the closer entity is translucent, both of those entity’s fragments contribute to the final color of that one pixel. I feel like this should work? Otherwise if I stay with the first variant, do you have any idea how I could get rid of the gradient banding?

@arthurfromberlin yes please make a sample project, and either upload it to GitHub or attach it to a Feedback Assistant report (or both).

Alright, I'm an *****. So apparently you need to explicitly set the blending property also on your CustomMaterial to make it work, which kinda makes sense but wasn't clear for me right away.

So for example this:

guard var customMaterial = try? CustomMaterial(…) else { return }
customMaterial.blending = .transparent(opacity: .init(floatLiteral: 1)) // .transparent is important! – floatliteral can be whatever
customMaterial.faceCulling = .none
customMaterial.emissiveColor = .init(color: .red)

works fine with proper blending of faces behind the object. Whereas if you don't specify the blending the default is .opaque and then if you would set the opacity in your fragment shader like so:

surface.set_opacity(0);

the CustomMaterial kinda works similar to an Occlusion Material from my testing and hides everything behind it.

What I haven't found out yet is how I can disabling the color banding when setting a black and white gradient texture as the blending texture. Will investigate and maybe file feedback for that!

Glad you made progress! I think you can also set the sampler you are using to sample the texture. Maybe the default sampler for PhysicallyBasedMaterial is causing the banding?