Multipass ShaderToy有自己的输入

问题描述 投票:1回答:1

我一直在尝试将着色器从ShaderToy移植到常规WebGL,然后打了一个墙。我设法使多通道着色器工作,但不是那些将自己作为输入通道的工具。我在这里做了一个测试例子:https://www.shadertoy.com/view/WsfSzj。着色器有两个通道,它们都有缓冲区A作为输入。据我了解,当缓冲区本身作为输入时,它使用前一个时间步的自身输出。

所以我尝试的是用结构制作两个rendertargets:

class RenderTarget { 
   tex: Texture 
   fbo: FrameBuffer 
}

使用渲染循环:

gl.useProgram(prog1) 
gl.bindFramebuffer(gl.TEXTURE_2D, rt1.fbo) 
gl.bindTexture(gl.TEXTURE_2D, rt2.texture); 
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

gl.useProgram(prog2) 
gl.bindFramebuffer(gl.TEXTURE_2D, null) 
gl.bindTexture(gl.TEXTURE_2D, rt1.texture); 
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

swap(rt1, rt2)

然而,这只会呈现底部条而不是粒子,我无法弄清楚原因。我在这里有一个缩小的例子:https://jsfiddle.net/f7jv8s6y/7/

const fsSource = document.getElementById("shader-fs").text;

class RenderTarget {
  constructor(gl, width, height) {
    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    this.level = 0;
    const internalFormat = gl.RGBA;
    const border = 0;
    const format = gl.RGBA;
    const type = gl.UNSIGNED_BYTE;
    const data = new Uint8Array(width * height * 4);

    gl.texImage2D(gl.TEXTURE_2D, this.level, internalFormat, width, height, border, format, type, data);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    this.fbo = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
  }
}

function loadShader(gl, type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    const info = String(gl.getShaderInfoLog(shader));
    console.log(info, source);
    gl.deleteShader(shader);
    return info;
  }
  return shader;
}

function initProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
  const shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);
  return shaderProgram;
}

function initQuad(gl) {
  const positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  const positions = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  return positionBuffer;
}


const vs = `#version 300 es
in vec2 aVertexPosition;
out vec2 uv;
void main() {
  uv = aVertexPosition;
  gl_Position =  vec4(aVertexPosition, 0., 1.0);
}`;


const fs = `#version 300 es
precision highp float;
uniform sampler2D iChannel0;
out vec4 fColor;
void main() {
vec2 p = gl_FragCoord.xy / vec2(420., 320.);
  fColor =  texture(iChannel0, p);
}`;

