google-ar / sceneform-android-sdk

Sceneform SDK for Android
https://developers.google.com/sceneform/develop/
Apache License 2.0
1.23k stars 604 forks source link

Custom Material that is Transparent yet Occludes rendering behind it #167

Open kunal-wayfair opened 6 years ago

kunal-wayfair commented 6 years ago

Hi,

I was wondering if there is a way to create a custom material that is transparent but at the same time occludes background object rendering.

Trying to mimic the shader provided by tango for Unity. The shader works perfectly with Unity and ARcore, but cannot figure out a way to write a similar material in Sceneform.


Shader "Tango/PointCloud (Occlusion)" {
    SubShader {
        Tags { "Queue" = "Background-1" }
        Pass {
            ColorMask 0

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
               float4 vertex : POSITION;
            };

            struct v2f
            {
               float4 vertex : SV_POSITION;
               float size : PSIZE;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.size = 30;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(1, 1, 1, 1);
            }
            ENDCG
        }
    }
}

Tried a lot of variations but none of them block rendering in transparent mode.

material {
    name : "Custom material",
    shadingModel : lit, // also tried unlit
    blending : transparent,
    transparency : "twoPassesTwoSides",
    depthCulling : false, // tried on / off
    doubleSided : true, // tried on / off
    colorWrite : false // tried on / off
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = vec4(0.0, 0.0, 0.0, 0.0);
    }
}
gstanlo commented 6 years ago

Is this a duplicate of https://github.com/google-ar/sceneform-android-sdk/issues/151 ?

kunal-wayfair commented 6 years ago

@gstanlo, I am not sure both of these have the same issue, I read through #151 / tried the material code there, since I thought I wanted the behavior that was the problem in #151 (not rendering background object)

I am trying to create a occluding shader, that is transparent and yet occludes any other object to be rendered in the background. Trying to achieve something like in the unity example below, where a transparent plane is rendered on the table surface and it occludes rendering the chair (different) object in the background.

chair_sample

romainguy commented 6 years ago

Use the following material. It will make your object only render in the depth buffer, thus occluding other objects in the scene.

Setting the blending mode to "transparent" causes the object to be drawn after all opaque objects, which is why you're not getting the result you want.

material {
    name : "Custom material",
    shadingModel : unlit,
    colorWrite : false,
    depthWrite : true
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = vec4(0.0);
    }
}

Note that this is not needed at all in your example. The table, being opaque, will occlude the chair already. There is no need for an extra invisible occluder.

kunal-wayfair commented 6 years ago

Thanks for the tip @romainguy but the material example you mentioned just gives me a black plane.

Sorry for the confusion. Some more clarity over my question, The table in the image above is not a 3D rendered object but is an actual real world table.

Since the table has a higher plane detected over it, I was trying to occlude / clip the 3D object (chair) with the real world table by laying over a material that is transparent (so that the real world table looks as it is) but occludes the 3D model (chair) rendering in background.

I was trying to replicate the following experience (inspired from Tango/PointCloud (Occlusion) shader): https://forum.unity.com/threads/clipping-objects-behind-a-higher-plane.492147/ ^ the Unity shader works perfectly.

I am in the dilemma that in Sceneform, I cannot set the blending mode to "transparent" because it causes the plane over the table to be drawn after all opaque objects, as well as, I cannot set it to "opaque" as it completely ignores transparency.

romainguy commented 6 years ago

We've had other users implement a similar occlusion system and the only thing needed is to set colorWrite to false in the material (you don't even need the fragment block actually).

What you want is not transparency. Don't try to use the blending mode to achieve this effect. If you look at the Unity shader, you'll see it's not transparent either, it simply disables writes to the color buffer (line ColorMask 0).

Can you confirm you've set colorWrite : false? If you still see black, can you move the camera around to see what's happening? It could just be Sceneform not drawing the camera feed in the right order.

kunal-wayfair commented 6 years ago

@romainguy, thanks for the detailed explanation. Yup, I can confirm I get a black surface when tried with:

material {
    name : "Custom material",
    shadingModel : unlit,
    colorWrite : false,
    depthWrite : true
}

// Tried with / without the fragment block
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = vec4(0.0);
    }
}

^ Gives me a black plane. If I do depthWrite = false (just out of curiosity) I get nothing rendered but no occlusion as well.

I tried moving around the camera and it has no effect on the black surface, may be it is Sceneform not drawing the camera feed in the right order. Is there a way to specify render queue order like in Unity? Tags { "Queue" = "Background-1" }

romainguy commented 6 years ago

You can change the rendering priority of a renderable: https://developers.google.com/ar/reference/java/com/google/ar/sceneform/rendering/Renderable.html#setRenderPriority(int)

Currently the camera feed is rendered last (priority 7). I'll forward this issue to the Sceneform team and see what can be done about it.

kunal-wayfair commented 6 years ago

@romainguy That worked! Setting both the object and the plane with render priority 7 along with colorWrite : false, worked!

Not sure how though?

  1. If all them, the object, the occluding plane and the camera renders at 7, I don't understand how would the colorWrite : false worked. I thought the sequence should have been something like: camera feed < occluding plane < 3d object

  2. Just out of curiosity, is there a way to change the render priority of camera feed to prior than 7? I wasn't able to extract the camera feed as renderable to change its render priority. arFragment.getArSceneView().getScene().getCamera().getRenderable().getRenderPriority() gave me a null renderable.

