Knightro63 / three_js

A dart conversion of three.js.
MIT License
19 stars 13 forks source link

Issue with UnrealBloomPass #25

Open willsmanley opened 2 days ago

willsmanley commented 2 days ago

Firstly thank you for maintaining an amazing port!!!

Repro example:

import 'dart:async';
import 'package:flutter/material.dart' as material;
import 'package:three_js/three_js.dart' as three;
import 'package:three_js_postprocessing/three_js_postprocessing.dart'
    as postprocessing;
import 'dart:math';
import 'package:three_js_geometry/three_js_geometry.dart';

const String vertexShader = """
      uniform float u_time;

      vec3 mod289(vec3 x)
      {
        return x - floor(x * (1.0 / 289.0)) * 289.0;
      }

      vec4 mod289(vec4 x)
      {
        return x - floor(x * (1.0 / 289.0)) * 289.0;
      }

      vec4 permute(vec4 x)
      {
        return mod289(((x*34.0)+10.0)*x);
      }

      vec4 taylorInvSqrt(vec4 r)
      {
        return 1.79284291400159 - 0.85373472095314 * r;
      }

      vec3 fade(vec3 t) {
        return t*t*t*(t*(t*6.0-15.0)+10.0);
      }

      // Classic Perlin noise, periodic variant
      float pnoise(vec3 P, vec3 rep)
      {
        vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
        vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
        Pi0 = mod289(Pi0);
        Pi1 = mod289(Pi1);
        vec3 Pf0 = fract(P); // Fractional part for interpolation
        vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
        vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
        vec4 iy = vec4(Pi0.yy, Pi1.yy);
        vec4 iz0 = Pi0.zzzz;
        vec4 iz1 = Pi1.zzzz;

        vec4 ixy = permute(permute(ix) + iy);
        vec4 ixy0 = permute(ixy + iz0);
        vec4 ixy1 = permute(ixy + iz1);

        vec4 gx0 = ixy0 * (1.0 / 7.0);
        vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
        gx0 = fract(gx0);
        vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
        vec4 sz0 = step(gz0, vec4(0.0));
        gx0 -= sz0 * (step(0.0, gx0) - 0.5);
        gy0 -= sz0 * (step(0.0, gy0) - 0.5);

        vec4 gx1 = ixy1 * (1.0 / 7.0);
        vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
        gx1 = fract(gx1);
        vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
        vec4 sz1 = step(gz1, vec4(0.0));
        gx1 -= sz1 * (step(0.0, gx1) - 0.5);
        gy1 -= sz1 * (step(0.0, gy1) - 0.5);

        vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
        vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
        vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
        vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
        vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
        vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
        vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
        vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);

        vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
        g000 *= norm0.x;
        g010 *= norm0.y;
        g100 *= norm0.z;
        g110 *= norm0.w;
        vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
        g001 *= norm1.x;
        g011 *= norm1.y;
        g101 *= norm1.z;
        g111 *= norm1.w;

        float n000 = dot(g000, Pf0);
        float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
        float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
        float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
        float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
        float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
        float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
        float n111 = dot(g111, Pf1);

        vec3 fade_xyz = fade(Pf0);
        vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
        vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
        float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 
        return 2.2 * n_xyz;
      }

      uniform float u_frequency;

      void main() {
          float noise = 3.0 * pnoise(position + u_time, vec3(10.0));
          float displacement = (u_frequency / 30.) * (noise / 10.);
          vec3 newPosition = position + normal * displacement;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
      }
""";

const String fragmentShader = """
        uniform float u_red;
        uniform float u_blue;
        uniform float u_green;
        void main() {
            gl_FragColor = vec4(vec3(u_red, u_green, u_blue), 1. );
        }
""";

List<double> values = [
  42.9375,
  53,
  60,
   // a bunch more like this, truncated for brevity
];

class OrbWidget extends material.StatefulWidget {
  const OrbWidget({super.key});

  @override
  OrbWidgetState createState() => OrbWidgetState();
}

class OrbWidgetState extends material.State<OrbWidget> {
  bool isPlaying = true;
  double lastValue = 0.0;
  int index = 0;
  late three.ThreeJS threeJs;
  late three.Clock clock;

  // Variables for EffectComposer and passes
  late postprocessing.EffectComposer composer;
  late postprocessing.RenderPass renderPass;
  late postprocessing.UnrealBloomPass bloomPass;

  var params = {
    'exposure': 1.0,
    'bloomStrength': 1.5,
    'bloomThreshold': 0.0,
    'bloomRadius': 0.1,
  };

  @override
  void initState() {
    clock = three.Clock();
    threeJs = three.ThreeJS(
      settings: three.Settings(alpha: true),
      onSetupComplete: () {
        setState(() {});
      },
      setup: setup,
    );
    super.initState();
  }