function init() {
  // Set up canvas and webgl2 context
  const width = 420;
  const height = 320;
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const gl = canvas.getContext("webgl2");
  document.body.appendChild(canvas);

  // Compile shaders and set up two render targets
  const prog1 = initProgram(gl, vs, fsSource);
  const prog2 = initProgram(gl, vs, fs);
  const rt1 = new RenderTarget(gl, width, height);
  const rt2 = new RenderTarget(gl, width, height);
  const rts = [rt1, rt2];

  // Bind vertex quad
  gl.useProgram(prog1);
  const quadPos = gl.getAttribLocation(prog1, 'aVertexPosition');
  const quad = initQuad(gl);
  gl.bindBuffer(gl.ARRAY_BUFFER, quad);
  gl.vertexAttribPointer(quadPos, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(quadPos);
  gl.viewport(0, 0, width, height);
  // Set up needed uniforms
  const iTimePos = gl.getUniformLocation(prog1, "iTime");
  const iTimeDeltaPos = gl.getUniformLocation(prog1, "iTimeDelta");
  const iFramePos = gl.getUniformLocation(prog1, "iFrame");
  const iResolutionPos = gl.getUniformLocation(prog1, "iResolution");
  const iChannelResolutionPos = gl.getUniformLocation(prog1, "iChannelResolution");
  const resos = [width, height, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
  gl.uniform2f(iResolutionPos, width, height);
  gl.uniform3fv(iChannelResolutionPos, new Float32Array(resos));
  const prog1Channel0Pos = gl.getUniformLocation(prog1, "iChannel0");
  gl.useProgram(prog2);
  const prog2Channel0Pos = gl.getUniformLocation(prog2, "iChannel0");

  let frame = 0;
  let time = 0;
  let lastTime = 0;

  let setTexture = function(gl, tex, location, spot) {
    gl.activeTexture(gl.TEXTURE0 + spot);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.uniform1i(location, spot);
  }

  let animate = function() {
    time = performance.now() / 1000;
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // FIRST RENDER PASS
    gl.useProgram(prog1);
    gl.uniform1f(iTimePos, time);
    gl.uniform1i(iFramePos, frame);
    gl.uniform1f(iTimeDeltaPos, time - lastTime);
    gl.bindFramebuffer(gl.FRAMEBUFFER, rts[0].fbo);
    gl.bindTexture(gl.TEXTURE_2D, rts[1].texture);
    //setTexture(gl, rts[1].texture, prog1Channel0Pos, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    //SECOND RENDER PASS
    gl.useProgram(prog2);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.bindTexture(gl.TEXTURE_2D, rts[0].texture);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    //setTexture(gl, rts[0].texture, prog2Channel0Pos, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    rts.reverse();
    lastTime = time;
    frame++;

    requestAnimationFrame(animate);
  }
  lastTime = performance.now();
  animate();
}


init();
<script type="x-shader/x-fragment" id="shader-fs" src="util/fs">#version 300 es
#ifdef GL_ES
precision highp float;
precision highp int;
precision mediump sampler3D;
#endif
uniform vec3 iChannelResolution[4];
uniform float iTime;
uniform float iTimeDelta;
uniform float timeDelta;
uniform vec2 iResolution; 
uniform vec4 iMouse;
uniform int iFrame;
in vec2 uv;
out vec4 fColor;

uniform sampler2D iChannel0;
// The MIT License
// Copyright © 2018 Ian Reichert-Watts
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// SHARED PARAMS (Must be same as Image :/)
const int NUM_PARTICLES = 64;
const float INTERACT_DATA_INDEX = float(NUM_PARTICLES)+1.0;
const float KINETIC_MOUSE_INDEX = INTERACT_DATA_INDEX+1.0;

// SHARED FUNCTIONS (Must be same as Image :/)
vec4 loadData( in float index ) 
{ 
    return texture( iChannel0, vec2((index+0.5)/iChannelResolution[0].x,0.0), -100.0 ); 
}

float floorHeight( in vec3 p )
{
    return (sin(p.z*0.00042)*0.2)+(sin(p.z*0.008)*0.64) + (sin(p.x*0.42+sin(p.z*0.000042)*420.0))*0.42-1.0;
}

// PARAMS
const float PARTICLE_LIFETIME_MIN = 0.02;
const float PARTICLE_LIFETIME_MAX = 4.2;
const float FALL_SPEED = 42.0;
const float JITTER_SPEED = 300.0;
const vec3 WIND_DIR = vec3(0.0,0.0,-1.0);
const float WIND_INTENSITY = 4.2;

// CONST
const float PI = 3.14159;
const float TAU = PI * 2.0;

float randFloat( in float n )
{
    return fract( sin( n*64.19 )*420.82 );
}
vec2 randVec2( in vec2 n )
{
    return vec2(randFloat( n.x*12.95+n.y*43.72 ),randFloat( n.x*16.21+n.y*90.23 )); 
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{    
    if ( fragCoord.y > iResolution.y-2.0 )
    {
        // Discard top pixels to avoid persistent data getting included in blur
        discard;
    }
    else if ( fragCoord.y < 2.0 )
    {
        if ( fragCoord.y >= 1.0 || fragCoord.x > float(NUM_PARTICLES+4) )
        {
            discard;
        }
        // Store persistent data in bottom pixel row
        if ( fragCoord.x < float(NUM_PARTICLES) )
        {
            vec4 particle;
            float pidx = floor(fragCoord.x);

            if ( iFrame == 0 )
            {
                float padding = 0.01;
                float particleStep = (1.0-(padding*2.0))/float(NUM_PARTICLES);
                particle = vec4(0.0);
                float r1 = randFloat(pidx);
                particle.xy = vec2(padding+(particleStep*pidx), 1.0+(1.0*r1));
                particle.xy *= iResolution.xy;
                particle.a = r1*(PARTICLE_LIFETIME_MAX-PARTICLE_LIFETIME_MIN);
            }
            else
            {   
               	vec4 interactData = loadData(INTERACT_DATA_INDEX);
                
                // Tick particles
        		particle = loadData(pidx);
                vec2 puv = particle.xy / iResolution.x;
                vec4 pbuf = texture(iChannel0, puv);
                
                // Camera must be the same as Image :/
                float rotYaw = -(interactData.x/iResolution.x)*TAU;
                float rotPitch = (interactData.y/iResolution.y)*PI;
                vec3 rayOrigin = vec3(0.0, 0.1, iTime*80.0);
                float floorY = floorHeight(rayOrigin);
                rayOrigin.y = floorY*0.9 + 0.2;

                vec3 forward = normalize( vec3(sin(rotYaw), rotPitch, cos(rotYaw)) );
                vec3 wup = normalize(vec3((floorY-floorHeight(rayOrigin+vec3(2.0,0.0,0.0)))*-0.2,1.0,0.0));
                vec3 right = normalize( cross( forward, wup ) );
                vec3 up = normalize( cross( right, forward ) );
                mat3 camMat = mat3(right, up, forward);

                vec3 surfforward = normalize( vec3(sin(rayOrigin.z*0.01)*0.042, ((floorY-floorHeight(rayOrigin+vec3(0.0,0.0,-20.0)))*0.2)+0.12, 1.0) );
                vec3 wright = vec3(1.0,0.0,0.0);
                mat3 surfMat = mat3(wright, up, surfforward); 

                vec2 centeredCoord = puv-vec2(0.5);
                vec3 rayDir = normalize( surfMat*normalize( camMat*normalize( vec3(centeredCoord, 1.0) ) ) );
                vec3 rayRight = normalize( cross( rayDir, up ) );
                vec3 rayUp = normalize( cross( rayRight, rayDir ) );

                // Wind
                vec2 windShield = (puv-vec2(0.5, 0.0))*2.0;
                float speedScale = 0.0015*(0.1+1.9*(sin(PI*0.5*pow( particle.z/particle.a, 2.0 ))))*iResolution.y;
                particle.x += (windShield.x+WIND_INTENSITY*dot(rayRight, WIND_DIR))*FALL_SPEED*speedScale*iTimeDelta;
                particle.y += (windShield.y+WIND_INTENSITY*dot(rayUp, WIND_DIR))*FALL_SPEED*speedScale*iTimeDelta;

                // Jitter
                particle.xy += 0.001*(randVec2( particle.xy+iTime )-vec2(0.5))*iResolution.y*JITTER_SPEED*iTimeDelta;

                // Age
                // Don't age as much when traveling over existing particle trails
                particle.z += (1.0-pbuf.b)*iTimeDelta;

                // Die of old age. Reset
                if ( particle.z > particle.a )
                {
                    float seedX = particle.x*25.36+particle.y*42.92;
                    float seedY = particle.x*16.78+particle.y*93.42;
                    particle = vec4(0.0);
                    particle.x = randFloat( seedX )*iResolution.x;
                    particle.y = randFloat( seedY )*iResolution.y;
                    particle.a = PARTICLE_LIFETIME_MIN+randFloat(pidx)*(PARTICLE_LIFETIME_MAX-PARTICLE_LIFETIME_MIN);
                }
            }
            fragColor = particle;
        }
		else
        {
            float dataIndex = floor(fragCoord.x);
            vec4 interactData = loadData(INTERACT_DATA_INDEX);
            vec4 kineticMouse = loadData(KINETIC_MOUSE_INDEX);
            
            if ( iMouse.z > 0.0 )
            {
            	vec2 mouseDelta = iMouse.xy-kineticMouse.xy;
                if ( length(iMouse.xy-iMouse.zw) < 4.0 )
                {
                    mouseDelta = vec2(0.0);
                }
                interactData.xy += mouseDelta;
                interactData.y = clamp( interactData.y, -iResolution.y, iResolution.y );
                kineticMouse = vec4(iMouse.xy, mouseDelta);
            }
            else
            {
                kineticMouse.zw *= 0.9;
                interactData.xy += kineticMouse.zw;
                interactData.y = clamp( interactData.y, -iResolution.y, iResolution.y );
                kineticMouse.xy = iMouse.xy;
            }
            fragColor = (dataIndex == KINETIC_MOUSE_INDEX) ? kineticMouse : interactData;
        }
    }
    else
    {
        // Draw Particles
        vec2 blurUV = fract( (fragCoord.xy + (fract( float(iFrame)*0.5 )*2.0-0.5)) / iResolution.xy );
        vec2 uv = fragCoord.xy / iResolution.xy;
        fragColor = texture( iChannel0, uv );
        vec4 prevColor = fragColor;

        if ( fragColor.a < 1.0 )
        {
            fragColor = texture( iChannel0, blurUV );
        }
        fragColor.b *= 0.996;

        for ( int i=0; i<NUM_PARTICLES; i++ )
        {
    		vec4 particle = loadData(float(i));
            vec2 delta = fragCoord.xy-particle.xy;
            float dist = length(delta);
            float radius = 0.002*(0.5+2.0*particle.a+abs(sin(1.0*iTime+float(i))))*iResolution.y;
            radius += 4.0*randFloat( particle.x*35.26+particle.y*93.12 )*pow((particle.z/particle.a), 12.0);
            if ( dist < radius )
            {
                // normal
                vec2 dir = delta/dist;
                fragColor.r = dot(dir, vec2(1.0,0.0))*0.5+0.5;
                fragColor.g = dot(dir, vec2(0.0,1.0))*0.5+0.5;
                // height
                float height = sin( dist/radius*PI*0.5 );
                height = pow( height, 8.0 );
                height = 1.0-height;
                fragColor.b = max( height, prevColor.b );
                // age
                fragColor.a = 0.0;
            }
        }
        fragColor.a += 0.1*iTimeDelta;
    }
}void main() {
  vec4 color = vec4(0.0,0.0,0.0,1.0);
  mainImage(color, gl_FragCoord.xy);
  color.w = 1.0;
  fColor = color;
}
</script>

有没有人有指向出错的指针?

javascript webgl shader fragment-shader webgl2
1个回答
2
投票

问题是你需要使用FLOATHALF_FLOAT纹理与你的帧缓冲区。

请注意,能够渲染到FLOATHALF_FLOAT是WebGL2的可选功能,因此您必须检查并启用EXT_color_buffer_floatEXT_color_buffer_half_float

过滤FLOATHALF_FLOAT纹理也是一项可选功能。我不知道Shadertoy是否使用NEARESTLINEAR。我把东西放到下面的NEAREST。如果你想使用LINEAR,你还需要检查并启用OES_texture_float_linearOES_texture_half_float_linear

如果你很好奇我是怎么想出来的......

首先,我通过添加验证第二遍正在工作

 fColor = vec4(p, 0, 1);

到第二个着色器底部。

接下来我尝试了类似的第一个着色器添加器

 fragColor = vec4(fragCoord / iResolution, 0, 1);
 return;

接下来我看着着色器。我看到它分成了3个部分。一部分跳过顶线所以我把fragColor = vec4(1,0,0,1)放在那里作为一个完整性检查,以验证它是按照我的预期做的

然后下一部分做了我跳过的粒子模拟,除了足够理解它只是使用底行存储。

最后一部分做了模糊,所以我再次放入fragColor = vec4(0,1,0,1)只是为了验证该区域变为绿色。它做了。

所以,鉴于它应该模糊,我添加了一些代码来手动设置纹理

gl.bindFramebuffer(gl.FRAMEBUFFER, rts[1].fbo);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(100, 100, 10, 10);
gl.clearColor(.2, .5, .7, .9);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);

我在渲染循环中尝试了这一点,然后仅在初始化时尝试。当我看到小矩形完全没有受到影响时,它会想到可能是什么问题,而且当我想到问题可能是8位渲染目标时。我查看了shadowrtoy网站的文档,但文档说绝对没有(在shadertoy.com上非常糟糕的文档)。 This source链接来自shadertoy.com。这是一个iOS应用程序,而不是网站,所以我searched for glTexImage2D看看他们是否使用浮点纹理。答案是'是'所以我试了一下,至少得到了一些似乎可以解决问题的结果。

关于代码的一些建议

  • 如果您没有在纹理中放置任何数据,则没有理由将数组传递给gl.texImage2D。只需通过null
  • 没有理由使用带有gl.uniform的类型数组,因此这段代码 const resos = [width, height, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; gl.uniform3fv(iChannelResolutionPos, new Float32Array(resos)); 可能只是 const resos = [width, height, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; gl.uniform3fv(iChannelResolutionPos, resos);
  • gl.getShaderInfoLog已经返回一个字符串 const info = String(gl.getShaderInfoLog(shader)); 可能只是 const info = gl.getShaderInfoLog(shader);
  • initProgram代码不检查链接错误。 链接错误可能由于任何原因发生。最常见的是错误匹配的变化但是规范实际上说只要着色器编译失败就连接失败,着色器编译总能成功。 gl.linkProgram(prg); const success = gl.getProgramParameter(gl.LINK_STATUS); if (!success) { console.log(gl.getProgramInfoLog(prg); }

const fsSource = document.getElementById("shader-fs").text;

class RenderTarget {
  constructor(gl, width, height) {
    this.texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    this.level = 0;
    const internalFormat = gl.RGBA32F;
    const border = 0;
    const format = gl.RGBA;
    const type = gl.FLOAT;
    const data = null;

    gl.texImage2D(gl.TEXTURE_2D, this.level, internalFormat, width, height, border, format, type, data);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    this.fbo = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
  }
}

function loadShader(gl, type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    const info = String(gl.getShaderInfoLog(shader));
    console.log(info, source);
    gl.deleteShader(shader);
    return info;
  }
  return shader;
}

function initProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
  const shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);
  return shaderProgram;
}

function initQuad(gl) {
  const positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  const positions = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  return positionBuffer;
}


const vs = `#version 300 es
in vec2 aVertexPosition;
out vec2 uv;
void main() {
  uv = aVertexPosition;
  gl_Position =  vec4(aVertexPosition, 0., 1.0);
}`;


const fs = `#version 300 es
precision highp float;
uniform sampler2D iChannel0;
out vec4 fColor;
void main() {
vec2 p = gl_FragCoord.xy / vec2(420., 320.);
  fColor =  texture(iChannel0, p);
}`;

function init() {
  // Set up canvas and webgl2 context
  const width = 420;
  const height = 320;
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const gl = canvas.getContext("webgl2");
  document.body.appendChild(canvas);
  const ext = gl.getExtension('EXT_color_buffer_float');
  if (!ext) {
    return alert('need EXT_color_buffer_float');
  }

  // Compile shaders and set up two render targets
  const prog1 = initProgram(gl, vs, fsSource);
  const prog2 = initProgram(gl, vs, fs);
  const rt1 = new RenderTarget(gl, width, height);
  const rt2 = new RenderTarget(gl, width, height);
  const rts = [rt1, rt2];

  // Bind vertex quad
  gl.useProgram(prog1);
  const quadPos = gl.getAttribLocation(prog1, 'aVertexPosition');
  const quad = initQuad(gl);
  gl.bindBuffer(gl.ARRAY_BUFFER, quad);
  gl.vertexAttribPointer(quadPos, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(quadPos);
  gl.viewport(0, 0, width, height);
  // Set up needed uniforms
  const iTimePos = gl.getUniformLocation(prog1, "iTime");
  const iTimeDeltaPos = gl.getUniformLocation(prog1, "iTimeDelta");
  const iFramePos = gl.getUniformLocation(prog1, "iFrame");
  const iResolutionPos = gl.getUniformLocation(prog1, "iResolution");
  const iChannelResolutionPos = gl.getUniformLocation(prog1, "iChannelResolution");
  const resos = [width, height, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
  gl.uniform2f(iResolutionPos, width, height);
  gl.uniform3fv(iChannelResolutionPos, new Float32Array(resos));
  const prog1Channel0Pos = gl.getUniformLocation(prog1, "iChannel0");
  gl.useProgram(prog2);
  const prog2Channel0Pos = gl.getUniformLocation(prog2, "iChannel0");

  let frame = 0;
  let time = 0;
  let lastTime = 0;

  let setTexture = function(gl, tex, location, spot) {
    gl.activeTexture(gl.TEXTURE0 + spot);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.uniform1i(location, spot);
  }

  let animate = function() {
    time = performance.now() / 1000;
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // FIRST RENDER PASS
    gl.useProgram(prog1);
    gl.uniform1f(iTimePos, time);
    gl.uniform1i(iFramePos, frame);
    gl.uniform1f(iTimeDeltaPos, time - lastTime);
    gl.bindFramebuffer(gl.FRAMEBUFFER, rts[0].fbo);
    gl.bindTexture(gl.TEXTURE_2D, rts[1].texture);
    //setTexture(gl, rts[1].texture, prog1Channel0Pos, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    //SECOND RENDER PASS
    gl.useProgram(prog2);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.bindTexture(gl.TEXTURE_2D, rts[0].texture);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    //setTexture(gl, rts[0].texture, prog2Channel0Pos, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    rts.reverse();
    lastTime = time;
    frame++;

    requestAnimationFrame(animate);
  }
  lastTime = performance.now();
  animate();
}


init();
<script type="x-shader/x-fragment" id="shader-fs" src="util/fs">#version 300 es
#ifdef GL_ES
precision highp float;
precision highp int;
precision mediump sampler3D;
#endif
uniform vec3 iChannelResolution[4];
uniform float iTime;
uniform float iTimeDelta;
uniform float timeDelta;
uniform vec2 iResolution; 
uniform vec4 iMouse;
uniform int iFrame;
in vec2 uv;
out vec4 fColor;

uniform sampler2D iChannel0;
// The MIT License
// Copyright © 2018 Ian Reichert-Watts
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// SHARED PARAMS (Must be same as Image :/)
const int NUM_PARTICLES = 64;
const float INTERACT_DATA_INDEX = float(NUM_PARTICLES)+1.0;
const float KINETIC_MOUSE_INDEX = INTERACT_DATA_INDEX+1.0;

// SHARED FUNCTIONS (Must be same as Image :/)
vec4 loadData( in float index ) 
{ 
    return texture( iChannel0, vec2((index+0.5)/iChannelResolution[0].x,0.0), -100.0 ); 
}

float floorHeight( in vec3 p )
{
    return (sin(p.z*0.00042)*0.2)+(sin(p.z*0.008)*0.64) + (sin(p.x*0.42+sin(p.z*0.000042)*420.0))*0.42-1.0;
}

// PARAMS
const float PARTICLE_LIFETIME_MIN = 0.02;
const float PARTICLE_LIFETIME_MAX = 4.2;
const float FALL_SPEED = 42.0;
const float JITTER_SPEED = 300.0;
const vec3 WIND_DIR = vec3(0.0,0.0,-1.0);
const float WIND_INTENSITY = 4.2;

// CONST
const float PI = 3.14159;
const float TAU = PI * 2.0;

float randFloat( in float n )
{
    return fract( sin( n*64.19 )*420.82 );
}
vec2 randVec2( in vec2 n )
{
    return vec2(randFloat( n.x*12.95+n.y*43.72 ),randFloat( n.x*16.21+n.y*90.23 )); 
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{    
    if ( fragCoord.y > iResolution.y-2.0 )
    {
        // Discard top pixels to avoid persistent data getting included in blur
        discard;
    }
    else if ( fragCoord.y < 2.0 )
    {
        if ( fragCoord.y >= 1.0 || fragCoord.x > float(NUM_PARTICLES+4) )
        {
            discard;
        }
        // Store persistent data in bottom pixel row
        if ( fragCoord.x < float(NUM_PARTICLES) )
        {
            vec4 particle;
            float pidx = floor(fragCoord.x);

            if ( iFrame == 0 )
            {
                float padding = 0.01;
                float particleStep = (1.0-(padding*2.0))/float(NUM_PARTICLES);
                particle = vec4(0.0);
                float r1 = randFloat(pidx);
                particle.xy = vec2(padding+(particleStep*pidx), 1.0+(1.0*r1));
                particle.xy *= iResolution.xy;
                particle.a = r1*(PARTICLE_LIFETIME_MAX-PARTICLE_LIFETIME_MIN);
            }
            else
            {   
               	vec4 interactData = loadData(INTERACT_DATA_INDEX);
                
                // Tick particles
        		particle = loadData(pidx);
                vec2 puv = particle.xy / iResolution.x;
                vec4 pbuf = texture(iChannel0, puv);
                
                // Camera must be the same as Image :/
                float rotYaw = -(interactData.x/iResolution.x)*TAU;
                float rotPitch = (interactData.y/iResolution.y)*PI;
                vec3 rayOrigin = vec3(0.0, 0.1, iTime*80.0);
                float floorY = floorHeight(rayOrigin);
                rayOrigin.y = floorY*0.9 + 0.2;

                vec3 forward = normalize( vec3(sin(rotYaw), rotPitch, cos(rotYaw)) );
                vec3 wup = normalize(vec3((floorY-floorHeight(rayOrigin+vec3(2.0,0.0,0.0)))*-0.2,1.0,0.0));
                vec3 right = normalize( cross( forward, wup ) );
                vec3 up = normalize( cross( right, forward ) );
                mat3 camMat = mat3(right, up, forward);

                vec3 surfforward = normalize( vec3(sin(rayOrigin.z*0.01)*0.042, ((floorY-floorHeight(rayOrigin+vec3(0.0,0.0,-20.0)))*0.2)+0.12, 1.0) );
                vec3 wright = vec3(1.0,0.0,0.0);
                mat3 surfMat = mat3(wright, up, surfforward); 

                vec2 centeredCoord = puv-vec2(0.5);
                vec3 rayDir = normalize( surfMat*normalize( camMat*normalize( vec3(centeredCoord, 1.0) ) ) );
                vec3 rayRight = normalize( cross( rayDir, up ) );
                vec3 rayUp = normalize( cross( rayRight, rayDir ) );

                // Wind
                vec2 windShield = (puv-vec2(0.5, 0.0))*2.0;
                float speedScale = 0.0015*(0.1+1.9*(sin(PI*0.5*pow( particle.z/particle.a, 2.0 ))))*iResolution.y;
                particle.x += (windShield.x+WIND_INTENSITY*dot(rayRight, WIND_DIR))*FALL_SPEED*speedScale*iTimeDelta;
                particle.y += (windShield.y+WIND_INTENSITY*dot(rayUp, WIND_DIR))*FALL_SPEED*speedScale*iTimeDelta;

                // Jitter
                particle.xy += 0.001*(randVec2( particle.xy+iTime )-vec2(0.5))*iResolution.y*JITTER_SPEED*iTimeDelta;

                // Age
                // Don't age as much when traveling over existing particle trails
                particle.z += (1.0-pbuf.b)*iTimeDelta;

                // Die of old age. Reset
                if ( particle.z > particle.a )
                {
                    float seedX = particle.x*25.36+particle.y*42.92;
                    float seedY = particle.x*16.78+particle.y*93.42;
                    particle = vec4(0.0);
                    particle.x = randFloat( seedX )*iResolution.x;
                    particle.y = randFloat( seedY )*iResolution.y;
                    particle.a = PARTICLE_LIFETIME_MIN+randFloat(pidx)*(PARTICLE_LIFETIME_MAX-PARTICLE_LIFETIME_MIN);
                }
            }
            fragColor = particle;
        }
		else
        {
            float dataIndex = floor(fragCoord.x);
            vec4 interactData = loadData(INTERACT_DATA_INDEX);
            vec4 kineticMouse = loadData(KINETIC_MOUSE_INDEX);
            
            if ( iMouse.z > 0.0 )
            {
            	vec2 mouseDelta = iMouse.xy-kineticMouse.xy;
                if ( length(iMouse.xy-iMouse.zw) < 4.0 )
                {
                    mouseDelta = vec2(0.0);
                }
                interactData.xy += mouseDelta;
                interactData.y = clamp( interactData.y, -iResolution.y, iResolution.y );
                kineticMouse = vec4(iMouse.xy, mouseDelta);
            }
            else
            {
                kineticMouse.zw *= 0.9;
                interactData.xy += kineticMouse.zw;
                interactData.y = clamp( interactData.y, -iResolution.y, iResolution.y );
                kineticMouse.xy = iMouse.xy;
            }
            fragColor = (dataIndex == KINETIC_MOUSE_INDEX) ? kineticMouse : interactData;
        }
    }
    else
    {
        // Draw Particles
        vec2 blurUV = fract( (fragCoord.xy + (fract( float(iFrame)*0.5 )*2.0-0.5)) / iResolution.xy );
        vec2 uv = fragCoord.xy / iResolution.xy;
        fragColor = texture( iChannel0, uv );
        vec4 prevColor = fragColor;

        if ( fragColor.a < 1.0 )
        {
            fragColor = texture( iChannel0, blurUV );
        }
        fragColor.b *= 0.996;

        for ( int i=0; i<NUM_PARTICLES; i++ )
        {
    		vec4 particle = loadData(float(i));
            vec2 delta = fragCoord.xy-particle.xy;
            float dist = length(delta);
            float radius = 0.002*(0.5+2.0*particle.a+abs(sin(1.0*iTime+float(i))))*iResolution.y;
            radius += 4.0*randFloat( particle.x*35.26+particle.y*93.12 )*pow((particle.z/particle.a), 12.0);
            if ( dist < radius )
            {
                // normal
                vec2 dir = delta/dist;
                fragColor.r = dot(dir, vec2(1.0,0.0))*0.5+0.5;
                fragColor.g = dot(dir, vec2(0.0,1.0))*0.5+0.5;
                // height
                float height = sin( dist/radius*PI*0.5 );
                height = pow( height, 8.0 );
                height = 1.0-height;
                fragColor.b = max( height, prevColor.b );
                // age
                fragColor.a = 0.0;
            }
        }
        fragColor.a += 0.1*iTimeDelta;
    }
}void main() {
  vec4 color = vec4(0.0,0.0,0.0,1.0);
  mainImage(color, gl_FragCoord.xy);
  color.w = 1.0;
  fColor = color;
}
</script>
© www.soinside.com 2019 - 2024. All rights reserved.