None the less, Thank you so much for your prompt responses and insights.

romainguy commented 6 years ago

This works because objects are still sorted by distance to the camera even in their priority group, and opaque objects are rendered front to back. The camera feed object is special and happens to be rendered first in this case (it doesn't have a bounding box, so it's treated as being located where the camera is).

Unfortunately there is no way to change the priority of the camera feed currently. I asked the Sceneform team to think about this issue.

kunal-wayfair commented 6 years ago

@romainguy I played around with setting both the object and the plane with render priority 7 along with colorWrite : false

The occluding plane is this way invisible but it does become black in few cases.

Lets say I keep detecting the floor and keep drawing occluding plane over it, If I move in front for a few meters from the app starting point scanning the floor all along and then turn over. While heading back (facing the opposite direction from the direction where I started) the plane renders as black. But then in mid way / or at any point, if I turn to the original direction, the plane renders fine.

There is definitely these few scenarios where in the camera feed is not rendered first. Any suggestions would be appreciated.

Here's a sample: Invisble Occluding plane on the floor: image

Same Occluding plane from other direction: image

romainguy commented 6 years ago

We need to add an API to control this in Sceneform then

JessHolle commented 6 years ago

Shouldn't this be tagged as a feature-request -- or is it not because this gap is seen as a bug?

Berenice2018 commented 6 years ago

@romainguy , I am also very interested in this feature. Are there any plans to add the API so that we can control it in Sceneform?

ThibaudM commented 5 years ago

I am working on Geo AR and occlusions are one of the main features I use for a more immersive experience.

In the beginning, I worked with ArCore/Rajawali and occlusions worked well. I'm currently moving my code from Rajawali to Sceneform and implementing occlusions is the last issue I encounter. I observe the same behavior than @kunal-wayfair, sometimes the occluding plan works, sometimes the occluding plan is black.

Do you have any plans (since July) to allow occlusions in your library?

romainguy commented 5 years ago

@dsternfeld7 @malik-at-work @tpsiaki With the camera feed currently always set to priority 7 occluders can lead to black areas in the final render. For the occlusion case users need a way to render the camera feed first. We could also investigate a solution using the stencil buffer instead (and turn off stencil testing on the camera feed).

bmacinfotech commented 5 years ago

@romainguy That worked! Setting both the object and the plane with render priority 7 along with colorWrite : false, worked!

Not sure how though?

  1. If all them, the object, the occluding plane and the camera renders at 7, I don't understand how would the colorWrite : false worked. I thought the sequence should have been something like: camera feed < occluding plane < 3d object
  2. Just out of curiosity, is there a way to change the render priority of camera feed to prior than 7? I wasn't able to extract the camera feed as renderable to change its render priority. arFragment.getArSceneView().getScene().getCamera().getRenderable().getRenderPriority() gave me a null renderable.

None the less, Thank you so much for your prompt responses and insights.

I tried this to make a transparent cube but still object(wallNode) behind the cube is showing. Below is my code:

MaterialFactory.makeTransparentWithColor(WallActivity.this, new Color(getColor(R.color.transparent)))
                            .thenAccept(new Consumer<Material>() {
                                @Override
                                public void accept(Material material) {
                                    Vector3 size = new Vector3(distanceX, distanceY, 0.01F); 
                                    Vector3 center = new Vector3(centerX, centerY, beginCoordinate.z);

                                    material.setBoolean("colorWrite", false);
                                    rectRenderable = ShapeFactory.makeCube(size, center, material);
                                    rectRenderable.setRenderPriority(7);

                                    if (rectNode == null) {
                                        rectNode = new Node();
                                        rectNode.setRenderable(rectRenderable);
                                        rectNode.setParent(wallNode);
                                    } else {
                                        rectNode.setRenderable(rectRenderable);
                                    }
                                }
                            });

Is there anything wrong in above code?

bobekos commented 4 years ago

@romainguy @tpsiaki

It seems that the occlusion is not working on android 10 devices. Here ist a example from a samsung s7 (android 8) and a google pixel 3a (android 10): Android <10 Android 10

The render priority is in this order camera < normal object < occluder object

yenenahmet commented 4 years ago

I have the same problem. Although I give priority to the transparent cube I created, the cube behind it appears.

how can i make it?

JessHolle commented 4 years ago

This strikes me as a huge remaining issue in SceneForm. Is this a SceneForm gap or a filament one? Having transparent occluding objects seems like a critical basic capability, while SceneForm and filament are providing much more complex (yet seemingly less critical) capabilities.

CasperLindbergAtGithub commented 4 years ago

Any progress made here?

AntonClaesson commented 4 years ago

@romainguy @tpsiaki

It seems that the occlusion is not working on android 10 devices. Here ist a example from a samsung s7 (android 8) and a google pixel 3a (android 10): Android <10 Android 10

The render priority is in this order camera < normal object < occluder object

I found the same as @bobekos for Sceneform 1.15: Works with a Galaxy S9+ with Android 10 Occlusion material renders as black for Mi 9 SE with Android 9.

However I need to use render priority in the following order for it to work otherwise the occluder material is just invisible (the stuff behind is still rendered): camera < occluder object < normal object