  @override
  void dispose() {
    threeJs.dispose();
    super.dispose();
  }

  @override
  material.Widget build(material.BuildContext context) {
    return material.Stack(
      children: [
        material.Container(color: const material.Color(0xFFEAEAEA)),
        threeJs.build(),
      ],
    );
  }

  late three.Mesh mesh;

  Future<void> setup() async {
    threeJs.camera =
        three.PerspectiveCamera(45, threeJs.width / threeJs.height, 1, 2000);
    threeJs.camera.position.setValues(0, -2, 14);
    threeJs.camera.lookAt(three.Vector3(0, 0, 0));

    threeJs.scene = three.Scene();

    // Create shader material with uniforms defined as maps
    var shaderMaterial = three.ShaderMaterial({
      three.MaterialProperty.vertexShader: vertexShader,
      three.MaterialProperty.fragmentShader: fragmentShader,
      three.MaterialProperty.wireframe: true,
      three.MaterialProperty.uniforms: {
        'u_time': {'value': 0.0},
        'u_frequency': {'value': 0.0},
        'u_red': {'value': 1.0},
        'u_green': {'value': 1.0},
        'u_blue': {'value': 1.0},
      },
    });

    var geometry = IcosahedronGeometry(4, 30);
    mesh = three.Mesh(geometry, shaderMaterial);
    threeJs.scene.add(mesh);

    // Set up the renderer
    threeJs.renderer?.outputEncoding = three.sRGBEncoding;
    threeJs.renderer?.physicallyCorrectLights = true;
    threeJs.renderer?.toneMapping = three.ReinhardToneMapping;
    threeJs.renderer?.toneMappingExposure = params['exposure'] as double;
    threeJs.renderer
        ?.setSize(threeJs.width.toDouble(), threeJs.height.toDouble());

    // Create the render passes
    renderPass = postprocessing.RenderPass(threeJs.scene, threeJs.camera);

    // Initialize UnrealBloomPass with correct parameters
    bloomPass = postprocessing.UnrealBloomPass(three.Vector2(256, 256), 1.5, 0.1, 0.0);
    // bloomPass =
    //     postprocessing.UnrealBloomPass(three.Vector2(256, 256), 0.0, 0.0, 0.0);
    // bloomPass = postprocessing.UnrealBloomPass(
    //   three.Vector2(threeJs.width.toDouble(), threeJs.height.toDouble()),
    //   params['bloomStrength'] as double,
    //   params['bloomRadius'] as double,
    //   params['bloomThreshold'] as double,
    // );

    // Create the EffectComposer
    composer = postprocessing.EffectComposer(threeJs.renderer!);
    composer.addPass(renderPass);
    composer.addPass(bloomPass);

    // Set the size of the EffectComposer
    // composer.setSize(threeJs.width.toInt(), threeJs.height.toInt());

    // Set up animation loop
    threeJs.addAnimationEvent((dt) {
      animate(dt);
    });
  }

  void animate(double dt) {
    // Update mesh rotation
    mesh.rotation.x += 0.01;
    mesh.rotation.y += 0.005;

    // Update uniforms
    final material = mesh.material as three.ShaderMaterial;

    // Update time uniform
    material.uniforms['u_time']['value'] = clock.getElapsedTime();

    // Update frequency uniform based on play state
    if (isPlaying) {
      double targetValue = values[index];
      material.uniforms['u_frequency']['value'] =
          min(targetValue, lastValue + 4);
    } else {
      material.uniforms['u_frequency']['value'] = max(0, lastValue - 2);
    }

    // Update lastValue and index
    lastValue = material.uniforms['u_frequency']['value'];
    if (isPlaying) {
      index = (index + 1) % values.length;
    }

    // Update bloom parameters via properties
    // bloomPass.threshold = params['bloomThreshold'] as double;
    // bloomPass.strength = params['bloomStrength'] as double;
    // bloomPass.radius = params['bloomRadius'] as double;

    // Render using the EffectComposer
    composer.render(null, dt);
  }
}

══╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY
╞═════════════════════════════════════════════════════════
The following OpenGLException was thrown during a scheduler callback:

Error linking program:
Internal error compiling Metal shader:
program_source:77:73: error: no matching function for call to
'_ulerpBloomFactor'
    out._upc_fragColor = ((((((float4(v_44._ubloomTintColors[0], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[0], v_44)) *
_ublurTexture1.sample(_ublurTexture1Smplr,
in._uvUv)) + ((float4(v_44._ubloomTintColors[1], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[1],
v_44)) * _ublurTexture2.sample(_ublurTexture2Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[2], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[2], v_44)) *
_ublurTexture3.sample(_ublurTexture3Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[3], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[3], v_44)) *
_ublurTexture4.sample(_ublurTexture4Smplr,
in._uvUv))) + ((float4(v_44._ubloomTintColors[4], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[4],
v_44)) * _ublurTexture5.sample(_ublurTexture5Smplr, in._uvUv))) *
v_44._ubloomStrength;
                                                                 ^~~~~~~~~~
                                                                 ~~~~~~~
