1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

add liquid plane

This commit is contained in:
Kevin Levron 2021-03-14 10:32:38 +01:00
parent e8d5414e1e
commit 458193be4a
2 changed files with 248 additions and 0 deletions

View File

@ -0,0 +1,194 @@
import {
FloatType,
Mesh,
OrthographicCamera,
PlaneBufferGeometry,
RGBAFormat,
ShaderMaterial,
Uniform,
Vector2,
WebGLRenderTarget,
} from 'three';
function LiquidEffect(renderer) {
this.renderer = renderer;
this.width = 512;
this.height = 512;
// this.delta = new Vector2(this.width / Math.pow(width, 2), this.height / Math.pow(height, 2));
this.delta = new Vector2(1 / this.width, 1 / this.height);
this.hMap = new WebGLRenderTarget(this.width, this.height, { type: FloatType, format: RGBAFormat, depthBuffer: false, stencilBuffer: false });
this.hMap1 = new WebGLRenderTarget(this.width, this.height, { type: FloatType, format: RGBAFormat, depthBuffer: false, stencilBuffer: false });
this.fsQuad = new FullScreenQuad();
this.initShaders();
};
LiquidEffect.prototype.initShaders = function () {
const defaultVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
this.copyMat = new ShaderMaterial({
uniforms: { tDiffuse: { value: null } },
vertexShader: defaultVertexShader,
fragmentShader: `
uniform sampler2D tDiffuse;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(tDiffuse, vUv);
}
`,
});
this.updateMat = new ShaderMaterial({
uniforms: {
tDiffuse: { value: null },
delta: new Uniform(this.delta),
},
vertexShader: defaultVertexShader,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform vec2 delta;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(tDiffuse, vUv);
vec2 dx = vec2(delta.x, 0.0);
vec2 dy = vec2(0.0, delta.y);
float average = (
texture2D(tDiffuse, vUv - dx).r +
texture2D(tDiffuse, vUv - dy).r +
texture2D(tDiffuse, vUv + dx).r +
texture2D(tDiffuse, vUv + dy).r
) * 0.25;
texel.g += (average - texel.r) * 2.0;
texel.g *= 0.995;
texel.r += texel.g;
gl_FragColor = texel;
}
`,
});
this.normalsMat = new ShaderMaterial({
uniforms: {
tDiffuse: { value: null },
delta: new Uniform(this.delta),
},
vertexShader: defaultVertexShader,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform vec2 delta;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(tDiffuse, vUv);
vec3 dx = vec3(delta.x, texture2D(tDiffuse, vec2(vUv.x + delta.x, vUv.y)).r - texel.r, 0.0);
vec3 dy = vec3(0.0, texture2D(tDiffuse, vec2(vUv.x, vUv.y + delta.y)).r - texel.r, delta.y);
texel.ba = normalize(cross(dy, dx)).xz;
gl_FragColor = texel;
}
`,
});
this.dropMat = new ShaderMaterial({
uniforms: {
tDiffuse: { value: null },
center: new Uniform(new Vector2()),
radius: { value: 0.05 },
strength: { value: 0.5 },
},
vertexShader: defaultVertexShader,
fragmentShader: `
const float PI = 3.1415926535897932384626433832795;
uniform sampler2D tDiffuse;
uniform vec2 center;
uniform float radius;
uniform float strength;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(tDiffuse, vUv);
float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - vUv) / radius);
drop = 0.5 - cos(drop * PI) * 0.5;
texel.r += drop * strength;
// texel.r = clamp(texel.r, -2.0, 2.0);
gl_FragColor = texel;
}
`,
});
};
LiquidEffect.prototype.update = function () {
this.updateHMap();
// this.updateHMap();
this.updateHMapNormals();
};
LiquidEffect.prototype.updateHMap = function () {
this.updateMat.uniforms.tDiffuse.value = this.hMap.texture;
this.renderShaderMat(this.updateMat, this.hMap1);
this.swapBuffers();
};
LiquidEffect.prototype.updateHMapNormals = function () {
this.normalsMat.uniforms.tDiffuse.value = this.hMap.texture;
this.renderShaderMat(this.normalsMat, this.hMap1);
this.swapBuffers();
};
LiquidEffect.prototype.addDrop = function (x, y, radius, strength) {
this.dropMat.uniforms.tDiffuse.value = this.hMap.texture;
this.dropMat.uniforms.center.value.set(x, y);
this.dropMat.uniforms.radius.value = radius;
this.dropMat.uniforms.strength.value = strength;
this.renderShaderMat(this.dropMat, this.hMap1);
this.swapBuffers();
};
LiquidEffect.prototype.renderBuffer = function (buffer, target) {
this.copyMat.uniforms.tDiffuse.value = buffer.texture;
this.renderShaderMat(this.copyMat, target);
};
LiquidEffect.prototype.renderShaderMat = function (mat, target) {
this.fsQuad.material = mat;
const oldTarget = this.renderer.getRenderTarget();
this.renderer.setRenderTarget(target);
this.fsQuad.render(this.renderer);
this.renderer.setRenderTarget(oldTarget);
};
LiquidEffect.prototype.swapBuffers = function () {
const temp = this.hMap;
this.hMap = this.hMap1;
this.hMap1 = temp;
};
// from https://threejs.org/examples/js/postprocessing/EffectComposer.js
const FullScreenQuad = (function () {
const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
const geometry = new PlaneBufferGeometry(2, 2);
const FullScreenQuad = function (material) {
this._mesh = new Mesh(geometry, material);
};
Object.defineProperty(FullScreenQuad.prototype, 'material', {
get: function () { return this._mesh.material; },
set: function (value) { this._mesh.material = value; },
});
Object.assign(FullScreenQuad.prototype, {
render: function (renderer) {
renderer.render(this._mesh, camera);
},
});
return FullScreenQuad;
})();
export default LiquidEffect;

