Search code examples
fragmentglslshaderwebglcurtains.js

Multiple textures webgl frag shader problem


I'm using curtains.js for my web project. I'm telling you this because I tried to find similar questions on stackoverflow about this topic but due to the way curtains is made, I wasn't able to reproduce the answers. curtains.js is very specific about objects in the DOM of the html. With that in mind, I would like to pose the following question:

Is there a way I could make my code more beautiful? Currently I have this:

export default [...]

varying vec2 vTextureCoord; 
varying vec2 vDisplacedTextureCoord;
varying vec2 vDistortionEffect;

// custom uniforms
uniform float uDisplacementStrength;
uniform float uVideoQueue;

// our textures samplers
uniform sampler2D displacementTexture2;
uniform sampler2D sourceVideo0;
uniform sampler2D sourceVideo1;
uniform sampler2D sourceVideo2;
uniform sampler2D sourceVideo3;
uniform sampler2D sourceVideo4;
uniform sampler2D sourceVideo5;
uniform sampler2D sourceVideo6;
uniform sampler2D sourceVideo7;
uniform sampler2D sourceVideo8;
uniform sampler2D sourceVideo9;
uniform sampler2D canvasTexture;

void main (void) {
    vec2 textureCoords = vTextureCoord;
    
    vec4 mouseEffect = texture2D(canvasTexture, textureCoords);
    vec4 mapEffect = texture2D(displacementTexture2, textureCoords);
    
    vec4 colorEffect = texture2D(sourceVideo0, textureCoords);
    vec4 finalColor = texture2D(sourceVideo0, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));

    if (uVideoQueue == 1.0) {
        colorEffect = texture2D(sourceVideo1, textureCoords);
        finalColor = texture2D(sourceVideo1, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 2.0) {
        colorEffect = texture2D(sourceVideo2, textureCoords);
        finalColor = texture2D(sourceVideo2, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 3.0) {
        colorEffect = texture2D(sourceVideo3, textureCoords);
        finalColor = texture2D(sourceVideo3, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 4.0) {
        colorEffect = texture2D(sourceVideo4, textureCoords);
        finalColor = texture2D(sourceVideo4, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 5.0) {
        colorEffect = texture2D(sourceVideo5, textureCoords);
        finalColor = texture2D(sourceVideo5, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 6.0) {
        colorEffect = texture2D(sourceVideo6, textureCoords);
        finalColor = texture2D(sourceVideo6, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 7.0) {
        colorEffect = texture2D(sourceVideo7, textureCoords);
        finalColor = texture2D(sourceVideo7, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 8.0) {
        colorEffect = texture2D(sourceVideo8, textureCoords);
        finalColor = texture2D(sourceVideo8, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } else if (uVideoQueue == 9.0) {
        colorEffect = texture2D(sourceVideo9, textureCoords);
        finalColor = texture2D(sourceVideo9, vDisplacedTextureCoord + vec2(mapEffect.r * mouseEffect.r * colorEffect.r, 0.0));
    } 
        
    gl_FragColor = finalColor;

[...]

In my main script there is a uniform that is constantly updated to tell the shader which texture it should use for the output (uVideoQueue) and ranges from 0-9 (the current number is the number of the specific video)

The sampler2D uniforms (0-9) are the videos that are in the DOM and need to be drawn.

As you can see in the code, with every draw call in the shader, the function has to check which sourceVideo sampler2D uniform variable it needs to add the the fragment shader.

As you can see: every if/else if line of code is the same as the one on top but only the name of the sampler2D uniform is different. There must be a way to have something like this

vec4 colorEffect = texture2D(sourceVideo **+ i**, textureCoords);
vec4 finalColor = texture2D(sourceVideo **+ i**, [...]);

where i is the number uVideoQueue.

The code as is works fine, but I think it is more processor intense with all the if/else if statements compared to a more elegant solution where only 2 lines are needed...

Thanks!


Solution

  • Based on the official documentation slideshow example code you should use an additional activeTexture texture and update its source whenever you want.

    Your fragment shader will then become a lot easier to write, using only the uActiveTexture sampler uniform.

    Here is a minimal codesandbox demonstrating the concept: https://codepen.io/martinlaxenaire/pen/YzpVYLE

    Cheers,