nartc / angular-three

🧊 THREE.js integration for Angular 🧊
https://angular-three.netlify.app/
MIT License
306 stars 23 forks source link

GL_INVALID_FRAMEBUFFER_OPERATION when trying to add bloom effect #78

Open 0x4a61636f62 opened 2 years ago

0x4a61636f62 commented 2 years ago

Getting hundreds of the following two warnings and a huge performance drop when trying to add bloom effect with ng-effect-composer. The bloom effect never takes effect. I have tried with different objects but got the same warning.

I have a queen component holding a primitive object below which I am trying to display on a landing-page.html.

queen.component.ts

import {
  ChangeDetectionStrategy,
  Component,
  Input,
} from '@angular/core';

import { NgtVector3 } from '@angular-three/core';
import { NgtGLTFLoader } from '@angular-three/soba/loaders'
import { NgtSobaOrbitControls } from '@angular-three/soba/controls';
import { Mesh, MeshStandardMaterial, Object3D, PerspectiveCamera } from 'three';

@Component({
  selector: 'queen',
  templateUrl: './queen.component.html',
  styleUrls: ['./queen.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class QueenComponent {

  @Input() public position?: NgtVector3; // imported from @angular-three/core
  public hover = false;
  public active = false;
  public queen = this._ngtGLTFLoader.load('assets/threed/queen.glb');
  public queenMaterial: MeshStandardMaterial | undefined;
  #color = '#5323ff';

  constructor(private _ngtGLTFLoader: NgtGLTFLoader) { }

  queenLoaded(object: Object3D) {
    this.queenMaterial = <MeshStandardMaterial>(<Mesh>object.getObjectByName('Queen')).material;
    this.applyColorToMaterial(this.#color);
    console.log(object);
  }

  setInitial(controls: NgtSobaOrbitControls) {
    const orbitControl = controls.controls;
    const camera = orbitControl.object as PerspectiveCamera;
    camera.zoom = 2;
    camera.position.setX(0);
    camera.position.setY(50);
    camera.position.setZ(200);
  }

  applyColorToMaterial(color: string) {
    if (this.queenMaterial) {
      this.queenMaterial.color.setHex(parseInt(color.substring(1), 16));
    }
  }

  onAnimate(mesh: THREE.Object3D) {
    mesh.rotation.y = mesh.rotation.x += 0.01;
}
}

queen.component.html

<ngt-canvas>
  <ngt-spot-light [position]="[50, 50, 50]"></ngt-spot-light>
  <ngt-spot-light [position]="[-50, -50, -50]"></ngt-spot-light>

  <ngt-directional-light
      [position]="[0, 1, 2]"
      color="white"
  ></ngt-directional-light>

  <ngt-primitive
  (animateReady)="onAnimate($event.object)"
  (ready)="queenLoaded($event)"
  *ngIf="queen | async as queen"
  [object]="queen.scene">
</ngt-primitive>

<ngt-soba-orbit-controls
#controls="ngtSobaOrbitControls"
(ready)="setInitial(controls)"
[target]="[0, 0, 0]"
>

</ngt-soba-orbit-controls>

<ngt-effect-composer>
    <ngt-bloom></ngt-bloom>
</ngt-effect-composer>

</ngt-canvas>

landing-page.html

<queen></queen>

IRobot1 commented 2 years ago

My understanding is that ngt-primitive is used to wrap an underly THREE object3d.

Have you tried loading into ngt-mesh or ngt-group instead of ngt-primitive?

This Example shows using ngt-mesh

0x4a61636f62 commented 2 years ago

Thank you IRobot1. Do you mean to wrap the primitive inside a ngt-group? If so, that does not work.

IRobot1 commented 2 years ago

No. I was suggesting something like this if queen was a single mesh

<ngt-mesh
  (animateReady)="onAnimate($event.object)"
  (ready)="queenLoaded($event)"
  *ngIf="queen | async as queen"
  [geometry]="queen.geometry"
  [material]="queen.material">
</ngt-mesh>

Can you share 'assets/threed/queen.glb'?

I've confirmed that using the following code in a different scene, does not result in the warnings you reported.

<ngt-effect-composer>
    <ngt-bloom></ngt-bloom>
</ngt-effect-composer>
0x4a61636f62 commented 2 years ago

Hi IRobot, thank you for your help.

I have uploaded queen.glb HERE.

nartc commented 2 years ago

Hi all, I've been pretty busy at work and some personal life stuffs. AngularThree is still being maintained but it's a little slow for now. Thank you.

0x4a61636f62 commented 2 years ago

Hi Nartc, it happens to the best of us. Hope you get time to give NGT some love soon. Again, thanks for this great library.

0x4a61636f62 commented 2 years ago

Hi @IRobot1, @nartc to test out the solution suggested by IRobot1 I need to figure out how to bind the geometry and material properties from the queen object with the ngt-mesh selector as suggested. But the ngt-mesh does not seem to have these properties, i.e., Can't bind to 'geometry' since it isn't a known property of 'ngt-mesh'.

Any help is much appreciated.

Also, wondering if this is the correct way to assign these properties in my queen.component.ts:

  public queen = this._ngtGLTFLoader.load('assets/threed/queen.glb');
  public queenMaterial: MeshPhongMaterial | undefined;
  public queenGeometry: BufferGeometry | undefined;
  #color = '#5323ff';

  constructor(private _ngtGLTFLoader: NgtGLTFLoader) { }

  queenLoaded(object: Object3D) {
    this.queenMaterial = <MeshPhongMaterial>(<Mesh>object.getObjectByName('Queen')).material;
    this.queenGeometry = <BufferGeometry>(<Mesh>object.getObjectByName('Queen')).geometry;
    console.log('Geometry: ', this.queenGeometry)
    console.log('Material: ', this.queenMaterial)
    console.log(object);
  }
nartc commented 2 years ago

@99JS if you try to console log the data of queen, you'll see that it has materials and geometries on it already

public queen = this._ngtGLTFLoader.load('assets/threed/queen.glb).pipe(
   tap(data => {
      console.log(data); // <-- check here
   })
)
0x4a61636f62 commented 2 years ago

@nartc Thank you. After a few tries, I was able to create the same example using NGT group and mesh. I created a new MeshStandardMaterial and used the geometry from my Blender object. Everything works fine, except the bloom effect is still throwing the same warnings and is not working from a performance perspective.

Would it be worth trying to create the bloom effect in Blender and try to include it as a new mesh in the NGT group somehow? I started learning everything about 3D just the other day and have never worked on this before so please excuse my ignorance.

IRobot1 commented 2 years ago

You were pretty close. The following code runs without the warnings and animated the model

image

<div style="height:100vh">
  <ngt-canvas>
    <ngt-spot-light [position]="[50, 50, 50]"></ngt-spot-light>
    <ngt-spot-light [position]="[-50, -50, -50]"></ngt-spot-light>

    <ngt-directional-light [position]="[0, 1, 2]"
                           color="white"></ngt-directional-light>

    <ngt-mesh *ngIf="mesh" (animateReady)="onAnimate($event.object)"
              [material]="mesh.material"
              [geometry]="mesh.geometry">
    </ngt-mesh>

    <ngt-soba-orbit-controls #controls="ngtSobaOrbitControls"
                             (ready)="setInitial(controls)"
                             [target]="[0, 0, 0]">

    </ngt-soba-orbit-controls>

    <ngt-effect-composer>
      <ngt-bloom></ngt-bloom>
    </ngt-effect-composer>

  </ngt-canvas>
</div>
import {
  Component,
  Input,
} from '@angular/core';

import { NgtVector3 } from '@angular-three/core';
import { NgtGLTFLoader } from '@angular-three/soba/loaders'
import { NgtSobaOrbitControls } from '@angular-three/soba/controls';
import { Mesh, MeshStandardMaterial, PerspectiveCamera } from 'three';

@Component({
  selector: 'queen',
  templateUrl: './queen.component.html',
})
export class QueenComponent {
  @Input() public position?: NgtVector3; // imported from @angular-three/core

  public hover = false;
  public active = false;
  public queenMaterial: MeshStandardMaterial | undefined;
  #color = '#5323ff';
  public mesh!: Mesh;

  constructor(private _ngtGLTFLoader: NgtGLTFLoader) {

    const s = this._ngtGLTFLoader.load('assets/queen.glb').subscribe(next => {
      const mesh = <Mesh>next.scene.children[0];
      this.mesh = mesh;
      (<MeshStandardMaterial>this.mesh.material).color.setHex(parseInt(this.#color.substring(1), 16))
    },
      () => { },
      () => { s.unsubscribe(); }
    );

  }

  setInitial(controls: NgtSobaOrbitControls) {
    const orbitControl = controls.controls;
    const camera = orbitControl.object as PerspectiveCamera;
    camera.zoom = 2;
    camera.position.setX(0);
    camera.position.setY(50);
    camera.position.setZ(200);
  }

  onAnimate(mesh: THREE.Object3D) {
    mesh.rotation.y = mesh.rotation.x += 0.01;
  }
}
IRobot1 commented 2 years ago

If I remove ngt-bloom it looks like this image

If I remove the ngt-effect-composer, it looks like this image

0x4a61636f62 commented 2 years ago

Thanks a lot, @IRobot1It looks somewhat similar to what I did but clearly I'm still missing something. I'll go over your code to see what the issue might be.

This is what I did (queen has been replaced by knight and don't mind the terrible code formatting):


import { CommonModule } from '@angular/common';
import {
    NgtGLTFLoader,
} from '@angular-three/soba/loaders';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import * as THREE from 'three';
import { tap } from 'rxjs';
import { BufferGeometry, Mesh, MeshBasicMaterial, MeshStandardMaterial, PerspectiveCamera } from 'three';
import { NgtSobaOrbitControls } from '@angular-three/soba/controls';

@Component({
    selector: 'knight',
    templateUrl: './knight.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
})

export class KnightComponent {
  public knightMaterial = new THREE.MeshPhongMaterial();
  public knightGeometry: BufferGeometry | undefined;
  #color = '#5323ff';

  constructor(private gltfLoader: NgtGLTFLoader) {}

    knight$ = this.gltfLoader.load('assets/threed/knight.glb').pipe(
      tap(data => {
        // console.log(data.scene);
        const mesh = <Mesh>data.scene.children[0];
        // this.knightMaterial = mesh.material;
        console.log(this.knightMaterial);
        this.knightGeometry = mesh.geometry;
     })
    );

    onAnimate(object: THREE.Object3D) {
      object.rotation.z += 0.01;
    }

    applyColorToMaterial(color: string) {
      if (this.knightMaterial) {
        this.knightMaterial.color.setHex(parseInt(color.substring(1), 16));
      }
    }

    setInitial(controls: NgtSobaOrbitControls) {
      const orbitControl = controls.controls;
      const camera = orbitControl.object as PerspectiveCamera;
      // camera.zoom = 4;
      camera.position.setX(0);
      camera.position.setY(10);
      camera.position.setZ(0);
      camera.rotateZ(0);
    }

    onReady(group: THREE.Group) {
        this.applyColorToMaterial(this.#color);
        // group.rotation.y = 0;
        // group.rotation.x = Math.PI / 2;
    }
}
<ngt-canvas
>
<ngt-spot-light [position]="[10, 10, 10]"></ngt-spot-light>
<ngt-spot-light [position]="[-10, -10, -10]"></ngt-spot-light>
<ngt-ambient-light></ngt-ambient-light>
<ngt-stats></ngt-stats>
<ng-container *ngIf="knight$ | async as knight">
  <!-- <ngt-soba-center [position]="[5, 5, 10]"> -->
  <ngt-group
      (ready)="onReady($event)"
      [dispose]="null"
      (animateReady)="onAnimate($event.object)"
  >
  <ngt-mesh
    [castShadow]="true"
    [receiveShadow]="true"
    [material]="knightMaterial"
    [geometry]="knightGeometry"
    >
  </ngt-mesh>
  </ngt-group>
<!-- </ngt-soba-center> -->
</ng-container>
<ngt-soba-orbit-controls
#controls="ngtSobaOrbitControls"
(ready)="setInitial(controls)"
[target]="[0, 0, 0]"
>
</ngt-soba-orbit-controls>
<!-- <ngt-effect-composer>
    <ngt-bloom></ngt-bloom>
    <ngt-noise [options]="{ premultiply: true }"></ngt-noise>
</ngt-effect-composer> -->
</ngt-canvas>
<ngt-soba-loader></ngt-soba-loader>
0x4a61636f62 commented 2 years ago

@IRobot1 I ran your code, but I still get the same warnings. Being new to Angular I do not know all the features and configurations, but I wonder if there might be some configuration issues on my local.

IRobot1 commented 2 years ago

Could be. Can you share your github repo? Perhaps you can give me temporary access.

I occassionally forget to include a module in the app that results in warnings or failures only at runtime. I would start by checking that. ng3 is very modular and requires a lot of modules to be imported so its easy to miss one. Most are caught at compile time, but some fail at runtime with not very obvious errors.

0x4a61636f62 commented 2 years ago

Thanks a lot for your help @IRobot1 !

I have created a private repository and made you a collaborator. The project includes all the packages from the original project. Let me know if you are facing problems accessing the repository.

image

IRobot1 commented 2 years ago

Thanks. I have access and can repeat your warnings. I'll start investigation.

IRobot1 commented 2 years ago

It has something to do with putting ng3 in your website module. When I remove the website module and route and reference the queen directly from app.module, it works without warnings.

IRobot1 commented 2 years ago

If I replace lazy loading, export the landing page and reference the landing page directly, the warnings come back and nothing is displayed.

const routes: Routes = [
  {
    path: '', component: LandingPageComponent,
  //  loadChildren: () =>
  //    import('./modules/website/website.module').then((m) => m.WebsiteModule),
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

At the moment, my recommendation is to avoid using modules and put all your ng3 components in the main app module.

That's a pain, but seems to be the only solution at the moment.

IRobot1 commented 2 years ago

I'll publish a repeat case so that @nartc can investigate.

Without this getting fixed, it won't be possible for anyone to publish reusable libraries that includes ng3.

IRobot1 commented 2 years ago

A simpler repeat case is not showing the same problem. Now, I'm stumped why its failing in your project. I'll keep looking.

IRobot1 commented 2 years ago

@nartc I have a repeat case now on 2 different branches of a simple template project. Clone to review

postprocessing branch is just an app. When ng3/postprocessing is added, it works as expected.

route+module+postprocessing branch demonstrates the warnings as originally reported by @99JS

IRobot1 commented 2 years ago

I have another branch where lazy loading is removed. The test component is referenced directly from the main app route.

The warnings still appear, however, there are fewer of them. This makes be think its related to loading modules from a route.

So the underly problem appears to be when using routes+modules.

I confirmed using post processing from a module directly has no warnings

0x4a61636f62 commented 2 years ago

Thank you very much @IRobot1. I'll be honest and say that I am new to programming and learnt the basics of Github just the other day.

What you did with creating a repository and different branches for different cases is something I can do to contribute to the Ng3 in the future. However, at the moment, as I am learning both Javascript, Angular and Ng3 at the same time, trying to fix these issues on my own is a bit over my head I'm afraid.

Maybe in the coming months or so I will have gotten enough understanding to do it myself and contribute to the repository in a more tangible way. In the meantime, I am very thankful for yours and @nartc help.

As you say, being able to create reusable Ng3 components is a must feature as having everything in the App root is untenable.

IRobot1 commented 2 years ago

@99JS That's a steep learning curve. That's how I felt 5 years ago when I started Angular development. There's honestly a lot I still don't know. I still struggle to decipher the code @nartc is writing.

I have a branch showing that having re-usable components in a module does work, just not when accessed from a route.

0x4a61636f62 commented 2 years ago

@IRobot1 I started my journey with Javascript and Angular in January this year indeed it has been a really steep learning curve. But I am happy that I am starting to get the basics down but still have a mountain to climb.

nartc commented 2 years ago

I've seen this as well but not sure how to fix it. If I put a long enough delay (500ms) on the EffectComposer's render, then the error's gone. So there must be some racing condition somewhere but I can't tell what it is because I don't know what generates the INVALID warnings :(