【问题标题】:Port shader with multiple buffers from shadertoy具有来自 shadertoy 的多个缓冲区的端口着色器
【发布时间】:2021-01-19 04:43:44
【问题描述】:

我试图通过在这个线程的帮助下从 shadertoy 移植一个着色器来了解如何在 three.js 中实现多个缓冲区。 https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/

我尝试为 js 重写它,但它没有编译。 这是代码: https://codepen.io/haangglide/pen/BaKXmLX

它基于这个: https://www.shadertoy.com/view/4sG3WV

我对使用缓冲区的理解是:

  1. 创建缓冲场景

bufferAscene = new THREE.Scene();

  1. 创建纹理
textureA = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
     minFilter: THREE.LinearFilter,
     magFilter: THREE.NearestFilter
    });
  1. 创建一个着色器材质,您可以在其中定义要传递给着色器的制服
    bufferA = new THREE.ShaderMaterial({
        uniforms: {
            iFrame: { value: 0 },
            iResolution: { value: resolution },
            iMouse: { value: mousePosition },
            iChannel0: { value: textureA.texture },
            iChannel1: { value: textureB.texture }
        },
        vertexShader: VERTEX_SHADER,
        fragmentShader: BUFFER_A_FRAG,
    });
  1. 创建一个 PlaneBufferGeometry 并从几何体和缓冲材料创建一个网格 new THREE.Mesh(planeA, bufferA)

  2. 将其添加到场景中 bufferAscene.add(new THREE.Mesh(planeA, bufferA));

在渲染中:

  1. 更新制服 bufferA.uniforms.iChannel0.value = textureA

虽然我不太了解交换。

如果有人可以帮助我编译应用程序,将不胜感激!

