在WebGL着色器中使用两个以上目标之间的mix()进行变形 [英] Morphing in WebGL shaders with mix() between more then two targets

查看:1413
本文介绍了在WebGL着色器中使用两个以上目标之间的mix()进行变形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用three.js构建图像滑块,并且难以将头部传递适当的状态到glsl着色器,因此我可以在幻灯片之间进行转换.我可以轻松地在两个目标(纹理或模型)之间完成此操作只需在0和1之间放松,然后将其作为attrib浮点传递即可,如下所示:

I am trying to build an image slider using three.js and am having difficulties with wrapping my head around passing the appropriate state to the glsl shaders so I can transition between the slides. I can easily do it between two targets (be it textures or models) with simply easing between 0 and 1 and passing it as an attrib float like this:

attribute float mix;
vec4 color = mix(tex1, tex2, mix);

但是我不明白如何用两个以上的目标来实现它.我应该传递一个数字并编写一堆if语句吗?

But I can't understand how to approach it with more then 2 targets. Should I pass a number and do a bunch of if statements?

我设置了缓冲平面的几何形状和着色器材质,其中包含我的3个纹理,如下所示:

I set up my buffer plane geometry and my shader material, which contains my 3 textures, like this:

const uniforms = {
  time: { value: 0 },
  tex1: { type: 't', value: null },
  tex2: { type: 't', value: null },
  tex3: { type: 't', value: null },
  activeTexture: { type: 'i', value: 0 },
  mixFactor:  { value: 0 }
}
const vertexShader = document.querySelector('#vertex-shader').text
const fragmentShader = document.querySelector('#fragment-shader').text

const geometry = new THREE.PlaneBufferGeometry(80, 40, 20, 20)
    const material = new THREE.ShaderMaterial({
    uniforms,
    vertexShader,
    fragmentShader
})

// textures are loaded here...

// transition using GSAP
function shift ()  {
    let ease = Power3.easeInOut
    if (counter === 0) {
      TweenMax.to(uniforms.mixFactor, 2, { value: 1, ease, onStart () {
        uniforms.activeTexture.value = 1
      } })
    } else if (counter === 1) {
      TweenMax.to(uniforms.mixFactor, 2, { value: 1, ease, onComplete () {
        uniforms.activeTexture.value = 2
      } })
   } else if (counter === 2) {
      TweenMax.to(uniforms.mixFactor, 2, { value: 2, ease, onComplete () {
        uniforms.activeTexture.value = 0
   } })


   console.log(uniforms.activeTexture.value)

   counter += 1
   if (counter === 3) counter = 0
}


// glsl
// morph between different targets depending on the passed int attribute

void main () {
  vec4 texColor = vec4(0.0);
  if (activeTexture == 0) {
  texColor = transition(tex1, tex2, vUv, mixFactor);
  } else if (activeTexture == 1) {
    texColor = transition(tex2, tex3, vUv, mixFactor);
  } else if (activeTexture == 2) {
    texColor = transition(tex3, tex1, vUv, mixFactor);
  }
  gl_FragColor = texColor;
}

这没有给我想要的效果(纹理突然在彼此之间切换,没有过渡到位,也有点丑陋).我是三岁的孩子,我不知道该如何解决这个问题.如何做到这一点?

This doesn't give me the desired effect (the textures abruptly switch between one another, don't transition into place, also it's a bit ugly). I am new to three and am clueless how should I even approach the problem. How does one do this?

推荐答案

我带来了5个kopeikas:)

I brought my 5 kopeikas :)

例如,我们希望过渡几个图片.这样我们就可以在制服中使用数组.

For example, we want to have transition for several pics. So we can use arrays in our uniforms.

我们在这里

var uniforms = {
  textures: {
    value: []
  },
  transition: {
    value: 0
  }
};
var textureLoader = new THREE.TextureLoader();
textureLoader.setCrossOrigin("");
var pics = [
  "https://threejs.org/examples/textures/UV_Grid_Sm.jpg",
  "https://threejs.org/examples/textures/colors.png",
  "https://threejs.org/examples/textures/planets/moon_1024.jpg",
  "https://threejs.org/examples/textures/decal/decal-normal.jpg"
];
pics.forEach((p, idx)=>{
    textureLoader.load(p, function(tex){
    uniforms.textures.value[idx] = tex;
    tex.needsUpdate = true;
  })
});

我们的几何图形和顶点着色器很常见:

Our geometry and vertex shader are usual:

var planeGeom = new THREE.PlaneBufferGeometry(10, 10);
var vertShader = `
    varying vec2 vUv;
    void main()
  {
    vUv = uv;
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;
  }
`;

Magic出现在我们的片段着色器中,该片段着色器是根据数组的长度动态生成的,并具有指向图片的链接:

Magic comes here in our fragment shader, which built dynamically and based on the length of our array with links to pics:

var fragShader = `
  uniform sampler2D textures[` + pics.length + `];
  uniform float transition;
  varying vec2 vUv;

  vec4 getTexture(int index){
    for(int i = 0; i < ` + pics.length + `; i++){
         if (i == index){   return texture2D(textures[i],vUv); }
    }
  }
  void main()
  {
    if (transition == 1.){
        gl_FragColor = texture2D(textures[` + (pics.length - 1) + `], vUv); // show last
    }
    else {
      float chunk = 1. / ` + (pics.length - 1) + `.; // amount of transitions = amount of pics - 1
        float t = floor(transition / chunk);
      int idx0 = int(t);
      int idx1 = int(t) + 1; 
      gl_FragColor = mix(
        getTexture(idx0),
        getTexture(idx1),
        (transition - (float(t) * chunk)) * ` + (pics.length - 1) + `.
      );
    }
  }
`;

该解决方案足够灵活,因此您可以根据需要进行任意数量的转换.

The solution is flexible enough, thus you can have as many transitions as you want.

jsfiddle 示例r86

这篇关于在WebGL着色器中使用两个以上目标之间的mix()进行变形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