【问题标题】:Three.js / WebGL How to mirror one side of a textureThree.js / WebGL 如何镜像纹理的一侧
【发布时间】:2020-05-28 17:40:49
【问题描述】:

我基本上是在尝试仅用一侧实现万花筒效果,但我正在处理很多点,所以我希望在着色器中发生这种情况。但是,如果有一个 Threejs 技巧可以镜像一半纹理或 Points 对象,那就太好了。我尝试应用转换矩阵,但无法正常工作。

我发现了一个旧的KaleidoShader,它需要使用 EffectComposer,但我想自己手动实现它(没有 EffectComposer),我正在努力这样做。我正在使用 FBO,我尝试在我的模拟和渲染着色器中添加该着色器的代码,但它根本没有任何效果。我是否必须再添加另一个 FBO 纹理,或者是否可以在现有着色器之一中进行这些计算?

供视觉参考https://ma-hub.imgix.net/wp-images/2019/01/23205110/premiere-pro-mirror-effect.jpg

我花了这么多时间没有深入了解这一点,希望有人能指出我正确的方向。

谢谢

【问题讨论】:

    标签: javascript three.js glsl


    【解决方案1】:

    我刚刚关注this article

    从该仓库中粘贴代码似乎可行

    body {
      margin: 0;
    }
    #c {
      width: 100vw;
      height: 100vh;
      display: block;
    }
    <canvas id="c"></canvas>
    <script type="module">
    import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
    import {EffectComposer} from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/examples/jsm/postprocessing/EffectComposer.js';
    import {RenderPass} from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/examples/jsm/postprocessing/RenderPass.js';
    import {ShaderPass} from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/examples/jsm/postprocessing/ShaderPass.js';
    import {GUI} from 'https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js';
    
    function main() {
      const canvas = document.querySelector('#c');
      const renderer = new THREE.WebGLRenderer({canvas});
    
      const fov = 75;
      const aspect = 2;  // the canvas default
      const near = 0.1;
      const far = 5;
      const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
      camera.position.z = 2;
    
      const scene = new THREE.Scene();
    
      {
        const color = 0xFFFFFF;
        const intensity = 2;
        const light = new THREE.DirectionalLight(color, intensity);
        light.position.set(-1, 2, 4);
        scene.add(light);
      }
    
      const boxWidth = 1;
      const boxHeight = 1;
      const boxDepth = 1;
      const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
    
      function makeInstance(geometry, color, x) {
        const material = new THREE.MeshPhongMaterial({color});
    
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);
    
        cube.position.x = x;
    
        return cube;
      }
    
      const cubes = [
        makeInstance(geometry, 0x44aa88,  0),
        makeInstance(geometry, 0x8844aa, -2),
        makeInstance(geometry, 0xaa8844,  2),
      ];
    
      const composer = new EffectComposer(renderer);
      composer.addPass(new RenderPass(scene, camera));
    
      // from:
      // https://github.com/mistic100/three.js-examples/blob/master/LICENSE
      const  kaleidoscopeShader = {
        uniforms: {
    
          "tDiffuse": { value: null },
          "sides":    { value: 6.0 },
          "angle":    { value: 0.0 }
    
        },
    
        vertexShader: `
    
          varying vec2 vUv;
    
          void main() {
    
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    
          }
    
        `,
    
        fragmentShader: `
    
          uniform sampler2D tDiffuse;
          uniform float sides;
          uniform float angle;
          
          varying vec2 vUv;
    
          void main() {
    
            vec2 p = vUv - 0.5;
            float r = length(p);
            float a = atan(p.y, p.x) + angle;
            float tau = 2. * 3.1416 ;
            a = mod(a, tau/sides);
            a = abs(a - tau/sides/2.) ;
            p = r * vec2(cos(a), sin(a));
            vec4 color = texture2D(tDiffuse, p + 0.5);
            gl_FragColor = color;
    
          }
        `
      };
    
    
      const kaleidoscopePass = new ShaderPass(kaleidoscopeShader);
      kaleidoscopePass.renderToScreen = true;
      composer.addPass(kaleidoscopePass);
    
      function resizeRendererToDisplaySize(renderer) {
        const canvas = renderer.domElement;
        const width = canvas.clientWidth;
        const height = canvas.clientHeight;
        const needResize = canvas.width !== width || canvas.height !== height;
        if (needResize) {
          renderer.setSize(width, height, false);
        }
        return needResize;
      }
    
      const gui = new GUI();
      gui.add(kaleidoscopePass.uniforms.sides, 'value', 0, 20).name('sides');
      gui.add(kaleidoscopePass.uniforms.angle, 'value', 0, 6.28, 0.01).name('angle');
      
      let then = 0;
      function render(now) {
        now *= 0.001;  // convert to seconds
        const deltaTime = now - then;
        then = now;
    
        if (resizeRendererToDisplaySize(renderer)) {
          const canvas = renderer.domElement;
          camera.aspect = canvas.clientWidth / canvas.clientHeight;
          camera.updateProjectionMatrix();
          composer.setSize(canvas.width, canvas.height);
        }
    
        cubes.forEach((cube, ndx) => {
          const speed = 1 + ndx * .1;
          const rot = now * speed;
          cube.rotation.x = rot;
          cube.rotation.y = rot;
        });
    
        composer.render(deltaTime);
    
        requestAnimationFrame(render);
      }
    
      requestAnimationFrame(render);
    }
    
    main();
    </script>

    【讨论】:

    • 感谢您的调查,感谢您的帮助!这就是我试图实现的着色器——我能够将你的示例集成到我的代码中,它几乎可以做到这一点,增加了能够添加更多边和调整角度的灵活性。我想避免使用 EffectComposer 并在我现有的着色器中添加所有代码,这更像是一个挑战而不是必需品(也猜测它更有效?),我将继续努力实现这一目标,看看是否有可能没有另一个渲染过程。
    • 当然,如果您想从头开始,您最终可能会复制 EffectComposer 中的大部分代码。您至少需要创建一个RenderTarget。将您的场景渲染到该目标,然后使用平面和万花筒着色器与另一个场景一起渲染,并从渲染目标传入纹理。
    • 我会试一试的,再次感谢!将您的答案标记为解决方案
    【解决方案2】:

    有一种纹理环绕模式可以进行镜像。

    texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping

    这有帮助吗?

    编辑:这是一个在两个轴上显示 mirroredrepeatwrapping 的示例:

    https://glitch.com/~three-mirroredrepeatwrapping

    【讨论】:

    • 不幸的是,当我像这样初始化它时它似乎没有任何效果 var texture = new THREE.DataTexture(data, width, height, THREE.RGBFormat, THREE.FloatType, THREE.DEFAULT_MAPPING,三.MirroredRepeatWrapping, 三.MirroredRepeatWrapping );
    • 是否有一些代码或小提琴展示了这种包装是如何工作的?我只在文档中看到它,但没有达到我的特定目的。
    • 谢谢,我会调查并尝试复制。我正在处理这种几何图形rawgit.com/nicoptere/FBO/master/noise.html,但我还没有找到立即解决的方法——也许它不适用于 ShaderMaterial 和 DataTexture。感谢您的帮助!
    • 据我所知,它应该适用于所有纹理类型。您必须读取 0 到 1 范围之外的纹理坐标才能获得翻转版本,但钳制/包装模式是 webgl 纹理 API 的一部分,因此它应该可以工作。
    猜你喜欢
    • 1970-01-01
    • 2014-04-02
    • 2019-07-31
    • 2013-02-23
    • 2012-07-24
    • 2014-05-25
    • 2020-09-17
    • 2016-11-25
    • 1970-01-01
    相关资源
    最近更新 更多