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:
parent
e8d5414e1e
commit
458193be4a
194
src/components/liquid/LiquidEffect.js
Normal file
194
src/components/liquid/LiquidEffect.js
Normal 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;
|
54
src/components/liquid/LiquidPlane.js
Normal file
54
src/components/liquid/LiquidPlane.js
Normal 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: {
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user