program_source:59:7: note: candidate function not viable: no known
conversion from 'const constant
float4' (vector of 4 'float' values) to 'float' for 1st argument
float _ulerpBloomFactor(float _ufactor, constant defaultUniforms& v_44)
      ^
program_source:77:223: error: no matching function for call to
'_ulerpBloomFactor'
    out._upc_fragColor = ((((((float4(v_44._ubloomTintColors[0], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[0], v_44)) *
_ublurTexture1.sample(_ublurTexture1Smplr,
in._uvUv)) + ((float4(v_44._ubloomTintColors[1], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[1],
v_44)) * _ublurTexture2.sample(_ublurTexture2Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[2], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[2], v_44)) *
_ublurTexture3.sample(_ublurTexture3Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[3], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[3], v_44)) *
_ublurTexture4.sample(_ublurTexture4Smplr,
in._uvUv))) + ((float4(v_44._ubloomTintColors[4], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[4],
v_44)) * _ublurTexture5.sample(_ublurTexture5Smplr, in._uvUv))) *
v_44._ubloomStrength;
                                                                 ^~~~~~~~~~
                                                                 ~~~~~~~
program_source:59:7: note: candidate function not viable: no known
conversion from 'const constant
float4' (vector of 4 'float' values) to 'float' for 1st argument
float _ulerpBloomFactor(float _ufactor, constant defaultUniforms& v_44)
      ^
program_source:77:374: error: no matching function for call to
'_ulerpBloomFactor'
    out._upc_fragColor = ((((((float4(v_44._ubloomTintColors[0], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[0], v_44)) *
_ublurTexture1.sample(_ublurTexture1Smplr,
in._uvUv)) + ((float4(v_44._ubloomTintColors[1], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[1],
v_44)) * _ublurTexture2.sample(_ublurTexture2Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[2], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[2], v_44)) *
_ublurTexture3.sample(_ublurTexture3Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[3], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[3], v_44)) *
_ublurTexture4.sample(_ublurTexture4Smplr,
in._uvUv))) + ((float4(v_44._ubloomTintColors[4], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[4],
v_44)) * _ublurTexture5.sample(_ublurTexture5Smplr, in._uvUv))) *
v_44._ubloomStrength;
                                                                 ^~~~~~~~~~
                                                                 ~~~~~~~
program_source:59:7: note: candidate function not viable: no known
conversion from 'const constant
float4' (vector of 4 'float' values) to 'float' for 1st argument
float _ulerpBloomFactor(float _ufactor, constant defaultUniforms& v_44)
      ^
program_source:77:525: error: no matching function for call to
'_ulerpBloomFactor'
    out._upc_fragColor = ((((((float4(v_44._ubloomTintColors[0], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[0], v_44)) *
_ublurTexture1.sample(_ublurTexture1Smplr,
in._uvUv)) + ((float4(v_44._ubloomTintColors[1], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[1],
v_44)) * _ublurTexture2.sample(_ublurTexture2Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[2], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[2], v_44)) *
_ublurTexture3.sample(_ublurTexture3Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[3], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[3], v_44)) *
_ublurTexture4.sample(_ublurTexture4Smplr,
in._uvUv))) + ((float4(v_44._ubloomTintColors[4], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[4],
v_44)) * _ublurTexture5.sample(_ublurTexture5Smplr, in._uvUv))) *
v_44._ubloomStrength;
                                                                 ^~~~~~~~~~
                                                                 ~~~~~~~
program_source:59:7: note: candidate function not viable: no known
conversion from 'const constant
float4' (vector of 4 'float' values) to 'float' for 1st argument
float _ulerpBloomFactor(float _ufactor, constant defaultUniforms& v_44)
      ^
program_source:77:676: error: no matching function for call to
'_ulerpBloomFactor'
    out._upc_fragColor = ((((((float4(v_44._ubloomTintColors[0], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[0], v_44)) *
_ublurTexture1.sample(_ublurTexture1Smplr,
in._uvUv)) + ((float4(v_44._ubloomTintColors[1], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[1],
v_44)) * _ublurTexture2.sample(_ublurTexture2Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[2], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[2], v_44)) *
_ublurTexture3.sample(_ublurTexture3Smplr, in._uvUv))) +
((float4(v_44._ubloomTintColors[3], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[3], v_44)) *
_ublurTexture4.sample(_ublurTexture4Smplr,
in._uvUv))) + ((float4(v_44._ubloomTintColors[4], 1.0) *
_ulerpBloomFactor(v_44._ubloomFactors[4],
v_44)) * _ublurTexture5.sample(_ublurTexture5Smplr, in._uvUv))) *
v_44._ubloomStrength;
                                                                 ^~~~~~~~~~
                                                                 ~~~~~~~
