Skip to content

Instantly share code, notes, and snippets.

@essingen123
Created February 2, 2020 21:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save essingen123/0ddede6a6b778016e500e014d68d6437 to your computer and use it in GitHub Desktop.
Save essingen123/0ddede6a6b778016e500e014d68d6437 to your computer and use it in GitHub Desktop.
Grid (test)
<canvas id="canvas"></canvas>
function App() {
const conf = {
el: 'canvas',
fov: 75,
cameraZ: 100,
background: 0x000000,
};
let renderer, scene, camera, cameraCtrl;
let width, height, cx, cy, wWidth, wHeight;
const { randFloat: rnd, randFloatSpread: rndFS } = THREE.Math;
let ripple;
let gridWWidth, gridWHeight, gridWidth, gridHeight;
const mouse = new THREE.Vector2();
const mousePlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
const mousePosition = new THREE.Vector3();
const raycaster = new THREE.Raycaster();
let mouseOver = false;
init();
function init() {
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById(conf.el), antialias: true });
// if (!renderer.extensions.get('OES_texture_float')) {
// alert('no floating point texture support');
// return;
// }
camera = new THREE.PerspectiveCamera(conf.fov);
camera.position.z = conf.cameraZ;
updateSize();
window.addEventListener('resize', updateSize, false);
gridWWidth = wWidth;
gridWHeight = wHeight;
gridWidth = gridWWidth * width / wWidth;
gridHeight = gridWHeight * height / wHeight;
ripple = new RippleEffect(renderer, gridWidth, gridHeight, 1 / 5);
const getGridMP = function (e) {
const v = new THREE.Vector3();
camera.getWorldDirection(v);
v.normalize();
mouse.x = (e.clientX / width) * 2 - 1;
mouse.y = -(e.clientY / height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(mousePlane, mousePosition);
return { x: 2 * mousePosition.x / gridWWidth, y: 2 * mousePosition.y / gridWHeight };
};
renderer.domElement.addEventListener('mouseleave', e => { mouseOver = false; });
renderer.domElement.addEventListener('mousemove', e => {
mouseOver = true;
const gp = getGridMP(e);
ripple.addDrop(gp.x, gp.y, 0.05, 0.1);
});
renderer.domElement.addEventListener('mouseup', e => {
const gp = getGridMP(e);
ripple.addDrop(gp.x, gp.y, 0.1, -1.5);
});
initScene();
animate();
}
function initScene() {
scene = new THREE.Scene();
if (conf.background) scene.background = new THREE.Color(conf.background);
const material = new THREE.MeshBasicMaterial({
color: 0xFFFFFF, side: THREE.DoubleSide, onBeforeCompile: shader => {
shader.uniforms.hmap = { value: ripple.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);
transformed.z = 20. * info.r;
`;
shader.vertexShader = shader.vertexShader.replace(token, customTransform);
}
});
const pSize = 1;
let nx = Math.round(gridWidth / 5), ny = Math.round(gridHeight / 50);
let dy = gridWHeight / ny;
for (let j = 0; j <= ny; j++) {
const geometry = new THREE.PlaneBufferGeometry(gridWWidth, pSize, nx, 1);
geometry.translate(0, - gridWHeight / 2 + j * dy, 0);
const uvH = pSize / gridWHeight;
const uvY = j / ny;
const uvs = geometry.attributes.uv.array;
for (let i = 0; i < uvs.length; i += 2) {
uvs[i + 1] = (uvs[i + 1] == 0) ? uvY - uvH : uvY + uvH;
}
scene.add(new THREE.Mesh(geometry, material));
}
nx = Math.round(gridWidth / 50); ny = Math.round(gridHeight / 5);
let dx = gridWWidth / nx;
for (let i = 0; i <= nx; i++) {
const geometry = new THREE.PlaneBufferGeometry(pSize, gridWHeight, 1, ny);
geometry.translate(- gridWWidth / 2 + i * dx, 0, 0);
const uvW = pSize / gridWWidth;
const uvX = i / nx;
const uvs = geometry.attributes.uv.array;
for (let i = 0; i < uvs.length; i += 2) {
uvs[i] = (uvs[i] == 0) ? uvX - uvW : uvX + uvW;
}
scene.add(new THREE.Mesh(geometry, material));
}
camera.position.set(0, -gridWHeight/1.6, 40);
camera.lookAt(new THREE.Vector3(0, -gridWHeight/6, 0));
cameraCtrl = new THREE.OrbitControls(camera, renderer.domElement);
cameraCtrl.enableDamping = true;
cameraCtrl.dampingFactor = 0.1;
cameraCtrl.rotateSpeed = 0.5;
}
function animate() {
if (!mouseOver) {
const time = Date.now() * 0.001;
const x = Math.cos(time) * 0.4;
const y = Math.sin(time) * 0.4;
ripple.addDrop(x, y, 0.05, -0.05);
}
ripple.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function updateSize() {
width = window.innerWidth; cx = width / 2;
height = window.innerHeight; cy = height / 2;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
const wsize = getRendererSize();
wWidth = wsize[0]; wHeight = wsize[1];
}
function getRendererSize() {
const cam = new THREE.PerspectiveCamera(camera.fov, camera.aspect);
const vFOV = (cam.fov * Math.PI) / 180;
const height = 2 * Math.tan(vFOV / 2) * Math.abs(conf.cameraZ);
const width = height * cam.aspect;
return [width, height];
}
}
const RippleEffect = (function () {
function RippleEffect(renderer, width, height, ratio) {
this.renderer = renderer;
this.width = Math.round(ratio * width);
this.height = Math.round(ratio * height);
this.delta = new THREE.Vector2(ratio / width, ratio / height);
this.hMap = new THREE.WebGLRenderTarget(this.width, this.height, { type: THREE.FloatType, depthBuffer: false, stencilBuffer: false });
this.hMap1 = new THREE.WebGLRenderTarget(this.width, this.height, { type: THREE.FloatType, depthBuffer: false, stencilBuffer: false });
this.fsQuad = new FullScreenQuad();
this.initShaders();
}
// From https://github.com/evanw/webgl-water
RippleEffect.prototype.initShaders = function () {
// default vertex shader
const defaultVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
this.copyMat = new THREE.ShaderMaterial({
uniforms: { 'tDiffuse': { value: null } },
vertexShader: defaultVertexShader,
fragmentShader: `
uniform sampler2D tDiffuse;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(tDiffuse, vUv);
}
`,
});
this.updateMat = new THREE.ShaderMaterial({
uniforms: {
'tDiffuse': { value: null },
'delta': new THREE.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.dropMat = new THREE.ShaderMaterial({
uniforms: {
'tDiffuse': { value: null },
'center': new THREE.Uniform(new THREE.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;
}
`,
});
};
RippleEffect.prototype.update = function () {
this.updateHMap();
};
RippleEffect.prototype.updateHMap = function () {
this.updateMat.uniforms.tDiffuse.value = this.hMap.texture;
this.renderShaderMat(this.updateMat, this.hMap1);
this.swapBuffers();
};
RippleEffect.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();
};
RippleEffect.prototype.renderBuffer = function (buffer, target) {
target = target ? target : null;
this.copyMat.uniforms.tDiffuse.value = buffer.texture;
this.renderShaderMat(this.copyMat, target);
};
RippleEffect.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);
};
RippleEffect.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 THREE.OrthographicCamera(- 1, 1, 1, - 1, 0, 1);
const geometry = new THREE.PlaneBufferGeometry(2, 2);
const FullScreenQuad = function (material) {
this._mesh = new THREE.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;
})();
return RippleEffect;
})();
App();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.js"></script>
<script src="https://klevron.github.io/codepen/three.js/OrbitControls.110.js"></script>
html, body {
margin: 0;
}
canvas {
display: block;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment