1

I am writing a custom blur shader,

        "#include <common>",

        // blur samples
        "const int SAMPLES = 10;",

        "uniform float radius;",

        "uniform sampler2D tDiffuse;",

        "varying vec2 vUv;",

        "void main() {",

        // sample the source
        "    vec2 uv = vUv;",

        "    vec4 cTextureScreen = texture2D( tDiffuse, vUv );",
        "    vec3 res = vec3(0);",

        "    for(int i = 0; i < SAMPLES; ++i) {",
        "        res += cTextureScreen.a;",
        "        vec2 d = vec2(0.5) - uv;",
        "        uv += d * radius;",
        "    }",

        "    gl_FragColor = vec4(res/float(SAMPLES), 1.0);",

        "}"

Which is a straight port of: https://www.shadertoy.com/view/ltVSRK

For use with the effect composer of THREE.

Unfortunately, it appears that tDiffuse is not packed as rgb, and I am pretty confused as to how I can achieve the desired effect or the conversion I need.

See also the following question for more details: what do texture2D().r and texture2D().a mean?

Mauro Colella
  • 446
  • 6
  • 12
  • Because the color channels are not available. That's exactly the problem. See also the other linked question. They rgb or xyz channels are always set to zero because the passed in texture, tDiffuse, is represented in a yuv format. That texture, to the best of my knowledge, is bound from within the effects composer of THREE.js. – Mauro Colella May 11 '20 at 11:02
  • "The texture is bound from within the effects composer of THREE.js" so I cannot change that. Pretty please read my comments and the other question, but essentially yes. – Mauro Colella May 11 '20 at 12:03
  • Oh, ok. Well, the effects composer is built into THREE.js, but I am using this from within react-three-fiber. The passed-in buffer is a render of the screen. I am seeing things related to the setup, for example, that the renderer can be configured to use srgb, see: https://discourse.threejs.org/t/srgb-encoding-as-a-postprocess-pass/12278 But for the time being, I will ask the question inside the discussion forum of react-three-fiber. – Mauro Colella May 11 '20 at 12:46
  • 1
    This would be the setup code: https://github.com/mrdoob/three.js/blob/dev/examples/jsm/postprocessing/EffectComposer.js – Mauro Colella May 11 '20 at 12:50

1 Answers1

1

I ended up solving my problems (and working around them):

  1. THREE uses a linear colorspace for textures inside (effect composer) shaders.
  2. It provides conversion utilities, such as LinearTosRGB.
  3. My code above contains an error. The effect of the line vec4 cTextureScreen = texture2D( tDiffuse, vUv ); should be repeated inside the for loop in order to properly resample fragments while zooming outward.
  4. I ended up using a more elaborate shader, drawn from the Wagner effects library: https://github.com/spite/Wagner/blob/master/fragment-shaders/zoom-blur-fs.glsl#L1
uniform sampler2D tInput;
uniform vec2 center;
uniform float strength;
uniform vec2 resolution;
varying vec2 vUv;

float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}

void main(){
    vec4 color=vec4(0.0);
    float total=0.0;
    vec2 toCenter=center-vUv*resolution;
    float offset=random(vec3(12.9898,78.233,151.7182),0.0);
    for(float t=0.0;t<=40.0;t++){
        float percent=(t+offset)/40.0;
        float weight=4.0*(percent-percent*percent);
        vec4 sample=texture2D(tInput,vUv+toCenter*percent*strength/resolution);
        sample.rgb*=sample.a;
        color+=sample*weight;
        total+=weight;
    }
    gl_FragColor=color/total;
    gl_FragColor.rgb/=gl_FragColor.a+0.00001;
}

And I am also looking at fast and efficient implementations of Gaussian Blur (https://github.com/Jam3/glsl-fast-gaussian-blur) to further soften my current results.

All in all, my original issue was the result of both a bug in my own code, and of THREE using a linear colorspace for textures.

Mauro Colella
  • 446
  • 6
  • 12