program_source:59:7: note: candidate function not viable: no known
conversion from 'const constant
float4' (vector of 4 'float' values) to 'float' for 1st argument
float _ulerpBloomFactor(float _ufactor, constant defaultUniforms& v_44)
      ^

 GLES error 0

When the exception was thrown, this was the stack:
#0      RenderingContext.linkProgram
(package:flutter_angle/desktop/wrapper.dart:966:9)
#1      WebGLProgram.init
(package:three_js_core/renderers/webgl/web_gl_program.dart:339:8)
#2      new WebGLProgram
(package:three_js_core/renderers/webgl/web_gl_program.dart:32:5)
#3      WebGLPrograms.acquireProgram
(package:three_js_core/renderers/webgl/web_gl_programs.dart:401:17)
#4      WebGLRenderer.getProgram
(package:three_js_core/renderers/web_gl_renderer.dart:965:30)
#5      WebGLRenderer.setProgram
(package:three_js_core/renderers/web_gl_renderer.dart:1133:17)
#6      WebGLRenderer.renderBufferDirect
(package:three_js_core/renderers/web_gl_renderer.dart:446:28)
#7      WebGLRenderer.renderObject
(package:three_js_core/renderers/web_gl_renderer.dart:905:7)
#8      WebGLRenderer.renderObjects
(package:three_js_core/renderers/web_gl_renderer.dart:872:9)
#9      WebGLRenderer.renderScene
(package:three_js_core/renderers/web_gl_renderer.dart:802:35)
#10     WebGLRenderer.render
(package:three_js_core/renderers/web_gl_renderer.dart:661:7)
#11     FullScreenQuad.render
(package:three_js_postprocessing/post/pass.dart:64:14)
#12     UnrealBloomPass.render
(package:example/new/unreal_bloom_pass.dart:276:12)
#13     EffectComposer.render
(package:three_js_postprocessing/post/effect_composer.dart:119:12)
#14     OrbWidgetState.animate
(package:example/new/first_dart_attempt.dart:162:14)
#15     OrbWidgetState.setup.<anonymous closure>
(package:example/new/first_dart_attempt.dart:126:7)
#16     ThreeJS.animate
(package:three_js_core/others/three_viewer.dart:162:21)
#17     Ticker._tick (package:flutter/src/scheduler/ticker.dart:261:12)
#18     SchedulerBinding._invokeFrameCallback
(package:flutter/src/scheduler/binding.dart:1397:15)
#19     SchedulerBinding.handleBeginFrame.<anonymous closure>
(package:flutter/src/scheduler/binding.dart:1240:11)
#20     _LinkedHashMapMixin.forEach
(dart:collection-patch/compact_hash.dart:633:13)
#21     SchedulerBinding.handleBeginFrame
(package:flutter/src/scheduler/binding.dart:1238:17)
#22     SchedulerBinding._handleBeginFrame
(package:flutter/src/scheduler/binding.dart:1155:5)
#23     _invoke1 (dart:ui/hooks.dart:328:13)
#24     PlatformDispatcher._beginFrame
(dart:ui/platform_dispatcher.dart:397:5)
#25     _beginFrame (dart:ui/hooks.dart:272:31)

This exception was thrown in the context of a scheduler callback. When the
scheduler callback was _registered_ (as opposed to when the exception was
thrown), this was the stack:
#2      SchedulerBinding.scheduleFrameCallback
(package:flutter/src/scheduler/binding.dart:611:49)
#3      Ticker.scheduleTick
(package:flutter/src/scheduler/ticker.dart:277:46)
#4      Ticker.start (package:flutter/src/scheduler/ticker.dart:183:7)
#5      ThreeJS.initScene
(package:three_js_core/others/three_viewer.dart:268:13)
<asynchronous suspension>
#6      ThreeJS.initPlatformState
(package:three_js_core/others/three_viewer.dart:292:5)
<asynchronous suspension>
(elided 2 frames from class _FrameCallbackEntry)
═══════════════════════════════════════════════════════════════════════════
═════════════════════════

I will keep working on trying to see if I'm doing something wrong here, but I'm pretty sure this is a bug with the implementation.

I think besides this error, the UnrealPass inititalizer needs to be updated to:

    this.radius = radius;
    this.threshold = threshold;
willsmanley commented 2 days ago

CC @Knightro63 thank you!

Knightro63 commented 6 hours ago

Hi @willsmanley,

There is a known issue with some of the post processing passes. Could you give me an image or video of what it is ment to look like. I will see if I can find another way to do it while I am working out some of the kinks.