纪念日快乐。
     播放
Must be run on the Webkits browsers
源码:
<style>body {
    padding:0;
    margin:0;
    overflow:hidden;
  height600px;
}
canvas {
    padding:0;
    margin:0;
}
div.btnbg {
    position:fixed;
    left:0;
    top:0;
}
</style>
<script>// Utilities
var Vector3 {};
var Matrix44 {};
Vector3.create function(xyz{
    return {'x':x'y':y'z':z};
};
Vector3.dot function (v0v1{
    return v0.v1.v0.v1.v0.v1.z;
};
Vector3.cross function (vv0v1{
    v.v0.v1.v0.v1.y;
    v.v0.v1.v0.v1.z;
    v.v0.v1.v0.v1.x;
};
Vector3.normalize function (v{
    var v.v.v.v.v.v.z;
    if(0.00001{
        1.0 Math.sqrt(l);
        v.*= l;
        v.*= l;
        v.*= l;
    }
};
Vector3.arrayForm function(v{
    if(v.array{
        v.array[0v.x;
        v.array[1v.y;
        v.array[2v.z;
    }
    else {
        v.array new Float32Array([v.xv.yv.z]);
    }
    return v.array;
};
Matrix44.createIdentity function ({
    return new Float32Array([1.00.00.00.00.01.00.00.00.00.01.00.00.00.00.01.0]);
};
Matrix44.loadProjection function (maspectvdegnearfar{
    var near Math.tan(vdeg Math.PI 180.0 0.52.0;
    var aspect;
    
    m[02.0 near w;
    m[10.0;
    m[20.0;
    m[30.0;
    
    m[40.0;
    m[52.0 near h;
    m[60.0;
    m[70.0;
    
    m[80.0;
    m[90.0;
    m[10-(far near(far near);
    m[11-1.0;
    
    m[120.0;
    m[130.0;
    m[14-2.0 far near (far near);
    m[150.0;
};
Matrix44.loadLookAt function (mvposvlookvup{
    var frontv Vector3.create(vpos.vlook.xvpos.vlook.yvpos.vlook.z);
    Vector3.normalize(frontv);
    var sidev Vector3.create(1.00.00.0);
    Vector3.cross(sidevvupfrontv);
    Vector3.normalize(sidev);
    var topv Vector3.create(1.00.00.0);
    Vector3.cross(topvfrontvsidev);
    Vector3.normalize(topv);
    
    m[0sidev.x;
    m[1topv.x;
    m[2frontv.x;
    m[30.0;
    
    m[4sidev.y;
    m[5topv.y;
    m[6frontv.y;
    m[70.0;
    
    m[8sidev.z;
    m[9topv.z;
    m[10frontv.z;
    m[110.0;
    
    m[12-(vpos.m[0vpos.m[4vpos.m[8]);
    m[13-(vpos.m[1vpos.m[5vpos.m[9]);
    m[14-(vpos.m[2vpos.m[6vpos.m[10]);
    m[151.0;
};

//
var timeInfo {
    'start':0'prev':0// Date
    'delta':0'elapsed':// Number(sec)
};

//
var gl;
var renderSpec {
    'width':0,
    'height':0,
    'aspect':1,
    'array':new Float32Array(3),
    'halfWidth':0,
    'halfHeight':0,
    'halfArray':new Float32Array(3)
    // and some render targets. see setViewport()
};
renderSpec.setSize function(wh{
    renderSpec.width w;
    renderSpec.height h;
    renderSpec.aspect renderSpec.width renderSpec.height;
    renderSpec.array[0renderSpec.width;
    renderSpec.array[1renderSpec.height;
    renderSpec.array[2renderSpec.aspect;
    
    renderSpec.halfWidth Math.floor(2);
    renderSpec.halfHeight Math.floor(2);
    renderSpec.halfArray[0renderSpec.halfWidth;
    renderSpec.halfArray[1renderSpec.halfHeight;
    renderSpec.halfArray[2renderSpec.halfWidth renderSpec.halfHeight;
};

function deleteRenderTarget(rt{
    gl.deleteFramebuffer(rt.frameBuffer);
    gl.deleteRenderbuffer(rt.renderBuffer);
    gl.deleteTexture(rt.texture);
}

function createRenderTarget(wh{
    var ret {
        'width':w,
        'height':h,
        'sizeArray':new Float32Array([whh]),
        'dtxArray':new Float32Array([1.0 w1.0 h])
    };
    ret.frameBuffer gl.createFramebuffer();
    ret.renderBuffer gl.createRenderbuffer();
    ret.texture gl.createTexture();
    
    gl.bindTexture(gl.TEXTURE_2Dret.texture);
    gl.texImage2D(gl.TEXTURE_2D0gl.RGBAwh0gl.RGBAgl.UNSIGNED_BYTEnull);
    gl.texParameteri(gl.TEXTURE_2Dgl.TEXTURE_WRAP_Sgl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2Dgl.TEXTURE_WRAP_Tgl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2Dgl.TEXTURE_MAG_FILTERgl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2Dgl.TEXTURE_MIN_FILTERgl.LINEAR);
    
    gl.bindFramebuffer(gl.FRAMEBUFFERret.frameBuffer);
    gl.framebufferTexture2D(gl.FRAMEBUFFERgl.COLOR_ATTACHMENT0gl.TEXTURE_2Dret.texture0);
    
    gl.bindRenderbuffer(gl.RENDERBUFFERret.renderBuffer);
    gl.renderbufferStorage(gl.RENDERBUFFERgl.DEPTH_COMPONENT16wh);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFERgl.DEPTH_ATTACHMENTgl.RENDERBUFFERret.renderBuffer);
    
    gl.bindTexture(gl.TEXTURE_2Dnull);
    gl.bindRenderbuffer(gl.RENDERBUFFERnull);
    gl.bindFramebuffer(gl.FRAMEBUFFERnull);
    
    return ret;
}

function compileShader(shtypeshsrc{
  var retsh gl.createShader(shtype);
  
  gl.shaderSource(retshshsrc);
  gl.compileShader(retsh);
  
  if(!gl.getShaderParameter(retshgl.COMPILE_STATUS){
    var errlog gl.getShaderInfoLog(retsh);
    gl.deleteShader(retsh);
    console.error(errlog);
    return null;
  }
  return retsh;
}

function createShader(vtxsrcfrgsrcuniformlistattrlist{
    var vsh compileShader(gl.VERTEX_SHADERvtxsrc);
    var fsh compileShader(gl.FRAGMENT_SHADERfrgsrc);
    
    if(vsh == null || fsh == null{
        return null;
    }
    
    var prog gl.createProgram();
    gl.attachShader(progvsh);
    gl.attachShader(progfsh);
    
    gl.deleteShader(vsh);
    gl.deleteShader(fsh);
    
    gl.linkProgram(prog);
    if (!gl.getProgramParameter(proggl.LINK_STATUS){
        var errlog gl.getProgramInfoLog(prog);
        console.error(errlog);
        return null;
    }
    
    if(uniformlist{
        prog.uniforms {};
        for(var 0uniformlist.lengthi++{
            prog.uniforms[uniformlist[i]gl.getUniformLocation(proguniformlist[i]);
        }
    }
    
    if(attrlist{
        prog.attributes {};
        for(var 0attrlist.lengthi++{
            var attr attrlist[i];
            prog.attributes[attrgl.getAttribLocation(progattr);
        }
    }
    
    return prog;
}

function useShader(prog{
    gl.useProgram(prog);
    for(var attr in prog.attributes{
        gl.enableVertexAttribArray(prog.attributes[attr]);;
    }
}

function unuseShader(prog{
    for(var attr in prog.attributes{
        gl.disableVertexAttribArray(prog.attributes[attr]);;
    }
    gl.useProgram(null);
}

/////
var projection {
    'angle':60,
    'nearfar':new Float32Array([0.1100.0]),
    'matrix':Matrix44.createIdentity()
};
var camera {
    'position':Vector3.create(00100),
    'lookat':Vector3.create(000),
    'up':Vector3.create(010),
    'dof':Vector3.create(10.04.08.0),
    'matrix':Matrix44.createIdentity()
};

var pointFlower {};
var meshFlower {};
var sceneStandBy false;

var BlossomParticle function ({
    this.velocity new Array(3);
    this.rotation new Array(3);
    this.position new Array(3);
    this.euler new Array(3);
    this.size 1.0;
    this.alpha 1.0;
    this.zkey 0.0;
};

BlossomParticle.prototype.setVelocity function (vxvyvz{
    this.velocity[0vx;
    this.velocity[1vy;
    this.velocity[2vz;
};

BlossomParticle.prototype.setRotation function (rxryrz{
    this.rotation[0rx;
    this.rotation[1ry;
    this.rotation[2rz;
};

BlossomParticle.prototype.setPosition function (nxnynz{
    this.position[0nx;
    this.position[1ny;
    this.position[2nz;
};

BlossomParticle.prototype.setEulerAngles function (rxryrz{
    this.euler[0rx;
    this.euler[1ry;
    this.euler[2rz;
};

BlossomParticle.prototype.setSize function (s{
    this.size s;
};

BlossomParticle.prototype.update function (dtet{
    this.position[0+= this.velocity[0dt;
    this.position[1+= this.velocity[1dt;
    this.position[2+= this.velocity[2dt;
    
    this.euler[0+= this.rotation[0dt;
    this.euler[1+= this.rotation[1dt;
    this.euler[2+= this.rotation[2dt;
};

function createPointFlowers({
    // get point sizes
    var prm gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE);
    renderSpec.pointSize {'min':prm[0]'max':prm[1]};
    
    var vtxsrc document.getElementById("sakura_point_vsh").textContent;
    var frgsrc document.getElementById("sakura_point_fsh").textContent;
    
    pointFlower.program createShader(
        vtxsrcfrgsrc,
        ['uProjection''uModelview''uResolution''uOffset''uDOF''uFade'],
        ['aPosition''aEuler''aMisc']
    );
    
    useShader(pointFlower.program);
    pointFlower.offset new Float32Array([0.00.00.0]);
    pointFlower.fader Vector3.create(0.010.00.0);
    
    // paramerters: velocity[3], rotate[3]
    pointFlower.numFlowers 1600;
    pointFlower.particles new Array(pointFlower.numFlowers);
    // vertex attributes {position[3], euler_xyz[3], size[1]}
    pointFlower.dataArray new Float32Array(pointFlower.numFlowers (2));
    pointFlower.positionArrayOffset 0;
    pointFlower.eulerArrayOffset pointFlower.numFlowers 3;
    pointFlower.miscArrayOffset pointFlower.numFlowers 6;
    
    pointFlower.buffer gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFERpointFlower.buffer);
    gl.bufferData(gl.ARRAY_BUFFERpointFlower.dataArraygl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFERnull);
    
    unuseShader(pointFlower.program);
    
    for(var 0pointFlower.numFlowersi++{
        pointFlower.particles[inew BlossomParticle();
    }
}

function initPointFlowers({
    //area
    pointFlower.area Vector3.create(20.020.020.0);
    pointFlower.area.pointFlower.area.renderSpec.aspect;
    
    pointFlower.fader.10.0//env fade start
    pointFlower.fader.pointFlower.area.z//env fade half
    pointFlower.fader.0.1;  //near fade start
    
    //particles
    var PI2 Math.PI 2.0;
    var tmpv3 Vector3.create(000);
    var tmpv 0;
    var symmetryrand function({return (Math.random(2.0 1.0);};
    for(var 0pointFlower.numFlowersi++{
        var tmpprtcl pointFlower.particles[i];
        
        //velocity
        tmpv3.symmetryrand(0.3 0.8;
        tmpv3.symmetryrand(0.2 1.0;
        tmpv3.symmetryrand(0.3 0.5;
        Vector3.normalize(tmpv3);
        tmpv 2.0 Math.random(1.0;
        tmpprtcl.setVelocity(tmpv3.tmpvtmpv3.tmpvtmpv3.tmpv);
        
        //rotation
        tmpprtcl.setRotation(
            symmetryrand(PI2 0.5,
            symmetryrand(PI2 0.5,
            symmetryrand(PI2 0.5
        );
        
        //position
        tmpprtcl.setPosition(
            symmetryrand(pointFlower.area.x,
            symmetryrand(pointFlower.area.y,
            symmetryrand(pointFlower.area.z
        );
        
        //euler
        tmpprtcl.setEulerAngles(
            Math.random(Math.PI 2.0,
            Math.random(Math.PI 2.0,
            Math.random(Math.PI 2.0
        );
        
        //size
        tmpprtcl.setSize(0.9 Math.random(0.1);
    }
}

function renderPointFlowers({
    //update
    var PI2 Math.PI 2.0;
    var limit [pointFlower.area.xpointFlower.area.ypointFlower.area.z];
    var repeatPos function (prtcmplimit{
        if(Math.abs(prt.position[cmp]prt.size 0.5 limit{
            //out of area
            if(prt.position[cmp0{
                prt.position[cmp-= limit 2.0;
            }
            else {
                prt.position[cmp+= limit 2.0;
            }
        }
    };
    var repeatEuler function (prtcmp{
        prt.euler[cmpprt.euler[cmpPI2;
        if(prt.euler[cmp0.0{
            prt.euler[cmp+= PI2;
        }
    };
    
    for(var 0pointFlower.numFlowersi++{
        var prtcl pointFlower.particles[i];
        prtcl.update(timeInfo.deltatimeInfo.elapsed);
        repeatPos(prtcl0pointFlower.area.x);
        repeatPos(prtcl1pointFlower.area.y);
        repeatPos(prtcl2pointFlower.area.z);
        repeatEuler(prtcl0);
        repeatEuler(prtcl1);
        repeatEuler(prtcl2);
        
        prtcl.alpha 1.0;//(pointFlower.area.z - prtcl.position[2]) * 0.5;
        
        prtcl.zkey (camera.matrix[2prtcl.position[0]
                    camera.matrix[6prtcl.position[1]
                    camera.matrix[10prtcl.position[2]
                    camera.matrix[14]);
    }
    
    // sort
    pointFlower.particles.sort(function(p0p1){return p0.zkey p1.zkey;});
    
    // update data
    var ipos pointFlower.positionArrayOffset;
    var ieuler pointFlower.eulerArrayOffset;
    var imisc pointFlower.miscArrayOffset;
    for(var 0pointFlower.numFlowersi++{
        var prtcl pointFlower.particles[i];
        pointFlower.dataArray[iposprtcl.position[0];
        pointFlower.dataArray[ipos 1prtcl.position[1];
        pointFlower.dataArray[ipos 2prtcl.position[2];
        ipos += 3;
        pointFlower.dataArray[ieulerprtcl.euler[0];
        pointFlower.dataArray[ieuler 1prtcl.euler[1];
        pointFlower.dataArray[ieuler 2prtcl.euler[2];
        ieuler += 3;
        pointFlower.dataArray[imiscprtcl.size;
        pointFlower.dataArray[imisc 1prtcl.alpha;
        imisc += 2;
    }
    
    //draw
    gl.enable(gl.BLEND);
    //gl.disable(gl.DEPTH_TEST);
    gl.blendFunc(gl.SRC_ALPHAgl.ONE_MINUS_SRC_ALPHA);
    
    var prog pointFlower.program;
    useShader(prog);
    
    gl.uniformMatrix4fv(prog.uniforms.uProjectionfalseprojection.matrix);
    gl.uniformMatrix4fv(prog.uniforms.uModelviewfalsecamera.matrix);
    gl.uniform3fv(prog.uniforms.uResolutionrenderSpec.array);
    gl.uniform3fv(prog.uniforms.uDOFVector3.arrayForm(camera.dof));
    gl.uniform3fv(prog.uniforms.uFadeVector3.arrayForm(pointFlower.fader));
    
    gl.bindBuffer(gl.ARRAY_BUFFERpointFlower.buffer);
    gl.bufferData(gl.ARRAY_BUFFERpointFlower.dataArraygl.DYNAMIC_DRAW);
    
    gl.vertexAttribPointer(prog.attributes.aPosition3gl.FLOATfalse0pointFlower.positionArrayOffset Float32Array.BYTES_PER_ELEMENT);
    gl.vertexAttribPointer(prog.attributes.aEuler3gl.FLOATfalse0pointFlower.eulerArrayOffset Float32Array.BYTES_PER_ELEMENT);
    gl.vertexAttribPointer(prog.attributes.aMisc2gl.FLOATfalse0pointFlower.miscArrayOffset Float32Array.BYTES_PER_ELEMENT);
    
    // doubler
    for(var 12i++{
        var zpos -2.0;
        pointFlower.offset[0pointFlower.area.-1.0;
        pointFlower.offset[1pointFlower.area.-1.0;
        pointFlower.offset[2pointFlower.area.zpos;
        gl.uniform3fv(prog.uniforms.uOffsetpointFlower.offset);
        gl.drawArrays(gl.POINT0pointFlower.numFlowers);
        
        pointFlower.offset[0pointFlower.area.-1.0;
        pointFlower.offset[1pointFlower.area.*  1.0;
        pointFlower.offset[2pointFlower.area.zpos;
        gl.uniform3fv(prog.uniforms.uOffsetpointFlower.offset);
        gl.drawArrays(gl.POINT0pointFlower.numFlowers);
        
        pointFlower.offset[0pointFlower.area.*  1.0;
        pointFlower.offset[1pointFlower.area.-1.0;
        pointFlower.offset[2pointFlower.area.zpos;
        gl.uniform3fv(prog.uniforms.uOffsetpointFlower.offset);
        gl.drawArrays(gl.POINT0pointFlower.numFlowers);
        
        pointFlower.offset[0pointFlower.area.*  1.0;
        pointFlower.offset[1pointFlower.area.*  1.0;
        pointFlower.offset[2pointFlower.area.zpos;
        gl.uniform3fv(prog.uniforms.uOffsetpointFlower.offset);
        gl.drawArrays(gl.POINT0pointFlower.numFlowers);
    }
    
    //main
    pointFlower.offset[00.0;
    pointFlower.offset[10.0;
    pointFlower.offset[20.0;
    gl.uniform3fv(prog.uniforms.uOffsetpointFlower.offset);
    gl.drawArrays(gl.POINT0pointFlower.numFlowers);
    
    gl.bindBuffer(gl.ARRAY_BUFFERnull);
    unuseShader(prog);
    
    gl.enable(gl.DEPTH_TEST);
    gl.disable(gl.BLEND);
}

// effects
//common util
function createEffectProgram(vtxsrcfrgsrcexunifsexattrs{
    var ret {};
    var unifs ['uResolution''uSrc''uDelta'];
    if(exunifs{
        unifs unifs.concat(exunifs);
    }
    var attrs ['aPosition'];
    if(exattrs{
        attrs attrs.concat(exattrs);
    }
    
    ret.program createShader(vtxsrcfrgsrcunifsattrs);
    useShader(ret.program);
    
    ret.dataArray new Float32Array([
        -1.0-1.0,
         1.0-1.0,
        -1.0,  1.0,
         1.0,  1.0
    ]);
    ret.buffer gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFERret.buffer);
    gl.bufferData(gl.ARRAY_BUFFERret.dataArraygl.STATIC_DRAW);
    
    gl.bindBuffer(gl.ARRAY_BUFFERnull);
    unuseShader(ret.program);
    
    return ret;
}

// basic usage
// useEffect(prog, srctex({'texture':texid, 'dtxArray':(f32)[dtx, dty]})); //basic initialize
// gl.uniform**(...); //additional uniforms
// drawEffect()
// unuseEffect(prog)
// TEXTURE0 makes src
function useEffect(fxobjsrctex{
    var prog fxobj.program;
    useShader(prog);
    gl.uniform3fv(prog.uniforms.uResolutionrenderSpec.array);
    
    if(srctex != null{
        gl.uniform2fv(prog.uniforms.uDeltasrctex.dtxArray);
        gl.uniform1i(prog.uniforms.uSrc0);
        
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2Dsrctex.texture);
    }
}
function drawEffect(fxobj{
    gl.bindBuffer(gl.ARRAY_BUFFERfxobj.buffer);
    gl.vertexAttribPointer(fxobj.program.attributes.aPosition2gl.FLOATfalse00);
    gl.drawArrays(gl.TRIANGLE_STRIP04);
}
function unuseEffect(fxobj{
    unuseShader(fxobj.program);
}

var effectLib {};
function createEffectLib({
    
    var vtxsrcfrgsrc;
    //common
    var cmnvtxsrc document.getElementById("fx_common_vsh").textContent;
    
    //background
    frgsrc document.getElementById("bg_fsh").textContent;
    effectLib.sceneBg createEffectProgram(cmnvtxsrcfrgsrc['uTimes']null);
    
    // make brightpixels buffer
    frgsrc document.getElementById("fx_brightbuf_fsh").textContent;
    effectLib.mkBrightBuf createEffectProgram(cmnvtxsrcfrgsrcnullnull);
    
    // direction blur
    frgsrc document.getElementById("fx_dirblur_r4_fsh").textContent;
    effectLib.dirBlur createEffectProgram(cmnvtxsrcfrgsrc['uBlurDir']null);
    
    //final composite
    vtxsrc document.getElementById("pp_final_vsh").textContent;
    frgsrc document.getElementById("pp_final_fsh").textContent;
    effectLib.finalComp createEffectProgram(vtxsrcfrgsrc['uBloom']null);
}

// background
function createBackground({
    //console.log("create background");
}
function initBackground({
    //console.log("init background");
}
function renderBackground({
    gl.disable(gl.DEPTH_TEST);
    
    useEffect(effectLib.sceneBgnull);
    gl.uniform2f(effectLib.sceneBg.program.uniforms.uTimestimeInfo.elapsedtimeInfo.delta);
    drawEffect(effectLib.sceneBg);
    unuseEffect(effectLib.sceneBg);
    
    gl.enable(gl.DEPTH_TEST);
}

// post process
var postProcess {};
function createPostProcess({
    //console.log("create post process");
}
function initPostProcess({
    //console.log("init post process");
}

function renderPostProcess({
    gl.enable(gl.TEXTURE_2D);
    gl.disable(gl.DEPTH_TEST);
    var bindRT function (rtisclear{
        gl.bindFramebuffer(gl.FRAMEBUFFERrt.frameBuffer);
        gl.viewport(00rt.widthrt.height);
        if(isclear{
            gl.clearColor(0000);
            gl.clear(gl.COLOR_BUFFER_BIT gl.DEPTH_BUFFER_BIT);
        }
    };
    
    //make bright buff
    bindRT(renderSpec.wHalfRT0true);
    useEffect(effectLib.mkBrightBufrenderSpec.mainRT);
    drawEffect(effectLib.mkBrightBuf);
    unuseEffect(effectLib.mkBrightBuf);
    
    // make bloom
    for(var 02i++{
        var 1.5 i;
        var 2.0 i;
        bindRT(renderSpec.wHalfRT1true);
        useEffect(effectLib.dirBlurrenderSpec.wHalfRT0);
        gl.uniform4f(effectLib.dirBlur.program.uniforms.uBlurDirp0.0s0.0);
        drawEffect(effectLib.dirBlur);
        unuseEffect(effectLib.dirBlur);
        
        bindRT(renderSpec.wHalfRT0true);
        useEffect(effectLib.dirBlurrenderSpec.wHalfRT1);
        gl.uniform4f(effectLib.dirBlur.program.uniforms.uBlurDir0.0p0.0s);
        drawEffect(effectLib.dirBlur);
        unuseEffect(effectLib.dirBlur);
    }
    
    //display
    gl.bindFramebuffer(gl.FRAMEBUFFERnull);
    gl.viewport(00renderSpec.widthrenderSpec.height);
    gl.clear(gl.COLOR_BUFFER_BIT gl.DEPTH_BUFFER_BIT);
    
    useEffect(effectLib.finalComprenderSpec.mainRT);
    gl.uniform1i(effectLib.finalComp.program.uniforms.uBloom1);
    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2DrenderSpec.wHalfRT0.texture);