【问题讨论】:

    标签: three.js buffer shader


    【解决方案1】:

    这是一个将原始 TS 代码移植到 JavaScript 的实时示例。

    // Port from Shadertoy to THREE.js: https://www.shadertoy.com/view/4sG3WV
    
    const VERTEX_SHADER = `
        varying vec2 vUv;
        
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
    `;
    
    const BUFFER_A_FRAG = `
        uniform vec4 iMouse;
        uniform sampler2D iChannel0;
        uniform sampler2D iChannel1;
        uniform vec2 iResolution;
        uniform float iFrame;
        varying vec2 vUv;
        
        #define mousedata(a,b) texture2D( iChannel1, (0.5+vec2(a,b)) / iResolution.xy, -0.0 )
        #define backbuffer(uv) texture2D( iChannel0, uv ).xy
        
        float lineDist(vec2 p, vec2 start, vec2 end, float width) {
            vec2 dir = start - end;
            float lngth = length(dir);
            dir /= lngth;
            vec2 proj = max(0.0, min(lngth, dot((start - p), dir))) * dir;
            return length( (start - p) - proj ) - (width / 2.0);
        }
        
        void main() {
            vec2 uv = vUv;
            vec2 col = uv;
            if (iFrame > 2.) {
                col = texture2D(iChannel0,uv).xy;
                vec2 mouse = iMouse.xy/iResolution.xy;
                vec2 p_mouse = mousedata(2.,0.).xy;
                if (mousedata(4.,0.).x > 0.) {
                    col = backbuffer(uv+((p_mouse-mouse)*clamp(1.-(lineDist(uv,mouse,p_mouse,0.)*20.),0.,1.)*.7));
                }
            }
            gl_FragColor = vec4(col,0.0,1.0);
        }
    
    `;
    
    const BUFFER_B_FRAG = `
        uniform vec4 iMouse;
        uniform sampler2D iChannel0;
        uniform vec3 iResolution;
        varying vec2 vUv;
        
        bool pixelAt(vec2 coord, float a, float b) {
            return (floor(coord.x) == a && floor(coord.y) == b);
        }
        
        vec4 backbuffer(float a,float b) {
          return texture2D( iChannel0, (0.5+vec2(a,b)) / iResolution.xy, -100.0 );
        }
        
        void main( ) {
        
            vec2 uv = vUv;// / iResolution.xy;
            vec4 color = texture2D(iChannel0,uv);
        
            if (pixelAt(gl_FragCoord.xy,0.,0.)) { //Surface position
                gl_FragColor = vec4(backbuffer(0.,0.).rg+(backbuffer(4.,0.).r*(backbuffer(2.,0.).rg-backbuffer(1.,0.).rg)),0.,1.);
            } else if (pixelAt(gl_FragCoord.xy,1.,0.)) { //New mouse position
                gl_FragColor = vec4(iMouse.xy/iResolution.xy,0.,1.);
            } else if (pixelAt(gl_FragCoord.xy,2.,0.)) { //Old mouse position
                gl_FragColor = vec4(backbuffer(1.,0.).rg,0.,1.);
            } else if (pixelAt(gl_FragCoord.xy,3.,0.)) { //New mouse holded
                gl_FragColor = vec4(clamp(iMouse.z,0.,1.),0.,0.,1.);
            } else if (pixelAt(gl_FragCoord.xy,4.,0.)) { //Old mouse holded
                gl_FragColor = vec4(backbuffer(3.,0.).r,0.,0.,1.);
            } else {
                gl_FragColor = vec4(0.,0.,0.,1.);
            }
        
        }
    `;
    
    const BUFFER_FINAL_FRAG = `
        uniform sampler2D iChannel0;
        uniform sampler2D iChannel1;
        varying vec2 vUv;
        
        void main() {
            vec2 uv = vUv;
            vec2 a = texture2D(iChannel1,uv).xy;
            gl_FragColor = vec4(texture2D(iChannel0,a).rgb,1.0);
        }
    `;
    
    class App {
    
      constructor() {
    
        this.width = 1024;
        this.height = 512;
    
        this.renderer = new THREE.WebGLRenderer();
        this.loader = new THREE.TextureLoader();
        this.mousePosition = new THREE.Vector4();
        this.orthoCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
        this.counter = 0;
    
        this.renderer.setSize(this.width, this.height);
        document.body.appendChild(this.renderer.domElement);
    
        this.renderer.domElement.addEventListener('mousedown', () => {
          this.mousePosition.setZ(1);
          this.counter = 0;
        });
    
        this.renderer.domElement.addEventListener('mouseup', () => {
          this.mousePosition.setZ(0);
        });
    
        this.renderer.domElement.addEventListener('mousemove', event => {
          this.mousePosition.setX(event.clientX);
          this.mousePosition.setY(this.height - event.clientY);
        });
    
        this.targetA = new BufferManager(this.renderer, {
          width: this.width,
          height: this.height
        });
        this.targetB = new BufferManager(this.renderer, {
          width: this.width,
          height: this.height
        });
        this.targetC = new BufferManager(this.renderer, {
          width: this.width,
          height: this.height
        });
    
      }
    
    
      start() {
    
        const resolution = new THREE.Vector3(this.width, this.height, window.devicePixelRatio);
        const channel0 = this.loader.load('https://res.cloudinary.com/di4jisedp/image/upload/v1523722553/wallpaper.jpg');
        this.loader.setCrossOrigin('');
    
        this.bufferA = new BufferShader(BUFFER_A_FRAG, {
          iFrame: {
            value: 0
          },
          iResolution: {
            value: resolution
          },
          iMouse: {
            value: this.mousePosition
          },
          iChannel0: {
            value: null
          },
          iChannel1: {
            value: null
          }
        });
    
        this.bufferB = new BufferShader(BUFFER_B_FRAG, {
          iFrame: {
            value: 0
          },
          iResolution: {
            value: resolution
          },
          iMouse: {
            value: this.mousePosition
          },
          iChannel0: {
            value: null
          }
        });
    
        this.bufferImage = new BufferShader(BUFFER_FINAL_FRAG, {
          iResolution: {
            value: resolution
          },
          iMouse: {
            value: this.mousePosition
          },
          iChannel0: {
            value: channel0
          },
          iChannel1: {
            value: null
          }
        });
    
        this.animate();
    
      }
    
      animate() {
        requestAnimationFrame(() => {
    
          this.bufferA.uniforms['iFrame'].value = this.counter++;
    
          this.bufferA.uniforms['iChannel0'].value = this.targetA.readBuffer.texture;
          this.bufferA.uniforms['iChannel1'].value = this.targetB.readBuffer.texture;
          this.targetA.render(this.bufferA.scene, this.orthoCamera);
    
          this.bufferB.uniforms['iChannel0'].value = this.targetB.readBuffer.texture;
          this.targetB.render(this.bufferB.scene, this.orthoCamera);
    
          this.bufferImage.uniforms['iChannel1'].value = this.targetA.readBuffer.texture;
          this.targetC.render(this.bufferImage.scene, this.orthoCamera, true);
    
          this.animate();
    
        });
    
      }
    
    }
    
    class BufferShader {
    
      constructor(fragmentShader, uniforms = {}) {
    
        this.uniforms = uniforms;
        this.material = new THREE.ShaderMaterial({
          fragmentShader: fragmentShader,
          vertexShader: VERTEX_SHADER,
          uniforms: uniforms
        });
        this.scene = new THREE.Scene();
        this.scene.add(
          new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), this.material)
        );
      }
    
    }
    
    class BufferManager {
    
    
      constructor(renderer, size) {
    
        this.renderer = renderer;
    
        this.readBuffer = new THREE.WebGLRenderTarget(size.width, size.height, {
          minFilter: THREE.LinearFilter,
          magFilter: THREE.LinearFilter,
          format: THREE.RGBAFormat,
          type: THREE.FloatType,
          stencilBuffer: false
        });
    
        this.writeBuffer = this.readBuffer.clone();
    
      }
    
      swap() {
        const temp = this.readBuffer;
        this.readBuffer = this.writeBuffer;
        this.writeBuffer = temp;
      }
    
      render(scene, camera, toScreen = false) {
        if (toScreen) {
          this.renderer.render(scene, camera);
        } else {
          this.renderer.setRenderTarget(this.writeBuffer);
          this.renderer.clear();
          this.renderer.render(scene, camera)
          this.renderer.setRenderTarget(null);
        }
        this.swap();
      }
    
    }
    
    document.addEventListener('DOMContentLoaded', () => {
      (new App()).start();
    });
    body {
          margin: 0;
    }
    
    canvas {
        display: block;
    }
    <script src="https://cdn.jsdelivr.net/npm/three@0.121.1/build/three.js"></script>

    【讨论】:

    • 谢谢!交换函数究竟是做什么的?
    • BufferManager 类管理读写缓冲区。 THREE.EffectComposer 也使用了这种方法的思想。这意味着您可以从读取缓冲区读取上一次渲染传递的结果,并将传递的结果生成到写入缓冲区。通过执行交换,您只需交换两个缓冲区的角色。
    猜你喜欢
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 2020-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多