在WebGL着色器中使用两个以上目标之间的mix()进行变形 [英] Morphing in WebGL shaders with mix() between more then two targets
问题描述
我正在尝试使用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屋!