Source: lib/xutils/shaders/color-array.glsl.js


import {GlxError, glx, glConfig, initPhongUni, updatePhongUni} from './glx.glsl';
import {ShaderFlag, ShaderAlpha} from '../../component/visual';

/**Get a shader that can be morphed with colors and textures.<br>
 * Test page: test/html/morph-color.html<br>
 * ShaderFlag: colorArray<br>
 * See test page for different mixing mode results.
 *
 * <a href='file:///home/ody/git/x-visual/docs/design-memo/shaders/phong.html'>
 * x-visual doc: Morphing Phong Material</a><br>
 * <a href='http://www.cs.toronto.edu/~jacobson/phong-demo/'>
 * referencing implementation @ cs.toronto.edu</a>
 * @param {object} vparas <pre>{
   texMix: vparas.texMix - ShaderAlpha.multiply | additive | mix, see {@link XComponent.ShaderAlpha}
   uniforms: {
     u_ambientColor: vec3
     u_specularColor: vec3
     u_lightPos: vec3 - diffuse light direction & specular light position
     u_alpha: float - object color alpha
   }
  }</pre>
 * @param {object} paras
 * @return {object} {vertexShader, fragmentShader}
 * @member xglsl.phongMorph2
 * @function
*/
export function phongMorph2(vparas) {
    // array uniform example:
    // https://stackoverflow.com/questions/60154858/rotate-a-texture-map-on-only-one-face-of-a-cube/60158820#60158820
    var tex = vparas.u_tex;
    var colr = vparas.colors;
    var len = colr.length;

    if (!colr || tex && !Array.isArray(tex)
        || !Array.isArray(colr)
        || tex && colr.length != tex.length
        || colr.length <= 0) {
        console.error('paras: ', vparas);
        throw new GlxError(`paras.u_tex and paras.colors not matching (required at least one color or texture): ${vparas}`);
    }

    // u_tex can be eighter empty or same length with colors
    var fragUnis = tex ? `uniform sampler2D u_tex[${len}];` : '';
    var vertUnis = `uniform vec3 u_colors[${len}];`;
	var bothUnis = '';
    var morphvert = '';

    for (var i = 0; i < len; i++) {
        bothUnis += `\nuniform float u_morph${i};`;
        if (i > 0)
            // vec3 colori = u_color[0];
            morphvert += `\ncolori = blend(colori, u_colors[${i}], u_morph${i - 1});`;
    }
    if (i > 0)
        morphvert += `\ncolori = blend(colori, u_colors[0], u_morph${len - 1});`;

    var morphfrag = '';
    if ( tex ) {
        // len > 0
        morphfrag = `
            tex0 = texture2D( u_tex[0], vUv );`;
        for (var i = 1; i < len; i++) {
            morphfrag += `
            texi = texture2D( u_tex[${i}], vUv );
            if (texi.a > 0.0)
                tex0 = blend( tex0, texi, u_morph${i-1} );` ;
        }
        if (len > 1)
            morphfrag += `
            texi = texture2D( u_tex[0], vUv );
            tex0 = blend(tex0, texi, u_morph${len - 1});` ;
    }
    else morphfrag = `tex0 = vec4(vmorphColor, 1.);`;

    // vec4 blend(txa, txb, t);
    var statements = '';
    if (vparas.texMix & ShaderAlpha.product) {
        statements = `clr = txa * txb;`;
    }
    if (vparas.texMix & ShaderAlpha.multiply) {
        statements = `clr = txa * txb * t;`;
    }
    if (vparas.texMix & ShaderAlpha.differential) {
        statements = `clr = txa * abs(0.5 - t) + txb * abs(t - 0.5);`;
    }
    else if (vparas.texMix & ShaderAlpha.additive) {
        // default ShaderAlpha.mix
        statements = `clr = clamp(txa * (1.0 - t) + txb * t, 0.0, 1.0);`;
    }
    else {
        statements = `clr = mix(txa, txb, t);`;
    }

    var vertBlender = `vec3 blend(vec3 txa, vec3 txb, float t) {
        vec3 clr = vec3(0.4);
        ${statements}
        return clr;
    } `;

    var fragBlender = `vec4 blend(vec4 txa, vec4 txb, float t) {
        vec4 clr = vec4(0.4);
        ${statements}
        return clr;
    } `;

    return {
  vertexShader: `
    ${glx.u_alpha}
    ${vertUnis}
    ${bothUnis}
    uniform float u_lightIntensity;

    varying vec3 vnormal;
    varying vec3 vertPos;
    varying float vAlpha;
    varying vec2 vUv;

    varying vec3 vmorphColor;
    varying float vIntense;

    ${vertBlender}
    vec3 morphColors() {
        vec3 colori = u_colors[0];
        ${morphvert}
        return colori;
    }

    ${glx.intenseAlpha}

    void main(){
        vUv = uv;
        vAlpha = u_alpha;

        vec4 vertPos4 = modelViewMatrix * vec4(position, 1.0);
        vertPos = vertPos4.xyz;
        gl_Position = projectionMatrix * vertPos4;

        vmorphColor = morphColors();
        vIntense = intenseAlpha(u_lightIntensity);
        vnormal = normalMatrix * normal;
    } `,

  fragmentShader: `
    ${glx.u_alpha}
    ${bothUnis}
    ${fragUnis}

    uniform float u_texWeight;
    uniform vec4  u_color; // diffuse
    uniform float u_shininess;
    uniform vec3  u_ambientColor;
    uniform vec3  u_specularColor;
    uniform vec3  u_lightPos;

    varying float vAlpha;` /* NOT used ? */ + `
    varying vec2  vUv;

    varying vec3  vertPos;
    varying vec3  vmorphColor;
    varying vec3  vnormal;
    varying float vIntense;

    ${glx.phongLight}

    ${fragBlender}
    vec4 mixTexi() {
        vec4 tex0 = vec4(0.0);
        vec4 texi = vec4(0.0);
        ${morphfrag}
        if (tex0.a < 0.00001)
            discard;
        return tex0;
    }

    void main() {
        vec3 diffuseColor = mix(vmorphColor, u_color.xyz, vIntense);
        vec4 phong = phongLight(vnormal, u_lightPos, cameraPosition, vertPos,
                u_ambientColor.xyz, diffuseColor, u_specularColor.xyz, u_shininess)
                * vIntense;

        // gl_FragColor = mix( phong, mixTexi(), u_texWeight * u_alpha );
        gl_FragColor = mix( phong, mixTexi(), u_texWeight );
        gl_FragColor.a *= u_alpha;
    }
  `};
}

phongMorph2.initUniform = initPhongUni;
phongMorph2.updateUniform = updatePhongUni;