View File

@ -0,0 +1,54 @@
import { DoubleSide, Mesh, MeshStandardMaterial, PlaneBufferGeometry } from 'three';
import { watch } from 'vue';
import Object3D from '../../core/Object3D.js';
import { bindProps } from '../../tools.js';
import LiquidEffect from './LiquidEffect.js';
export default {
extends: Object3D,
props: {
width: { type: Number, default: 10 },
height: { type: Number, default: 10 },
widthSegments: { type: Number, default: 200 },
heightSegments: { type: Number, default: 200 },
color: { type: [Number, String], default: '#ffffff' },
metalness: { type: Number, default: 0.75 },
roughness: { type: Number, default: 0.25 },
},
mounted() {
this.liquidEffect = new LiquidEffect(this.three.renderer);
this.rendererComponent.onMounted(() => {
this.liquidEffect.renderer = this.rendererComponent.renderer;
this.three.onBeforeRender(() => {
this.liquidEffect.update();
});
});
this.material = new MeshStandardMaterial({ color: this.color, side: DoubleSide, metalness: this.metalness, roughness: this.roughness,
onBeforeCompile: shader => {
shader.uniforms.hmap = { value: this.liquidEffect.hMap.texture };
shader.vertexShader = "uniform sampler2D hmap;\n" + shader.vertexShader;
const token = '#include <begin_vertex>';
const customTransform = `
vec3 transformed = vec3(position);
vec4 info = texture2D(hmap, uv);
vNormal = vec3(info.b, sqrt(1.0 - dot(info.ba, info.ba)), info.a).xzy;
transformed.z = 20. * info.r;
`;
shader.vertexShader = shader.vertexShader.replace(token, customTransform);
},
});
bindProps(this, ['metalness', 'roughness'], this.material);
watch(() => this.color, (value) => this.material.color.set(value));
this.geometry = new PlaneBufferGeometry(this.width, this.height, this.widthSegments, this.heightSegments);
this.mesh = new Mesh(this.geometry, this.material);
this.initObject3D(this.mesh);
},
// mounted() {
// },
// unmounted() {
// },
methods: {
},
};