cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
317 lines (316 loc) • 12.7 kB
JavaScript
import Pass from "../../../../claygl/src/compositor/Pass.mjs";
import Geometry from "../../../../claygl/src/Geometry.mjs";
import Mesh from "../../../../claygl/src/Mesh.mjs";
import Material from "../../../../claygl/src/Material.mjs";
import Shader from "../../../../claygl/src/Shader.mjs";
import Texture2D from "../../../../claygl/src/Texture2D.mjs";
import Texture from "../../../../claygl/src/Texture.mjs";
import OrthoCamera from "../../../../claygl/src/camera/Orthographic.mjs";
import PlaneGeometry from "../../../../claygl/src/geometry/Plane.mjs";
import FrameBuffer from "../../../../claygl/src/FrameBuffer.mjs";
import Line2DGeometry from "./Line2D.mjs";
import vectorFieldParticleGLSL from "./vectorFieldParticle.glsl.mjs";
Shader["import"](vectorFieldParticleGLSL);
function createSpriteCanvas(size) {
var canvas = document.createElement("canvas");
canvas.width = canvas.height = size;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
ctx.fill();
return canvas;
}
var VectorFieldParticleSurface = function() {
this.motionBlurFactor = 0.99;
this.vectorFieldTexture = new Texture2D({
type: Texture.FLOAT,
flipY: false
});
this.particleLife = [5, 20];
this._particleType = "point";
this._particleSize = 1;
this.particleColor = [1, 1, 1, 1];
this.particleSpeedScaling = 1;
this._thisFrameTexture = null;
this._particlePass = null;
this._spawnTexture = null;
this._particleTexture0 = null;
this._particleTexture1 = null;
this._particlePointsMesh = null;
this._surfaceFrameBuffer = null;
this._elapsedTime = 0;
this._scene = null;
this._camera = null;
this._lastFrameTexture = null;
this._supersampling = 1;
this._downsampleTextures = [];
this._width = 512;
this._height = 512;
this.init();
};
VectorFieldParticleSurface.prototype = {
constructor: VectorFieldParticleSurface,
init: function() {
var parameters = {
type: Texture.FLOAT,
minFilter: Texture.NEAREST,
magFilter: Texture.NEAREST,
useMipmap: false
};
this._spawnTexture = new Texture2D(parameters);
this._particleTexture0 = new Texture2D(parameters);
this._particleTexture1 = new Texture2D(parameters);
this._frameBuffer = new FrameBuffer({
depthBuffer: false
});
this._particlePass = new Pass({
fragment: Shader.source("ecgl.vfParticle.particle.fragment")
});
this._particlePass.setUniform("velocityTexture", this.vectorFieldTexture);
this._particlePass.setUniform("spawnTexture", this._spawnTexture);
this._downsamplePass = new Pass({
fragment: Shader.source("clay.compositor.downsample")
});
var particlePointsMesh = new Mesh({
renderOrder: 10,
material: new Material({
shader: new Shader(Shader.source("ecgl.vfParticle.renderPoints.vertex"), Shader.source("ecgl.vfParticle.renderPoints.fragment"))
}),
mode: Mesh.POINTS,
geometry: new Geometry({
dynamic: true,
mainAttribute: "texcoord0"
})
});
var particleLinesMesh = new Mesh({
renderOrder: 10,
material: new Material({
shader: new Shader(Shader.source("ecgl.vfParticle.renderLines.vertex"), Shader.source("ecgl.vfParticle.renderLines.fragment"))
}),
geometry: new Line2DGeometry(),
culling: false
});
var lastFrameFullQuad = new Mesh({
material: new Material({
shader: new Shader(Shader.source("ecgl.color.vertex"), Shader.source("ecgl.color.fragment"))
}),
geometry: new PlaneGeometry()
});
lastFrameFullQuad.material.enableTexture("diffuseMap");
this._particlePointsMesh = particlePointsMesh;
this._particleLinesMesh = particleLinesMesh;
this._lastFrameFullQuadMesh = lastFrameFullQuad;
this._camera = new OrthoCamera();
this._thisFrameTexture = new Texture2D();
this._lastFrameTexture = new Texture2D();
},
setParticleDensity: function(width, height) {
var nVertex = width * height;
var spawnTextureData = new Float32Array(nVertex * 4);
var off = 0;
var lifeRange = this.particleLife;
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++, off++) {
spawnTextureData[off * 4] = Math.random();
spawnTextureData[off * 4 + 1] = Math.random();
spawnTextureData[off * 4 + 2] = Math.random();
var life = (lifeRange[1] - lifeRange[0]) * Math.random() + lifeRange[0];
spawnTextureData[off * 4 + 3] = life;
}
}
if (this._particleType === "line") {
this._setLineGeometry(width, height);
} else {
this._setPointsGeometry(width, height);
}
this._spawnTexture.width = width;
this._spawnTexture.height = height;
this._spawnTexture.pixels = spawnTextureData;
this._particleTexture0.width = this._particleTexture1.width = width;
this._particleTexture0.height = this._particleTexture1.height = height;
this._particlePass.setUniform("textureSize", [width, height]);
},
_setPointsGeometry: function(width, height) {
var nVertex = width * height;
var geometry = this._particlePointsMesh.geometry;
var attributes = geometry.attributes;
attributes.texcoord0.init(nVertex);
var off = 0;
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++, off++) {
attributes.texcoord0.value[off * 2] = i / width;
attributes.texcoord0.value[off * 2 + 1] = j / height;
}
}
geometry.dirty();
},
_setLineGeometry: function(width, height) {
var nLine = width * height;
var geometry = this._getParticleMesh().geometry;
geometry.setLineCount(nLine);
geometry.resetOffset();
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++) {
geometry.addLine([i / width, j / height]);
}
}
geometry.dirty();
},
_getParticleMesh: function() {
return this._particleType === "line" ? this._particleLinesMesh : this._particlePointsMesh;
},
update: function(renderer, api, deltaTime, firstFrame) {
var particleMesh = this._getParticleMesh();
var frameBuffer = this._frameBuffer;
var particlePass = this._particlePass;
if (firstFrame) {
this._updateDownsampleTextures(renderer, api);
}
particleMesh.material.set("size", this._particleSize * this._supersampling);
particleMesh.material.set("color", this.particleColor);
particlePass.setUniform("speedScaling", this.particleSpeedScaling);
frameBuffer.attach(this._particleTexture1);
particlePass.setUniform("firstFrameTime", firstFrame ? (this.particleLife[1] + this.particleLife[0]) / 2 : 0);
particlePass.setUniform("particleTexture", this._particleTexture0);
particlePass.setUniform("deltaTime", deltaTime);
particlePass.setUniform("elapsedTime", this._elapsedTime);
particlePass.render(renderer, frameBuffer);
particleMesh.material.set("particleTexture", this._particleTexture1);
particleMesh.material.set("prevParticleTexture", this._particleTexture0);
frameBuffer.attach(this._thisFrameTexture);
frameBuffer.bind(renderer);
renderer.gl.clear(renderer.gl.DEPTH_BUFFER_BIT | renderer.gl.COLOR_BUFFER_BIT);
var lastFrameFullQuad = this._lastFrameFullQuadMesh;
lastFrameFullQuad.material.set("diffuseMap", this._lastFrameTexture);
lastFrameFullQuad.material.set("color", [1, 1, 1, this.motionBlurFactor]);
this._camera.update(true);
renderer.renderPass([lastFrameFullQuad, particleMesh], this._camera);
frameBuffer.unbind(renderer);
this._downsample(renderer);
this._swapTexture();
this._elapsedTime += deltaTime;
},
_downsample: function(renderer) {
var downsampleTextures = this._downsampleTextures;
if (downsampleTextures.length === 0) {
return;
}
var current = 0;
var sourceTexture = this._thisFrameTexture;
var targetTexture = downsampleTextures[current];
while (targetTexture) {
this._frameBuffer.attach(targetTexture);
this._downsamplePass.setUniform("texture", sourceTexture);
this._downsamplePass.setUniform("textureSize", [sourceTexture.width, sourceTexture.height]);
this._downsamplePass.render(renderer, this._frameBuffer);
sourceTexture = targetTexture;
targetTexture = downsampleTextures[++current];
}
},
getSurfaceTexture: function() {
var downsampleTextures = this._downsampleTextures;
return downsampleTextures.length > 0 ? downsampleTextures[downsampleTextures.length - 1] : this._lastFrameTexture;
},
setRegion: function(region) {
this._particlePass.setUniform("region", region);
},
resize: function(width, height) {
this._lastFrameTexture.width = width * this._supersampling;
this._lastFrameTexture.height = height * this._supersampling;
this._thisFrameTexture.width = width * this._supersampling;
this._thisFrameTexture.height = height * this._supersampling;
this._width = width;
this._height = height;
},
setParticleSize: function(size) {
var particleMesh = this._getParticleMesh();
if (size <= 2) {
particleMesh.material.disableTexture("spriteTexture");
particleMesh.material.transparent = false;
return;
}
if (!this._spriteTexture) {
this._spriteTexture = new Texture2D();
}
if (!this._spriteTexture.image || this._spriteTexture.image.width !== size) {
this._spriteTexture.image = createSpriteCanvas(size);
this._spriteTexture.dirty();
}
particleMesh.material.transparent = true;
particleMesh.material.enableTexture("spriteTexture");
particleMesh.material.set("spriteTexture", this._spriteTexture);
this._particleSize = size;
},
setGradientTexture: function(gradientTexture) {
var material = this._getParticleMesh().material;
material[gradientTexture ? "enableTexture" : "disableTexture"]("gradientTexture");
material.setUniform("gradientTexture", gradientTexture);
},
setColorTextureImage: function(colorTextureImg, api) {
var material = this._getParticleMesh().material;
material.setTextureImage("colorTexture", colorTextureImg, api, {
flipY: true
});
},
setParticleType: function(type) {
this._particleType = type;
},
clearFrame: function(renderer) {
var frameBuffer = this._frameBuffer;
frameBuffer.attach(this._lastFrameTexture);
frameBuffer.bind(renderer);
renderer.gl.clear(renderer.gl.DEPTH_BUFFER_BIT | renderer.gl.COLOR_BUFFER_BIT);
frameBuffer.unbind(renderer);
},
setSupersampling: function(supersampling) {
this._supersampling = supersampling;
this.resize(this._width, this._height);
},
_updateDownsampleTextures: function(renderer, api) {
var downsampleTextures = this._downsampleTextures;
var upScale = Math.max(Math.floor(Math.log(this._supersampling / api.getDevicePixelRatio()) / Math.log(2)), 0);
var scale = 2;
var width = this._width * this._supersampling;
var height = this._height * this._supersampling;
for (var i = 0; i < upScale; i++) {
downsampleTextures[i] = downsampleTextures[i] || new Texture2D();
downsampleTextures[i].width = width / scale;
downsampleTextures[i].height = height / scale;
scale *= 2;
}
for (; i < downsampleTextures.length; i++) {
downsampleTextures[i].dispose(renderer);
}
downsampleTextures.length = upScale;
},
_swapTexture: function() {
var tmp = this._particleTexture0;
this._particleTexture0 = this._particleTexture1;
this._particleTexture1 = tmp;
var tmp = this._thisFrameTexture;
this._thisFrameTexture = this._lastFrameTexture;
this._lastFrameTexture = tmp;
},
dispose: function(renderer) {
renderer.disposeFrameBuffer(this._frameBuffer);
renderer.disposeTexture(this.vectorFieldTexture);
renderer.disposeTexture(this._spawnTexture);
renderer.disposeTexture(this._particleTexture0);
renderer.disposeTexture(this._particleTexture1);
renderer.disposeTexture(this._thisFrameTexture);
renderer.disposeTexture(this._lastFrameTexture);
renderer.disposeGeometry(this._particleLinesMesh.geometry);
renderer.disposeGeometry(this._particlePointsMesh.geometry);
renderer.disposeGeometry(this._lastFrameFullQuadMesh.geometry);
if (this._spriteTexture) {
renderer.disposeTexture(this._spriteTexture);
}
this._particlePass.dispose(renderer);
this._downsamplePass.dispose(renderer);
this._downsampleTextures.forEach(function(texture) {
texture.dispose(renderer);
});
}
};
var VectorFieldParticleSurface$1 = VectorFieldParticleSurface;
export { VectorFieldParticleSurface$1 as default };