Skip to content

Add second instance of Renderer to manage transparent background.#949

Closed
nicolas-daures wants to merge 12 commits intogoogle:masterfrom
nicolas-daures:master
Closed

Add second instance of Renderer to manage transparent background.#949
nicolas-daures wants to merge 12 commits intogoogle:masterfrom
nicolas-daures:master

Conversation

@nicolas-daures
Copy link
Contributor

Reference Issue

Fixes #874

Management of the "transparent" value for the background-color property.

To allow the property value to change (transparent -> opaque, opaque -> transparent), we create two instances of the Renderer class. The first to manage a background color, the second to manage transparency.

@cdata
Copy link
Contributor

cdata commented Jan 9, 2020

Thanks for the change suggestion @nicolas-daures !

Would it be possible to structure this so that the transparent renderer is created on-demand? This way folks only pay a cost in the case that they actually want to use transparency.

Also for the sake of cross-referencing things, we have had issues with transparent rendering in the past (see #381 ).

Copy link
Contributor

@elalish elalish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an example of using transparency, perhaps here?

const widthDPR = width * dpr;
const heightDPR = height * dpr;
if (this.isTransparent) {
context.clearRect(0, 0, widthDPR, heightDPR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary to clear both the context and three's renderer color?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't clear the context, the transparent pixels of the image to draw in the canvas will not overwrite the pixels of the previous image.

If I don't clear the color buffer of the three renderer, the transparent pixels of the new rendering will not overwrite those of the previous rendering.

rendering_without_clear

@nicolas-daures
Copy link
Contributor Author

@cdata The renderer is now created on-demand.

About transparency issues, I have the same rendering as on this comment: #381 (comment)

@elalish I added an example in customize-ui with a linear gradient background to see the transparency.

Copy link
Contributor

@elalish elalish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing the rendering issue to my attention. I don't see a reason to hold this back due to a Three.js bug, especially since it works for most cases. I want to test this a bit more before merging, but I really like it! Thanks for taking the initiative!


static get singleton(): Renderer {
let singleton = this[$singleton];
if (!singleton) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: in this project's style we prefer to say explicitly x == null rather than !x. Also, is the temporary variable necessary here? Same comment for the changes below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the code to adapt to the project's style. I deleted the temporary variable. It just used to avoid the cast of Renderer.

</div>
<example-snippet stamp-to="demo-container-2" highlight-as="html">
<template>
<model-viewer ar camera-controls ios-src="assets/Astronaut.usdz" src="assets/Astronaut.glb" background-color="transparent" alt="A 3D model of an astronaut">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Everything inside the <template> should be indented starting over from the left. If you look at this page in your browser you'll see why. Also, can we add an image or some text or something behind this so that it's clear the transparency is working? These dev pages serve as manual regression tests for this project.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I just saw the linear gradient; the trouble is that it's not inside the <template>, so it's not a visible part of the demo code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the example to integrate the gradient and a text in the template. Thanks for all the feedback.

if (this[$singleton] != null) {
(this[$singleton] as Renderer).dispose();
}
this[$singleton] = new Renderer({debug: isDebugMode()});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be created only if the previous value was defined (aka inside the if statement)? Same for the singleton with transparency below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good remark, thank you.

@cdata
Copy link
Contributor

cdata commented Jan 16, 2020

@nicolas-daures thanks so much for your diligent work on this change. I want to give you a friendly heads up that we are trying to start the year off on the right foot by landing a major source restructuring change (see #962 ). Your changes are still good, but I will likely ask you to reconcile before this lands and everything will have been moved two directories deep.

Anyway, I am happy to help you reconcile the changes when that time comes. Just wanted to let you know what is going on.

@elalish
Copy link
Contributor

elalish commented Jan 16, 2020

@nicolas-daures This seems to work, except when switching between the renderers. You can repro by scrolling up and down on the customize-ui page. It seems to switch back and forth between rendering transparently and non each time it comes into view. Can you repro?

@nicolas-daures
Copy link
Contributor Author

@cdata I merged master in my pull request to get the packages and lerna. It works. I would update the pull request with your changes regularly.

@elalish I can reproduce the bug and I fixed it. Thanks for the feedback.

@elalish
Copy link
Contributor

elalish commented Jan 17, 2020

@nicolas-daures Thanks! That big change @cdata was talking about just landed, so once you merge or rebase I'll take another look. Note the build instructions have changed too, so check out the updated READMEs.

…-viewer

# Conflicts:
#	packages/modelviewer.dev/examples/customizing-ui.html
@nicolas-daures
Copy link
Contributor Author

@elalish It's merged.

Copy link
Contributor

@cdata cdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping this in sync with master, I know there has been a lot of change happening on the side 🍻

Ideally we would also have at least one test that confirms that setting backgroundColor to "transparent" causes the canvas to render with alpha transparency.


// Update background color before to get texture utils because renderer singleton depends on background transparency
if (skyboxImage == null)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format needs to be run on this change

if (backgroundColor === 'transparent') {
if (this[$scene].background != null) {
this.disconnectedCallback();
delete this[$scene].background;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't significantly different from assigning null, prefer to assign null instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, that's fixed.

{
if (backgroundColor === 'transparent') {
if (this[$scene].background != null) {
this.disconnectedCallback();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These (connectedCallback/disconnectedCallback) are custom element lifecycle methods, and are meant to be invoked by the browser based on the semantics defined in the custom element spec. If we are calling them manually, then it's a sign that we may need to factor out part of their implementation to discrete methods.

Why are we invoking these methods manually?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These callbacks are called to unregister the scene from the renderer and to unsubscribe from the contextlost event. I change the code to call only what is necessary.


static get singleton() {
return this[$singleton];
static get singleton(): Renderer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to call this a singleton now that we are creating up to two of them. Maybe we should just call them opaqueRenderer and transparentRenderer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, that's renamed.

}
else {
const mustUpdateRenderer = this[$scene].background == null;
mustUpdateRenderer && this.disconnectedCallback();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these lifecycle methods are being invoked in order to get the renderer to update, then we could use this[$needsRender]() to provoke a re-render and skip calling the lifecycle methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The objective is not really to update the rendering but to unsubscribe listeners and unregister the scene from the old renderer before switching to the new renderer. So the accessor $renderer must return the old renderer to unsubscribe the listener, then the same accessor must return the new renderer to subscribe the listener. This is why there is the test to null on the background performed in the accessor.


get[$renderer]() {
return Renderer.singleton;
return this[$scene].background ? Renderer.singleton : Renderer.singletonWithTransparency;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we should only use the transparent renderer if the background color is, in fact, "transparent". One way to accomplish this would be to specialize the [$renderer] accessor for the EnvironmentMixin implementation, so that it overrides the default ("always use opaque renderer") with a version that returns a renderer based on the value of the backgroundColor property.

@koch-crsd
Copy link

hi guys, great work! :)

I have a small question: I've noticed that if the background-color attribute is set as "transparent" it works. But if for example additionally also the shadow-intensity attribute is set, then the transparency isn't applied and it gets the default white background.?

it would be really awesome if the transparency would still work even when having the shadow-intensity attribute set...

… Assign null to background instead of using delete.
@elalish elalish mentioned this pull request Jan 29, 2020
@elalish
Copy link
Contributor

elalish commented Jan 31, 2020

@nicolas-daures @valdrinkoshi @koch-crsd I have an alternative PR that I'd love feedback on: #975. It draws heavily from this one, but also simplifies our API.

@elalish
Copy link
Contributor

elalish commented Feb 3, 2020

Closing in favor of #975. Thank you @nicolas-daures for pushing us on this and contributing to the landed change!

@elalish elalish closed this Feb 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make it possible to configure the background to be transparent

5 participants