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

Setting node opacity #635

Open fzaiatz opened 5 years ago

fzaiatz commented 5 years ago

Are you guys planning to enable setting each node's opacity (as in https://developer.apple.com/documentation/scenekit/scnnode/1408010-opacity for instance)? It would be great to be able to set the node's opacity property at runtime (and also animate it, like localPosition/localRotation).

tpsiaki commented 5 years ago

There are a few catches here that make opacity specifically a somewhat more challenging parameter to set on nodes than other material parameters. Specifically, in order for a mesh to actually be rendered as transparent, it must be rendered with a transparent material model. If you use a custom shader this is simply a shader setting, but if you use an imported asset file, you'll have to make sure that the meshes in your source asset itself are transparent so that the transparent built-in material will be used.

The opacity is a setting on the Renderable (via the Material) that belongs to the Node, so the way to set a node's opacity is to [get its Renderable](https://developers.google.com/ar/reference/java/sceneform/reference/com/google/ar/sceneform/Node#getRenderable()), then use [getMaterial](https://developers.google.com/ar/reference/java/sceneform/reference/com/google/ar/sceneform/rendering/Renderable#getMaterial()) to get the material for this renderable (or use the indexed version of getMaterial to get a material for a particular submesh).

Once you have a Material, you can set material parameters. The available parameters depend on the Filament material that you are using. If you're using one of the built-in-materials from an sfb that you have imported, the material parameters that you set correspond to the parameters in the sfa file. If you are using a custom material the available parameters are whatever parameters you have set up in your custom material.

For built-in materials, the opacity can be changed by setting the "baseColorFactor" parameter, which is a float4 parameter - the w value here corresponds to opacity.

One final note - when you change a material parameter on a renderable, this material may also be used by other renderables that share all the same material parameters. If you wish to only change this one renderable mesh, and not any others that might also use the material, you should [make a copy](https://developers.google.com/ar/reference/java/sceneform/reference/com/google/ar/sceneform/rendering/Material#makeCopy()) of the material, and set this new material copy as the material of the renderable you are working with..

fzaiatz commented 5 years ago

Thanks for that. Based on the docs, API and your previous comment, this is what I assume would be the path to follow:

public void setAlpha(float alpha) {
    if (getRenderable() instanceof ViewRenderable) {
        ((ViewRenderable) getRenderable()).getView().setAlpha(alpha);;
    } else {
        this.callOnHierarchy(node -> {
                Renderable r = node.getRenderable();
                for (int i=0; i<r.getSubmeshCount(); i++) {
                    Material m = r.getMaterial(i).makeCopy();
                    // Set opacity here
                    r.setMaterial(i, m);
                }
        });
    }
}

This method belongs to a class extending Sceneform's Node, where each material's renderable could be a ViewRenderable or a ModelRenderable. For the first case, the alpha will be set directly on the view itself. On the second case, I don't know in advance what kind of model it is (it was built in a some Factory some time ago, and now I'm running the scene with user interaction now trying to modify the alpha prop).

In some cases I could be dealing with a SFB, and in some other cases I could be dealing with a GLTF (built in real time through RenderableSource's builder). Based on what you said previously, I'm not sure how can I identify the type of Material, and which parameter I should set. Also, let say I'm in front of a built-in material, how could I override just the "w" value, leaving the other three with the same value? (Material class only has setters).

cliveleehere commented 5 years ago

+1 for being able to set the alpha on a renderable directly.

I'm trying to place an invisible plane in sceneform, and the work around i've seen are by loading a gltf with a material that is unlit. That seems pretty roundabout and you're loading resources that are not needed.

ivstka95 commented 5 years ago

+1 for being able to set the alpha on a renderable directly.

I'm trying to place an invisible plane in sceneform, and the work around i've seen are by loading a gltf with a material that is unlit. That seems pretty roundabout and you're loading resources that are not needed.

in case you need what i needed, just a transparent plane. you can create it as a Node with ViewRenderable built from a plain view

<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="2000dp"
    android:layout_height="2000dp" />

private CompletableFuture<ViewRenderable> getTransparrentVerticalPlaneCompletableFuture() {
    return ViewRenderable.builder()
            .setView(getContext(), R.layout.ar_transparent_vertical_plane)
            .build()
            .thenApply(disableShadowsFunction());
}

private Function<ViewRenderable, ViewRenderable> disableShadowsFunction() {
    return viewRenderable -> {
        viewRenderable.setShadowCaster(false);
        viewRenderable.setShadowReceiver(false);
        return viewRenderable;
    };
}