THREE.JS | GLSL Set particle colour by scene texture - javascript
I have matrix of GLSL particles (THREE.Points), which have to be coloured by overall scene texture.
So, the result should be like this:
Yeah, I can create canvas and set buffer.color by ctx.getImageData(left, top, width, height), but is it possible to set it inside shader?
I have tried to get particle 'global' position, but without any success.
The output is:
var blob = "data:image/png;base64,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eG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0yOVQyMjozODowNCswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDQtMjlUMjI6Mzg6MDQrMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRlNDg0MDNlLTU2MzUtNDJjOC05MWM0LWY2ZTgyMzM5NDA4OSIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MjliNmNjMy01Y2Y3LTQzOWUtYTA1YS1lOTNhN2FjYzg2OWEiIHN0RXZ0OndoZW49IjIwMTktMDEtMjFUMTU6MzM6NDErMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTI5VDIyOjM4OjA0KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x4suaAAAA5FJREFUeJzt20uLHFUYxvHf1KgkKk4wMCEIUcGoMSAGyeBK8RLwsjFZ6CfwAuJ1bbI0++gmfgNduDOB6Ci6iYwKLtSxTSAmoHEGRm3BZBidlIszHXvavlR3ddWZrp4/NFRXV9V53rdP3d7LRJqmSuBa3Im7sBO3YBu2rP2+jD/wMy6ihh/xd9HCrinw2DvwKB7E/diDGzLu+xfm8TU+xywWCtBoooAZ8CyewZP++4fzsowTeB/vDemYGJ4DbsRLeAG3D+OAXTiH43gbl3IfLU3TvJ9X0zRdSMvn17Wxc+nPMwMewDHsz/0v5ONLvIIvBtk5GXDQN3FafOMJGk4Lmvqm3xlwHT7AU4MMVgIf4hBWsu7QjwN24lPhXr6RqeFh4XmiJ1kdcCvmMD24rlJZxAzO99owiwOm8T2259dVKku4R3BGR3o5YBJnFH9vL4pz2I3VThv0ugucNLrGE7Sf7LZBNwccxoGhyonDAcGWtnQ6Bfbi26IURWKvcC1bRycH1ITX1ypxRhub2p0Cz7fbsALsxoutK1tnwCTqsr+3jxqXMIV/GitaZ8Brqms8XC/YeJXWGbCEm0sUFIPfND3UNc+Ap1XfeIKNBxtfmh3wvwtEhXmusdA4BabwOyZiKSqZK8JMqDdmwOPGx3jCzH+isUAIX48bjxAcMCm8O48bM5icSNN0D74Rwl3jxAr2JULGZtyMJ9h8d4JdsZVEZFdidOJ8RTCdCFnacWVbgq2xVURk66CZoapwJcHl2CoispwIlRnjSj3RI3FQcRYTXIitIiIXEqEWJ3M2tUKs4IdEqMaajywmBvOoJULebC6ymBjMYbXxHDAbU0kkZtkMiV0NidVxKp6e0jkl2LwuKnw8jpYovNtY2EyMtPz4VrlaonC0+Uu75OifQg6tilzGTbokR1fxepmKSuYNTcYzXgUSZ4UagXV0CogcKlZLFA62W9nJAd/hSHFaSueIDjVPveoEP8JjRSgqkY91qXbLUih5FrcNV1Np/IQ75CiUXBX6ApaGp6k0lgTtHY0nW7/AgtD0NEqhs0VBc89Gq6xh8fO4T7g9bnRq2CdDpTj9dYxcxL1C99ZG5YSg8ZesO/SbGFkRukU61t5G5LCgra/4ZhWapr7Cy0pumrI24IxQeBjjArmwNvZ+AxrPaDdOviO02OZis3W2AAc02CFUYj0kX/P0Z/jECDVPt6Nd+/yU9e3zdRHa5/8FGLnMg5/e7DMAAAAASUVORK5CYII=";
var pattern = "data:image/jpeg;base64,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AABSZ3NNYm9vbAAAAAAAQ3JuQ2Jvb2wAAAAAAENudENib29sAAAAAABMYmxzYm9vbAAAAAAATmd0dmJvb2wAAAAAAEVtbERib29sAAAAAABJbnRyYm9vbAAAAAAAQmNrZ09iamMAAAABAAAAAAAAUkdCQwAAAAMAAAAAUmQgIGRvdWJAb+AAAAAAAAAAAABHcm4gZG91YkBv4AAAAAAAAAAAAEJsICBkb3ViQG/gAAAAAAAAAAAAQnJkVFVudEYjUmx0AAAAAAAAAAAAAAAAQmxkIFVudEYjUmx0AAAAAAAAAAAAAAAAUnNsdFVudEYjUHhsQFIAAAAAAAAAAAAKdmVjdG9yRGF0YWJvb2wBAAAAAFBnUHNlbnVtAAAAAFBnUHMAAAAAUGdQQwAAAABMZWZ0VW50RiNSbHQAAAAAAAAAAAAAAABUb3AgVW50RiNSbHQAAAAAAAAAAAAAAABTY2wgVW50RiNQcmNAWQAAAAAAAAAAABBjcm9wV2hlblByaW50aW5nYm9vbAAAAAAOY3JvcFJlY3RCb3R0b21sb25nAAAAAAAAAAxjcm9wUmVjdExlZnRsb25nAAAAAAAAAA1jcm9wUmVjdFJpZ2h0bG9uZwAAAAAAAAALY3JvcFJlY3RUb3Bsb25nAAAAAAA4QklNA+0AAAAAABAASAAAAAEAAgBIAAAAAQACOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD+AAAA4QklNA/IAAAAAAAoAAP///////wAAOEJJTQQNAAAAAAAEAAAAWjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAAAAAEAAAAADhC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aWZ5RGF0ZT0iMjAyMC0wNS0wMVQxMjo0NDoyNSswMzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5NjU5MzU1MC1mNGVhLTAyNGUtOWM1YS0zMmNiYjNjMmQwZmYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzM0N2QzZi1iZTA0LTQxNTQtODBlMS00ZWVhNDVmMTM0MDQiIGRjOmZvcm1hdD0iaW1hZ2UvanBlZyIgcGhvdG9zaG9wOkxlZ2FjeUlQVENEaWdlc3Q9IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iRGlzcGxheSBQMyI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDMzNDdkM2YtYmUwNC00MTU0LTgwZTEtNGVlYTQ1ZjEzNDA0IiBzdEV2dDp3aGVuPSIyMDIwLTA1LTAxVDEyOjIyOjA5KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc3MDM5N2M0LTI3YTItNDM4ZS05MzZiLTAwMzA2MTA2YjAwMSIgc3RFdnQ6d2hlbj0iMjAyMC0wNS0wMVQxMjoyMjowOSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDFUMTI6NDQ6MjUrMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/Pv/iAjRJQ0NfUFJPRklMRQABAQAAAiRhcHBsBAAAAG1udHJSR0IgWFlaIAfhAAcABwANABYAIGFjc3BBUFBMAAAAAEFQUEwAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbMoalYIlfxBNOJkT1dHqFYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmRlc2MAAAD8AAAAZWNwcnQAAAFkAAAAI3d0cHQAAAGIAAAAFHJYWVoAAAGcAAAAFGdYWVoAAAGwAAAAFGJYWVoAAAHEAAAAFHJUUkMAAAHYAAAAIGNoYWQAAAH4AAAALGJUUkMAAAHYAAAAIGdUUkMAAAHYAAAAIGRlc2MAAAAAAAAAC0Rpc3BsYXkgUDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxNwAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAg98A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";
var width = 512,
height = 512;
inits();
function inits(){
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
scene.add(camera);
var N = 8.0;
var points = [];
var colors = [];
for(var y = 16; y <= height - 16; y += N){
for(var x = 16; x <= width - 16; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
buffer = {
position: new Float32Array(points.length * 3),
color: new Float32Array(points.length * 3),
radius: new Float32Array(points.length * 1),
};
for(var i = 0; i < points.length; i++){
buffer.position[i * 3] = points[i].x;
buffer.position[i * 3 + 1] = points[i].y;
buffer.position[i * 3 + 2] = 0;
buffer.radius[i] = N * 0.5;
buffer.color[i * 3] = 1.0;
buffer.color[i * 3 + 1] = 0.0;
buffer.color[i * 3 + 2] = 1.0;
}
uniforms = {
blob: { value: new THREE.TextureLoader().load(blob) },
pattern: { value: new THREE.TextureLoader().load(pattern) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
} );
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var cloud = new THREE.BufferGeometry();
cloud.addAttribute('position', new THREE.BufferAttribute(buffer.position, 3));
cloud.addAttribute('radius', new THREE.BufferAttribute(buffer.radius, 1));
cloud.addAttribute('color', new THREE.BufferAttribute(buffer.color, 3));
var points = new THREE.Points(cloud, material);
points.dynamic = true;
points.frustumCulled = false;
scene.add(points);
animate();
}
function animate() {
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PixelWordMap Demo</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<meta name="author" content="Vladimir V. KUCHINOV">
<link rel="stylesheet" href="styles.css" charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.js"></script>
</head>
<body>
<script type='x-shader/x-vertex' id='vertexShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
attribute float radius;
attribute vec3 color;
varying vec3 vColor;
varying vec2 pos;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = radius;
pos = vec2(1.0 + position.x / 2.0, 1.0 + position.y / 2.0);
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragmentShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
varying vec3 vColor;
varying vec2 pos;
void main() {
vec4 color = texture2D(pattern, vec2(pos.x, pos.y));
gl_FragColor = color * texture2D(blob, gl_PointCoord);
if(gl_FragColor.a < 0.1) discard;
}
</script>
</body>
</html>
The vertex shader output varying vec2 pos; is never assigned a value, because the position is assigned to the local variable pos with the same name: vec2 pos = vec2(...);.
Furthermore you missed the braces, to prioritized the + operation:
pos = (position.xy + 1.0) / 2.0;
Choose a different number for N to get a different number of points:
var N = 32;
for(var y = N/2; y <= height; y += N){
for(var x = N/2; x <= width; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
Note, the point size is limited. See Working around gl_PointSize limitations in three.js / webGL.
var blob = "data:image/png;base64,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eG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0yOVQyMjozODowNCswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDQtMjlUMjI6Mzg6MDQrMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRlNDg0MDNlLTU2MzUtNDJjOC05MWM0LWY2ZTgyMzM5NDA4OSIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MjliNmNjMy01Y2Y3LTQzOWUtYTA1YS1lOTNhN2FjYzg2OWEiIHN0RXZ0OndoZW49IjIwMTktMDEtMjFUMTU6MzM6NDErMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTI5VDIyOjM4OjA0KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x4suaAAAA5FJREFUeJzt20uLHFUYxvHf1KgkKk4wMCEIUcGoMSAGyeBK8RLwsjFZ6CfwAuJ1bbI0++gmfgNduDOB6Ci6iYwKLtSxTSAmoHEGRm3BZBidlIszHXvavlR3ddWZrp4/NFRXV9V53rdP3d7LRJqmSuBa3Im7sBO3YBu2rP2+jD/wMy6ihh/xd9HCrinw2DvwKB7E/diDGzLu+xfm8TU+xywWCtBoooAZ8CyewZP++4fzsowTeB/vDemYGJ4DbsRLeAG3D+OAXTiH43gbl3IfLU3TvJ9X0zRdSMvn17Wxc+nPMwMewDHsz/0v5ONLvIIvBtk5GXDQN3FafOMJGk4Lmvqm3xlwHT7AU4MMVgIf4hBWsu7QjwN24lPhXr6RqeFh4XmiJ1kdcCvmMD24rlJZxAzO99owiwOm8T2259dVKku4R3BGR3o5YBJnFH9vL4pz2I3VThv0ugucNLrGE7Sf7LZBNwccxoGhyonDAcGWtnQ6Bfbi26IURWKvcC1bRycH1ITX1ypxRhub2p0Cz7fbsALsxoutK1tnwCTqsr+3jxqXMIV/GitaZ8Brqms8XC/YeJXWGbCEm0sUFIPfND3UNc+Ap1XfeIKNBxtfmh3wvwtEhXmusdA4BabwOyZiKSqZK8JMqDdmwOPGx3jCzH+isUAIX48bjxAcMCm8O48bM5icSNN0D74Rwl3jxAr2JULGZtyMJ9h8d4JdsZVEZFdidOJ8RTCdCFnacWVbgq2xVURk66CZoapwJcHl2CoispwIlRnjSj3RI3FQcRYTXIitIiIXEqEWJ3M2tUKs4IdEqMaajywmBvOoJULebC6ymBjMYbXxHDAbU0kkZtkMiV0NidVxKp6e0jkl2LwuKnw8jpYovNtY2EyMtPz4VrlaonC0+Uu75OifQg6tilzGTbokR1fxepmKSuYNTcYzXgUSZ4UagXV0CogcKlZLFA62W9nJAd/hSHFaSueIDjVPveoEP8JjRSgqkY91qXbLUih5FrcNV1Np/IQ75CiUXBX6ApaGp6k0lgTtHY0nW7/AgtD0NEqhs0VBc89Gq6xh8fO4T7g9bnRq2CdDpTj9dYxcxL1C99ZG5YSg8ZesO/SbGFkRukU61t5G5LCgra/4ZhWapr7Cy0pumrI24IxQeBjjArmwNvZ+AxrPaDdOviO02OZis3W2AAc02CFUYj0kX/P0Z/jECDVPt6Nd+/yU9e3zdRHa5/8FGLnMg5/e7DMAAAAASUVORK5CYII=";
var pattern = "data:image/jpeg;base64,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AABSZ3NNYm9vbAAAAAAAQ3JuQ2Jvb2wAAAAAAENudENib29sAAAAAABMYmxzYm9vbAAAAAAATmd0dmJvb2wAAAAAAEVtbERib29sAAAAAABJbnRyYm9vbAAAAAAAQmNrZ09iamMAAAABAAAAAAAAUkdCQwAAAAMAAAAAUmQgIGRvdWJAb+AAAAAAAAAAAABHcm4gZG91YkBv4AAAAAAAAAAAAEJsICBkb3ViQG/gAAAAAAAAAAAAQnJkVFVudEYjUmx0AAAAAAAAAAAAAAAAQmxkIFVudEYjUmx0AAAAAAAAAAAAAAAAUnNsdFVudEYjUHhsQFIAAAAAAAAAAAAKdmVjdG9yRGF0YWJvb2wBAAAAAFBnUHNlbnVtAAAAAFBnUHMAAAAAUGdQQwAAAABMZWZ0VW50RiNSbHQAAAAAAAAAAAAAAABUb3AgVW50RiNSbHQAAAAAAAAAAAAAAABTY2wgVW50RiNQcmNAWQAAAAAAAAAAABBjcm9wV2hlblByaW50aW5nYm9vbAAAAAAOY3JvcFJlY3RCb3R0b21sb25nAAAAAAAAAAxjcm9wUmVjdExlZnRsb25nAAAAAAAAAA1jcm9wUmVjdFJpZ2h0bG9uZwAAAAAAAAALY3JvcFJlY3RUb3Bsb25nAAAAAAA4QklNA+0AAAAAABAASAAAAAEAAgBIAAAAAQACOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD+AAAA4QklNA/IAAAAAAAoAAP///////wAAOEJJTQQNAAAAAAAEAAAAWjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAAAAAEAAAAADhC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aWZ5RGF0ZT0iMjAyMC0wNS0wMVQxMjo0NDoyNSswMzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5NjU5MzU1MC1mNGVhLTAyNGUtOWM1YS0zMmNiYjNjMmQwZmYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzM0N2QzZi1iZTA0LTQxNTQtODBlMS00ZWVhNDVmMTM0MDQiIGRjOmZvcm1hdD0iaW1hZ2UvanBlZyIgcGhvdG9zaG9wOkxlZ2FjeUlQVENEaWdlc3Q9IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iRGlzcGxheSBQMyI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDMzNDdkM2YtYmUwNC00MTU0LTgwZTEtNGVlYTQ1ZjEzNDA0IiBzdEV2dDp3aGVuPSIyMDIwLTA1LTAxVDEyOjIyOjA5KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc3MDM5N2M0LTI3YTItNDM4ZS05MzZiLTAwMzA2MTA2YjAwMSIgc3RFdnQ6d2hlbj0iMjAyMC0wNS0wMVQxMjoyMjowOSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDFUMTI6NDQ6MjUrMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/Pv/iAjRJQ0NfUFJPRklMRQABAQAAAiRhcHBsBAAAAG1udHJSR0IgWFlaIAfhAAcABwANABYAIGFjc3BBUFBMAAAAAEFQUEwAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbMoalYIlfxBNOJkT1dHqFYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmRlc2MAAAD8AAAAZWNwcnQAAAFkAAAAI3d0cHQAAAGIAAAAFHJYWVoAAAGcAAAAFGdYWVoAAAGwAAAAFGJYWVoAAAHEAAAAFHJUUkMAAAHYAAAAIGNoYWQAAAH4AAAALGJUUkMAAAHYAAAAIGdUUkMAAAHYAAAAIGRlc2MAAAAAAAAAC0Rpc3BsYXkgUDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxNwAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAg98A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";
var width = 512,
height = 512;
inits();
function inits(){
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
scene.add(camera);
var points = [];
var colors = [];
var N = 32;
for(var y = N/2; y <= height; y += N){
for(var x = N/2; x <= width; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
buffer = {
position: new Float32Array(points.length * 3),
color: new Float32Array(points.length * 3),
radius: new Float32Array(points.length * 1),
};
for(var i = 0; i < points.length; i++){
buffer.position[i * 3] = points[i].x;
buffer.position[i * 3 + 1] = points[i].y;
buffer.position[i * 3 + 2] = 0;
buffer.radius[i] = N * 0.5;
buffer.color[i * 3] = 1.0;
buffer.color[i * 3 + 1] = 0.0;
buffer.color[i * 3 + 2] = 1.0;
}
uniforms = {
blob: { value: new THREE.TextureLoader().load(blob) },
pattern: { value: new THREE.TextureLoader().load(pattern) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
} );
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var cloud = new THREE.BufferGeometry();
cloud.addAttribute('position', new THREE.BufferAttribute(buffer.position, 3));
cloud.addAttribute('radius', new THREE.BufferAttribute(buffer.radius, 1));
cloud.addAttribute('color', new THREE.BufferAttribute(buffer.color, 3));
var points = new THREE.Points(cloud, material);
points.dynamic = true;
points.frustumCulled = false;
scene.add(points);
animate();
}
function animate() {
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script type='x-shader/x-vertex' id='vertexShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
attribute float radius;
attribute vec3 color;
varying vec3 vColor;
varying vec2 pos;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = radius;
pos = (position.xy + 1.0) / 2.0;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragmentShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
varying vec3 vColor;
varying vec2 pos;
void main() {
vec4 color = texture2D(pattern, vec2(pos.x, pos.y));
gl_FragColor = color * texture2D(blob, gl_PointCoord);
if(gl_FragColor.a < 0.1) discard;
}
</script>
Related
how do i make the html code appear on top of the animation and not behind it
I tried putting this in CSS p{ z-index: 99 !important; } it didn't work I Just want to make everything in html appear on top of the animation here is the code:: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> </head> <body> <div id="container"> <p>hellpo</p> </div> <script id="vertex-shader" type="no-js"> void main() { gl_Position = vec4( position, 1.0 ); } </script> <script id="fragment-shader" type="no-js"> uniform float iGlobalTime; uniform vec2 iResolution; const int NUM_STEPS = 8; const float PI = 3.1415; const float EPSILON = 1e-3; float EPSILON_NRM = 0.1 / iResolution.x; // sea variables const int ITER_GEOMETRY = 3; const int ITER_FRAGMENT = 5; const float SEA_HEIGHT = 0.6; const float SEA_CHOPPY = 1.0; const float SEA_SPEED = 1.0; const float SEA_FREQ = 0.16; const vec3 SEA_BASE = vec3(0.1,0.19,0.22); const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6); float SEA_TIME = iGlobalTime * SEA_SPEED; mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); mat3 fromEuler(vec3 ang) { vec2 a1 = vec2(sin(ang.x),cos(ang.x)); vec2 a2 = vec2(sin(ang.y),cos(ang.y)); vec2 a3 = vec2(sin(ang.z),cos(ang.z)); mat3 m; m[0] = vec3( a1.y*a3.y+a1.x*a2.x*a3.x, a1.y*a2.x*a3.x+a3.y*a1.x, -a2.y*a3.x ); m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x); m[2] = vec3( a3.y*a1.x*a2.x+a1.y*a3.x, a1.x*a3.x-a1.y*a3.y*a2.x, a2.y*a3.y ); return m; } float hash( vec2 p ) { float h = dot(p,vec2(127.1,311.7)); return fract(sin(h)*43758.5453123); } float noise( in vec2 p ) { vec2 i = floor(p); vec2 f = fract(p); vec2 u = f * f * (3.0 - 2.0 * f); return -1.0 + 2.0 * mix( mix( hash(i + vec2(0.0,0.0) ), hash(i + vec2(1.0,0.0)), u.x), mix(hash(i + vec2(0.0,1.0) ), hash(i + vec2(1.0,1.0) ), u.x), u.y ); } float diffuse(vec3 n,vec3 l,float p) { return pow(dot(n,l) * 0.4 + 0.6,p); } float specular(vec3 n,vec3 l,vec3 e,float s) { float nrm = (s + 8.0) / (3.1415 * 8.0); return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; } vec3 getSkyColor(vec3 e) { e.y = max(e.y, 0.0); vec3 ret; ret.x = pow(1.0 - e.y, 2.0); ret.y = 1.0 - e.y; ret.z = 0.6+(1.0 - e.y) * 0.4; return ret; } float sea_octave(vec2 uv, float choppy) { uv += noise(uv); vec2 wv = 1.0 - abs(sin(uv)); vec2 swv = abs(cos(uv)); wv = mix(wv, swv, wv); return pow(1.0 - pow(wv.x * wv.y, 0.65), choppy); } float map(vec3 p) { float freq = SEA_FREQ; float amp = SEA_HEIGHT; float choppy = SEA_CHOPPY; vec2 uv = p.xz; uv.x *= 0.75; float d, h = 0.0; for(int i = 0; i < ITER_GEOMETRY; i++) { d = sea_octave((uv + SEA_TIME) * freq, choppy); d += sea_octave((uv - SEA_TIME) * freq, choppy); h += d * amp; uv *= octave_m; freq *= 1.9; amp *= 0.22; choppy = mix(choppy, 1.0, 0.2); } return p.y - h; } float map_detailed(vec3 p) { float freq = SEA_FREQ; float amp = SEA_HEIGHT; float choppy = SEA_CHOPPY; vec2 uv = p.xz; uv.x *= 0.75; float d, h = 0.0; for(int i = 0; i < ITER_FRAGMENT; i++) { d = sea_octave((uv+SEA_TIME) * freq, choppy); d += sea_octave((uv-SEA_TIME) * freq, choppy); h += d * amp; uv *= octave_m; freq *= 1.9; amp *= 0.22; choppy = mix(choppy,1.0,0.2); } return p.y - h; } vec3 getSeaColor( vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist ) { float fresnel = 1.0 - max(dot(n,-eye),0.0); fresnel = pow(fresnel,3.0) * 0.65; vec3 reflected = getSkyColor(reflect(eye,n)); vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; vec3 color = mix(refracted,reflected,fresnel); float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; color += vec3(specular(n,l,eye,60.0)); return color; } // tracing vec3 getNormal(vec3 p, float eps) { vec3 n; n.y = map_detailed(p); n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; n.y = eps; return normalize(n); } float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) { float tm = 0.0; float tx = 1000.0; float hx = map(ori + dir * tx); if(hx > 0.0) { return tx; } float hm = map(ori + dir * tm); float tmid = 0.0; for(int i = 0; i < NUM_STEPS; i++) { tmid = mix(tm,tx, hm/(hm-hx)); p = ori + dir * tmid; float hmid = map(p); if(hmid < 0.0) { tx = tmid; hx = hmid; } else { tm = tmid; hm = hmid; } } return tmid; } void main() { vec2 uv = gl_FragCoord.xy / iResolution.xy; uv = uv * 2.0 - 1.0; uv.x *= iResolution.x / iResolution.y; float time = iGlobalTime * 0.3; // ray vec3 ang = vec3( sin(time*3.0)*0.1,sin(time)*0.2+0.3,time ); vec3 ori = vec3(0.0,3.5,time*5.0); vec3 dir = normalize( vec3(uv.xy,-2.0) ); dir.z += length(uv) * 0.15; dir = normalize(dir); // tracing vec3 p; heightMapTracing(ori,dir,p); vec3 dist = p - ori; vec3 n = getNormal( p, dot(dist,dist) * EPSILON_NRM ); vec3 light = normalize(vec3(0.0,1.0,0.8)); // color vec3 color = mix( getSkyColor(dir), getSeaColor(p,n,light,dir,dist), pow(smoothstep(0.0,-0.05,dir.y),0.3) ); // post gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0); } </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script> <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> <script src="script.js"></script> </body> </html> css body { overflow: hidden; margin: 0; } p{ z-index: 99 !important; } JavaScript var container, renderer, scene, camera, mesh, start = Date.now(), fov = 30; var clock = new THREE.Clock(); var timeUniform = { iGlobalTime: { type: 'f', value: 0.1 }, iResolution: { type: 'v2', value: new THREE.Vector2() } }; timeUniform.iResolution.value.x = window.innerWidth; timeUniform.iResolution.value.y = window.innerHeight; window.addEventListener('load', function() { container = document.getElementById('container'); scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.x = 20; camera.position.y = 10; camera.position.z = 20; camera.lookAt(scene.position); scene.add(camera); var axis = new THREE.AxisHelper(10); scene.add (axis); material = new THREE.ShaderMaterial({ uniforms: timeUniform, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent }); var water = new THREE.Mesh( new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight, 40), material ); scene.add(water); var geometry = new THREE.SphereGeometry( 10, 32, 32 ); var material = new THREE.MeshBasicMaterial( {color: 0xffff00} ); var sphere = new THREE.Mesh( geometry, material ); scene.add( sphere ); renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild( renderer.domElement ); render(); }); window.addEventListener('resize',function() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); function render() { timeUniform.iGlobalTime.value += clock.getDelta(); renderer.render(scene, camera); requestAnimationFrame(render); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
you can wrap ur animation in a div(your ThreeJs canvas). give the div and add this styles to it position: absolute; top: 0; left: 0; z-index: -1; and make #container position: relative
How to add a displacement map to a point material in ThreeJS?
I'm new to ThreeJS and trying to create something like asteroids with textures made of particles (something like from this website https://staratlas.com/). Unfortunately, when I tried to apply a displacement map to a points material nothing worked. How can I resolve this issue? const dispTexture = new THREE.TextureLoader().load('NormalMap.jpg'); const sphere = new THREE.Points( new THREE.SphereGeometry(3, 32, 32), new THREE.PointsMaterial({ size: 0.07, color: 0xFF325F, displacementMap: dispTexture, displacementScale : 0.2, }) ); scene.add(sphere);
The option, mentioned in comments, with the using of noise. An asteroid of 30K points. Added a simple lighting: the more light on a point, the bigger and brighter it is. Just in case, codepen: https://codepen.io/prisoner849/pen/mdBzjBy Maybe it'll be helpful for somebody: body{ overflow: hidden; margin: 0; } <script> simplexNoise = ` // Simplex 3D Noise // by Ian McEwan, Ashima Arts // vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);} vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} float snoise(vec3 v){ const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); // First corner vec3 i = floor(v + dot(v, C.yyy) ); vec3 x0 = v - i + dot(i, C.xxx) ; // Other corners vec3 g = step(x0.yzx, x0.xyz); vec3 l = 1.0 - g; vec3 i1 = min( g.xyz, l.zxy ); vec3 i2 = max( g.xyz, l.zxy ); // x0 = x0 - 0. + 0.0 * C vec3 x1 = x0 - i1 + 1.0 * C.xxx; vec3 x2 = x0 - i2 + 2.0 * C.xxx; vec3 x3 = x0 - 1. + 3.0 * C.xxx; // Permutations i = mod(i, 289.0 ); vec4 p = permute( permute( permute( i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); // Gradients // ( N*N points uniformly over a square, mapped onto an octahedron.) float n_ = 1.0/7.0; // N=7 vec3 ns = n_ * D.wyz - D.xzx; vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N) vec4 x_ = floor(j * ns.z); vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) vec4 x = x_ *ns.x + ns.yyyy; vec4 y = y_ *ns.x + ns.yyyy; vec4 h = 1.0 - abs(x) - abs(y); vec4 b0 = vec4( x.xy, y.xy ); vec4 b1 = vec4( x.zw, y.zw ); vec4 s0 = floor(b0)*2.0 + 1.0; vec4 s1 = floor(b1)*2.0 + 1.0; vec4 sh = -step(h, vec4(0.0)); vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; vec3 p0 = vec3(a0.xy,h.x); vec3 p1 = vec3(a0.zw,h.y); vec3 p2 = vec3(a1.xy,h.z); vec3 p3 = vec3(a1.zw,h.w); //Normalise gradients vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; // Mix final noise value vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); m = m * m; return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) ); } `; </script> <script type="module"> import * as THREE from "https://cdn.skypack.dev/three#0.136.0"; import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls"; let scene = new THREE.Scene(); let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000); camera.position.set(0, 0, 10); let renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(innerWidth, innerHeight); //renderer.setClearColor(0xffffff) document.body.appendChild(renderer.domElement); window.addEventListener("resize", event => { camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); }) let controls = new OrbitControls(camera, renderer.domElement); let light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(1, 1, 0); scene.add(light, new THREE.AmbientLight(0xffffff, 0.5)); //scene.add(new THREE.GridHelper()); let pts = new Array(30000).fill().map(p => { return new THREE.Vector3().randomDirection().multiplyScalar(4); }); let g = new THREE.BufferGeometry().setFromPoints(pts); let u = { time: {value: 0}, lightPos: {value: new THREE.Vector3()} } let m = new THREE.PointsMaterial({ size: 0.075, color: 0x7fffff, //map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png"), onBeforeCompile: shader => { shader.uniforms.lightPos = u.lightPos; shader.vertexShader = ` uniform float time; // just the force of habit to add it :) uniform vec3 lightPos; varying float vShade; ${simplexNoise} float turbulence( vec3 p ) { float w = 100.0; float t = -.5; for (float f = 1.0 ; f <= 10.0 ; f++ ){ float power = pow( 2.0, f ); t += snoise( vec3( power * p ) ) / power ; } return t; } vec3 setFromSphericalCoords( float radius, float phi, float theta ) { float sinPhiRadius = sin( phi ) * radius; vec3 v = vec3( sinPhiRadius * sin( theta ), cos( phi ) * radius, sinPhiRadius * cos( theta ) ); return v; } vec2 setFromCartesianCoords( vec3 v ) { float radius = sqrt( v.x * v.x + v.y * v.y + v.z * v.z ); float theta = 0.; float phi = 0.; if ( radius != 0. ) { theta = atan( v.x, v.z ); phi = acos( clamp( v.y / radius, - 1., 1. ) ); } return vec2(phi, theta); } vec3 getPoint(vec3 p){ vec3 n = normalize(p); float s = turbulence(n * 0.5); return p + n * s; } ${shader.vertexShader} `.replace( `#include <begin_vertex>`, `#include <begin_vertex> vec3 p0 = getPoint(position); vec2 spherical = setFromCartesianCoords(position); vec2 s = vec2(0.01, 0.); vec3 p1 = setFromSphericalCoords(length(position), spherical.x + s.x, spherical.y + s.y); vec3 p2 = setFromSphericalCoords(length(position), spherical.x + s.y, spherical.y + s.x); p1 = getPoint(p1); p2 = getPoint(p2); vec3 nor = normalize(cross(p1 - p0, p2 - p0)); transformed = p0; ` ).replace( `gl_PointSize = size;`, ` vec3 lightDir = normalize(lightPos); float shade = clamp(dot(nor, lightDir), 0., 1.); vShade = shade; gl_PointSize = size + (shade * size);` ); console.log(shader.vertexShader); shader.fragmentShader = ` varying float vShade; ${shader.fragmentShader} `.replace( `vec4 diffuseColor = vec4( diffuse, opacity );`, ` if(length(gl_PointCoord - 0.5) > 0.5) discard; // make'em round float shade = vShade * 0.5 + 0.5; vec4 diffuseColor = vec4( diffuse * shade, opacity );` ); console.log(shader.fragmentShader); } }); let p = new THREE.Points(g, m); scene.add(p); let clock = new THREE.Clock(); renderer.setAnimationLoop(() => { let t = clock.getElapsedTime() * 0.5; p.rotation.x = t * 0.271; p.rotation.y = t * 0.314; p.rotation.z = t * 0.158; p.worldToLocal(u.lightPos.value.copy(light.position).add(p.position)); renderer.render(scene, camera); }) </script>
Responsive canvas using webgl and meatballs.js
I'm attempting to implement this codepen as a background of my personal website. I have no real knowledge of WebGL, so please bear with me. I temporarily added an event listener to update the width and height of the canvas when the page is resized. I can tell this works because when the bubbles start going out of bounds, they continue going and don't bounce off the edge of the page, so I know it somewhat works the way I want it to. When the fragment shader source is defined it also defines the width and height and I'm not sure how to change those variables after that. I tried redefining, recompiling, and reattaching the fragment shader source with the new widths and heights. This obviously doesn't work because the bubbles do not render past the size of the page when the canvas was created. I'm not sure if i'm even going about this the right way, if so what am I doing wrong? All/any help is appreciated, thank you. The code I changed: var canvas = document.createElement("canvas"); var width = canvas.width = window.innerWidth * 0.75; var height = canvas.height = window.innerHeight * 0.75; document.body.appendChild(canvas); var gl = canvas.getContext('webgl'); var mouse = {x: 0, y: 0}; var numMetaballs = 30; var metaballs = []; var first = true window.addEventListener('resize', function(){ width = canvas.width = window.innerWidth * 0.75; height = canvas.height = window.innerHeight * 0.75; shaderStuff() }) function shaderStuff(){ if(!first) { gl.detachShader(program, gl.getAttachedShaders(program)[1]) } first = false var fragmentShaderSrc = ` precision highp float; const float WIDTH = ` + (width >> 0) + `.0; const float HEIGHT = ` + (height >> 0) + `.0; uniform vec3 metaballs[` + numMetaballs + `]; void main(){ float x = gl_FragCoord.x; float y = gl_FragCoord.y; float sum = 0.0; for (int i = 0; i < ` + numMetaballs + `; i++) { vec3 metaball = metaballs[i]; float dx = metaball.x - x; float dy = metaball.y - y; float radius = metaball.z; sum += (radius * radius) / (dx * dx + dy * dy); } if (sum >= 0.99) { gl_FragColor = vec4(mix(vec3(x / WIDTH, y / HEIGHT, 1.0), vec3(0, 0, 0), max(0.0, 1.0 - (sum - 0.99) * 100.0)), 1.0); return; } gl_FragColor = vec4(0, 0, 0, 0); } `; var fragmentShader = compileShader(fragmentShaderSrc, gl.FRAGMENT_SHADER); gl.attachShader(program, fragmentShader); } for (var i = 0; i < numMetaballs; i++) { var radius = Math.random() * 60 + 10; metaballs.push({ x: Math.random() * (width - 2 * radius) + radius, y: Math.random() * (height - 2 * radius) + radius, vx: (Math.random() - 0.5) * 3, vy: (Math.random() - 0.5) * 3, r: radius * 0.75 }); } var vertexShaderSrc = ` attribute vec2 position; void main() { // position specifies only x and y. // We set z to be 0.0, and w to be 1.0 gl_Position = vec4(position, 0.0, 1.0); } `; var vertexShader = compileShader(vertexShaderSrc, gl.VERTEX_SHADER); var program = gl.createProgram(); gl.attachShader(program, vertexShader); shaderStuff() gl.linkProgram(program); gl.useProgram(program); The whole project https://meatballsjs.000webhostapp.com/ The original https://codepen.io/TC5550/pen/WNNWoaO
The easiest way would be to put all of the background creation code in a function, and call it every time the page is resized. You will also need to add some code to cause the previous background loops to stop, and you should add some throttling to prevent too many backgrounds to be created at once. This is somewhat inefficient, but most users don't expect applications to be extremely responsive while they are being resized, and resizing is an infrequent operation. I added a code snippet, which appears to work, however I could not get my changes to work in codepen. I believe this is because codepen instruments and modifies the code in a certain way that breaks it (jsbin has similar behavior to prevent infinite loops, and to sandbox it). However I tested my changes in just a .html file, and they seemed to work there, so they should work on your site. On a side note, very cool use of WebGL! var nextBackgroundId = 1; var currentBackgroundId = 0; setupBackground(currentBackgroundId); window.addEventListener("resize", () => { var ourBackgroundId = nextBackgroundId++; currentBackgroundId = ourBackgroundId; setTimeout(() => { setupBackground(ourBackgroundId); }, 100); }); function setupBackground(ourBackgroundId) { if (currentBackgroundId !== ourBackgroundId) { return; } var prevCanvas = document.getElementById("blob-canvas"); if (prevCanvas) { prevCanvas.remove(); } var canvas = document.createElement("canvas"); canvas.id = "blob-canvas"; var mouse = { x: 0, y: 0 }; canvas.onmousemove = function (e) { mouse.x = e.clientX; mouse.y = e.clientY; } var width = canvas.width = window.innerWidth; var height = canvas.height = window.innerHeight; document.body.appendChild(canvas); var gl = canvas.getContext('webgl'); var numMetaballs = 30; var metaballs = []; for (var i = 0; i < numMetaballs; i++) { var radius = Math.random() * 60 + 10; metaballs.push({ x: Math.random() * (width - 2 * radius) + radius, y: Math.random() * (height - 2 * radius) + radius, vx: (Math.random() - 0.5) * 3, vy: (Math.random() - 0.5) * 3, r: radius * 0.75 }); } var vertexShaderSrc = ` attribute vec2 position; void main() { // position specifies only x and y. // We set z to be 0.0, and w to be 1.0 gl_Position = vec4(position, 0.0, 1.0); } `; var fragmentShaderSrc = ` precision highp float; const float WIDTH = ` + (width >> 0) + `.0; const float HEIGHT = ` + (height >> 0) + `.0; uniform vec3 metaballs[` + numMetaballs + `]; void main(){ float x = gl_FragCoord.x; float y = gl_FragCoord.y; float sum = 0.0; for (int i = 0; i < ` + numMetaballs + `; i++) { vec3 metaball = metaballs[i]; float dx = metaball.x - x; float dy = metaball.y - y; float radius = metaball.z; sum += (radius * radius) / (dx * dx + dy * dy); } if (sum >= 0.99) { gl_FragColor = vec4(mix(vec3(x / WIDTH, y / HEIGHT, 1.0), vec3(0, 0, 0), max(0.0, 1.0 - (sum - 0.99) * 100.0)), 1.0); return; } gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } `; var vertexShader = compileShader(vertexShaderSrc, gl.VERTEX_SHADER); var fragmentShader = compileShader(fragmentShaderSrc, gl.FRAGMENT_SHADER); var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); var vertexData = new Float32Array([ -1.0, 1.0, // top left -1.0, -1.0, // bottom left 1.0, 1.0, // top right 1.0, -1.0, // bottom right ]); var vertexDataBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); var positionHandle = getAttribLocation(program, 'position'); gl.enableVertexAttribArray(positionHandle); gl.vertexAttribPointer(positionHandle, 2, // position is a vec2 gl.FLOAT, // each component is a float gl.FALSE, // don't normalize values 2 * 4, // two 4 byte float components per vertex 0 // offset into each span of vertex data ); var metaballsHandle = getUniformLocation(program, 'metaballs'); loop(); function loop() { if (currentBackgroundId !== ourBackgroundId) { return; } for (var i = 0; i < numMetaballs; i++) { var metaball = metaballs[i]; metaball.x += metaball.vx; metaball.y += metaball.vy; if (metaball.x < metaball.r || metaball.x > width - metaball.r) metaball.vx *= -1; if (metaball.y < metaball.r || metaball.y > height - metaball.r) metaball.vy *= -1; } var dataToSendToGPU = new Float32Array(3 * numMetaballs); for (var i = 0; i < numMetaballs; i++) { var baseIndex = 3 * i; var mb = metaballs[i]; dataToSendToGPU[baseIndex + 0] = mb.x; dataToSendToGPU[baseIndex + 1] = mb.y; dataToSendToGPU[baseIndex + 2] = mb.r; } gl.uniform3fv(metaballsHandle, dataToSendToGPU); //Draw gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); requestAnimationFrame(loop); } function compileShader(shaderSource, shaderType) { var shader = gl.createShader(shaderType); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw "Shader compile failed with: " + gl.getShaderInfoLog(shader); } return shader; } function getUniformLocation(program, name) { var uniformLocation = gl.getUniformLocation(program, name); if (uniformLocation === -1) { throw 'Can not find uniform ' + name + '.'; } return uniformLocation; } function getAttribLocation(program, name) { var attributeLocation = gl.getAttribLocation(program, name); if (attributeLocation === -1) { throw 'Can not find attribute ' + name + '.'; } return attributeLocation; } } body { font-family: 'Alatsi', sans-serif; margin: 0; overflow: hidden; background: black; } .container { display: flex; justify-content: center; align-items: center; position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; } .title { font-size: 10vw; color: white; } canvas { width: 100%; } <div class="container"> <span class="title">MEATBALLS</span> </div>
There are a lot of issues with that codepen. It's hacking the canvas size instead of letting CSS size the canvas. In the code the canvas size is set with var width = canvas.width = window.innerWidth * 0.75; var height = canvas.height = window.innerHeight * 0.75; It's arguably best to let the browser size the canvas html, body { height: 100%; overflow: hidden; } canvas { width: 100%; height: 100%; } and then ask the browser what size the canvas is and set the canvas's resolution to match canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; It's asking for things are larger than the window so it would get a scrollbar and then hiding the that fact by hiding the scrollbar. It makes no sense. If you don't want a scrollbar don't ask for content that requires a scrollbar. html, body { height: 100%; /* removed overflow: hidden */ } canvas { width: 100%; height: 100%; display: block; } It's using template strings but not actually using them as templates var fragmentShaderSrc = ` precision highp float; const float WIDTH = ` + (width >> 0) + `.0; const float HEIGHT = ` + (height >> 0) + `.0; uniform vec3 metaballs[` + numMetaballs + `]; ... `; should arguably be var fragmentShaderSrc = ` precision highp float; const float WIDTH = ${width >> 0}.0; const float HEIGHT = ${height >> 0}.0; uniform vec3 metaballs[${numMetaballs}]; ... `; The main point of using backticks for strings is so you can use the templating feature ${code} It's hard coding the width and height const float WIDTH = ${width >> 0}.0; const float HEIGHT = ${height >> 0}.0; should arguably be uniform float WIDTH; uniform float HEIGHT; so they can be set Metaballs is misspelled as Meatballs (maybe that was intentional) Here's a new version. Note: anytime the window is resized the metaballs get a random position. If you comment out the call to updateMetaballs after resizing the canvas then they will not get new random positions. Which is better is up to you. The logic for how they bounce is such that any balls that are off the screen after you resize will stay off the screen. You could fix it so they'll head toward the screen and only bounce from the inside back in. The current code is such that on the outside they'll just wobble where they are. var canvas = document.createElement("canvas"); document.body.appendChild(canvas); var gl = canvas.getContext('webgl'); var mouse = {x: 0, y: 0}; var numMetaballs = 30; var metaballs = []; function updateMetaballs() { const width = canvas.clientWidth; const height = canvas.clientHeight; for (var i = 0; i < numMetaballs; i++) { var radius = Math.random() * 60 + 10; metaballs[i] = { x: Math.random() * (width - 2 * radius) + radius, y: Math.random() * (height - 2 * radius) + radius, vx: (Math.random() - 0.5) * 3, vy: (Math.random() - 0.5) * 3, r: radius * 0.75 }; } } updateMetaballs(); var vertexShaderSrc = ` attribute vec2 position; void main() { // position specifies only x and y. // We set z to be 0.0, and w to be 1.0 gl_Position = vec4(position, 0.0, 1.0); } `; var fragmentShaderSrc = ` precision highp float; uniform float WIDTH; uniform float HEIGHT; #define NUM_METABALLS ${numMetaballs} uniform vec3 metaballs[NUM_METABALLS]; void main(){ float x = gl_FragCoord.x; float y = gl_FragCoord.y; float sum = 0.0; for (int i = 0; i < NUM_METABALLS; i++) { vec3 metaball = metaballs[i]; float dx = metaball.x - x; float dy = metaball.y - y; float radius = metaball.z; sum += (radius * radius) / (dx * dx + dy * dy); } if (sum >= 0.99) { gl_FragColor = vec4(mix(vec3(x / WIDTH, y / HEIGHT, 1.0), vec3(0, 0, 0), max(0.0, 1.0 - (sum - 0.99) * 100.0)), 1.0); return; } gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } `; var vertexShader = compileShader(vertexShaderSrc, gl.VERTEX_SHADER); var fragmentShader = compileShader(fragmentShaderSrc, gl.FRAGMENT_SHADER); var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); var vertexData = new Float32Array([ -1.0, 1.0, // top left -1.0, -1.0, // bottom left 1.0, 1.0, // top right 1.0, -1.0, // bottom right ]); var vertexDataBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); var positionHandle = getAttribLocation(program, 'position'); gl.enableVertexAttribArray(positionHandle); gl.vertexAttribPointer(positionHandle, 2, // position is a vec2 gl.FLOAT, // each component is a float gl.FALSE, // don't normalize values 2 * 4, // two 4 byte float components per vertex 0 // offset into each span of vertex data ); var metaballsHandle = getUniformLocation(program, 'metaballs'); var widthHandle = getUniformLocation(program, 'WIDTH'); var heightHandle = getUniformLocation(program, 'HEIGHT'); function resizeCanvasToDisplaySize(canvas) { const width = canvas.clientWidth; const height = canvas.clientHeight; const needResize = canvas.width !== width || canvas.height !== height; if (needResize) { canvas.width = width; canvas.height = height; } return needResize; } loop(); function loop() { if (resizeCanvasToDisplaySize(canvas)) { updateMetaballs(); } const {width, height} = canvas; gl.viewport(0, 0, canvas.width, canvas.height); for (var i = 0; i < numMetaballs; i++) { var metaball = metaballs[i]; metaball.x += metaball.vx; metaball.y += metaball.vy; if (metaball.x < metaball.r || metaball.x > width - metaball.r) metaball.vx *= -1; if (metaball.y < metaball.r || metaball.y > height - metaball.r) metaball.vy *= -1; } var dataToSendToGPU = new Float32Array(3 * numMetaballs); for (var i = 0; i < numMetaballs; i++) { var baseIndex = 3 * i; var mb = metaballs[i]; dataToSendToGPU[baseIndex + 0] = mb.x; dataToSendToGPU[baseIndex + 1] = mb.y; dataToSendToGPU[baseIndex + 2] = mb.r; } gl.uniform3fv(metaballsHandle, dataToSendToGPU); gl.uniform1f(widthHandle, canvas.clientWidth); gl.uniform1f(heightHandle, canvas.clientHeight); //Draw gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); requestAnimationFrame(loop); } function compileShader(shaderSource, shaderType) { var shader = gl.createShader(shaderType); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw "Shader compile failed with: " + gl.getShaderInfoLog(shader); } return shader; } function getUniformLocation(program, name) { var uniformLocation = gl.getUniformLocation(program, name); if (uniformLocation === -1) { throw 'Can not find uniform ' + name + '.'; } return uniformLocation; } function getAttribLocation(program, name) { var attributeLocation = gl.getAttribLocation(program, name); if (attributeLocation === -1) { throw 'Can not find attribute ' + name + '.'; } return attributeLocation; } canvas.onmousemove = function(e) { mouse.x = e.clientX; mouse.y = e.clientY; } html, body { font-family: 'Alatsi', sans-serif; margin: 0; background: black; height: 100%; } .container { display: flex; justify-content: center; align-items: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .title { font-size: 10vw; color: white; } canvas { width: 100%; height: 100%; display: block; } <div class="container"> <span class="title">METABALLS</span> </div> If you want to learn WebGL consider these tutorials
Error in calling createcanvas method of p5.js using webgl
I'm studying p5.js. While testing the example below locally, I got the following error: Here's the code I tested locally: let flower = (p) => { let size, shading; p.setup = function() { let renderer = p.createCanvas(1024, 768, p.WEBGL); p.noStroke(); size = p.min(p.width, p.height) / 3; shading = new p5.Shader(renderer, p.vert, p.frag); p.shader(shading); p.mouseX = p.width / 2; p.mouseY = p.height / 2; } p.draw = function() { p.background("#1a0633"); p.orbitControl(); p.rotateX(p.PI / 2); let time = p.millis() / 1000; shading.setUniform('iTime', time); shading.setUniform('size', size); shading.setUniform('mouse', [p.mouseX / p.width, p.mouseY / p.height]); p.plane(1, 1, 100, 100); } }; let p5Flower = new p5(flower); What is causing the error?
You haven't defined any shaders This error suggests that p.vert is not set to anything. Also I'm pretty sure you want to move p.shader(shading) from setup to draw. The only reason it's working in the example is because there is only 1 shader. As soon as there are 2 it will fail. But, that's not why you're getting an error. You're getting an error because p.vert is undefined. const vert = `precision mediump float; #define PI 3.14159265359 // our vertex data attribute vec3 aPosition; attribute vec2 aTexCoord; //transform uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; uniform mat3 uNormalMatrix; // lets get texcoords just for fun! varying vec2 vTexCoord; varying float n; uniform float iTime; uniform vec2 mouse; uniform float size; // --- Simplex3D noise by Lallis/2015 https://www.shadertoy.com/view/XtBGDG //http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf //simplex pretty much 99% copied from there //adjusted by getting "completely random" gradients instead of randoming from 12 preset ones //and normalizing the gradient vector float noise3D(vec3 p) { return fract(sin(dot(p ,vec3(12.9898,78.233,128.852))) * 43758.5453)*2.0-1.0; } float simplex3D(vec3 p) { float f3 = 1.0/3.0; float s = (p.x+p.y+p.z)*f3; int i = int(floor(p.x+s)); int j = int(floor(p.y+s)); int k = int(floor(p.z+s)); float g3 = 1.0/6.0; float t = float((i+j+k))*g3; float x0 = float(i)-t; float y0 = float(j)-t; float z0 = float(k)-t; x0 = p.x-x0; y0 = p.y-y0; z0 = p.z-z0; int i1,j1,k1; int i2,j2,k2; if(x0>=y0) { if(y0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order else if(x0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Z order } else { if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order } float x1 = x0 - float(i1) + g3; float y1 = y0 - float(j1) + g3; float z1 = z0 - float(k1) + g3; float x2 = x0 - float(i2) + 2.0*g3; float y2 = y0 - float(j2) + 2.0*g3; float z2 = z0 - float(k2) + 2.0*g3; float x3 = x0 - 1.0 + 3.0*g3; float y3 = y0 - 1.0 + 3.0*g3; float z3 = z0 - 1.0 + 3.0*g3; vec3 ijk0 = vec3(i,j,k); vec3 ijk1 = vec3(i+i1,j+j1,k+k1); vec3 ijk2 = vec3(i+i2,j+j2,k+k2); vec3 ijk3 = vec3(i+1,j+1,k+1); vec3 gr0 = normalize(vec3(noise3D(ijk0),noise3D(ijk0*2.01),noise3D(ijk0*2.02))); vec3 gr1 = normalize(vec3(noise3D(ijk1),noise3D(ijk1*2.01),noise3D(ijk1*2.02))); vec3 gr2 = normalize(vec3(noise3D(ijk2),noise3D(ijk2*2.01),noise3D(ijk2*2.02))); vec3 gr3 = normalize(vec3(noise3D(ijk3),noise3D(ijk3*2.01),noise3D(ijk3*2.02))); float n0 = 0.0; float n1 = 0.0; float n2 = 0.0; float n3 = 0.0; float t0 = 0.5 - x0*x0 - y0*y0 - z0*z0; if(t0>=0.0) { t0*=t0; n0 = t0 * t0 * dot(gr0, vec3(x0, y0, z0)); } float t1 = 0.5 - x1*x1 - y1*y1 - z1*z1; if(t1>=0.0) { t1*=t1; n1 = t1 * t1 * dot(gr1, vec3(x1, y1, z1)); } float t2 = 0.5 - x2*x2 - y2*y2 - z2*z2; if(t2>=0.0) { t2 *= t2; n2 = t2 * t2 * dot(gr2, vec3(x2, y2, z2)); } float t3 = 0.5 - x3*x3 - y3*y3 - z3*z3; if(t3>=0.0) { t3 *= t3; n3 = t3 * t3 * dot(gr3, vec3(x3, y3, z3)); } return 96.0*(n0+n1+n2+n3); } void main() { vTexCoord = aTexCoord; vec4 pos = vec4(aPosition, 1.0); n = simplex3D(vec3(iTime,pos.xy * 10.))*0.5+0.5; vec3 temp = pos.xyz; pos.x = cos(temp.x * PI * 10. * mouse.x) * (n+0.3) * size * (temp.y-mouse.y+0.5); pos.z = sin(temp.x * PI * 10. * mouse.x) * (n+0.3) * size * (temp.y-mouse.y+0.5); pos.y = pos.y * size * 3.; gl_Position = uProjectionMatrix * uModelViewMatrix * pos; }` const frag = `precision mediump float; varying vec2 vTexCoord; varying float n; uniform float iTime; void main() { vec2 uv = vTexCoord;//*2.0-1.0; vec3 col = vec3(n*1.0, n*n*n*0.76, .2); gl_FragColor = vec4(col, 1.0); }` let flower = (p) => { let size, shading; p.setup = function() { let renderer = p.createCanvas(1024, 768, p.WEBGL); p.noStroke(); size = p.min(p.width, p.height) / 3; shading = new p5.Shader(renderer, vert, frag); p.mouseX = p.width / 2; p.mouseY = p.height / 2; } p.draw = function() { p.background("#1a0633"); p.orbitControl(); p.rotateX(p.PI / 2); p.shader(shading); let time = p.millis() / 1000; shading.setUniform('iTime', time); shading.setUniform('size', size); shading.setUniform('mouse', [p.mouseX / p.width, p.mouseY / p.height]); p.plane(1, 1, 100, 100); } }; let p5Flower = new p5(flower); <script src="https://cdn.jsdelivr.net/npm/p5#0.10.2/lib/p5.js"></script>
Is it possible to make this 'Water Shader Animation' not spherical?
http://codepen.io/Khangeldy/pen/gPJoxJ JS // init camera, scene, renderer var scene, camera, renderer; scene = new THREE.Scene(); var fov = 75, aspect = window.innerWidth / window.innerHeight; camera = new THREE.PerspectiveCamera(fov, aspect, 0.1, 1000); camera.position.z = 100; camera.lookAt(scene.position); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(0xc4c4c4); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var clock = new THREE.Clock(); var tuniform = { iGlobalTime: { type: 'f', value: 0.1 }, iResolution: { type: 'v2', value: new THREE.Vector2() }, iMouse: { type: 'v4', value: new THREE.Vector2() } }; // Mouse position in - 1 to 1 renderer.domElement.addEventListener('mousedown', function(e) { var canvas = renderer.domElement; var rect = canvas.getBoundingClientRect(); tuniform.iMouse.value.x = (e.clientX - rect.left) / window.innerWidth * 2 - 1; tuniform.iMouse.value.y = (e.clientY - rect.top) / window.innerHeight * -2 + 1; }); renderer.domElement.addEventListener('mouseup', function(e) { var canvas = renderer.domElement; var rect = canvas.getBoundingClientRect(); tuniform.iMouse.value.z = (e.clientX - rect.left) / window.innerWidth * 2 - 1; tuniform.iMouse.value.w = (e.clientY - rect.top) / window.innerHeight * -2 + 1; }); // resize canvas function window.addEventListener('resize',function() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); tuniform.iResolution.value.x = window.innerWidth; tuniform.iResolution.value.y = window.innerHeight; // Create Plane var material = new THREE.ShaderMaterial({ uniforms: tuniform, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent }); var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight, 40), material ); scene.add(mesh); // draw animation function render(time) { tuniform.iGlobalTime.value += clock.getDelta(); requestAnimationFrame(render); renderer.render(scene, camera); } render(); I'm wanting to know if its possible to edit this animation, so the horizon is flat (so it doesnt look like a ball of water, instead like the horizon of an ocean?) and ontop of this, is it possible to make the camera 'still'? Thanks
Yes, it's possible. All you have to do is experiment with the variables. // init camera, scene, renderer var scene, camera, renderer; scene = new THREE.Scene(); var fov = 75, aspect = window.innerWidth / window.innerHeight; camera = new THREE.PerspectiveCamera(fov, aspect, 0.1, 1000); camera.position.z = 100; camera.lookAt(scene.position); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(0xc4c4c4); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var clock = new THREE.Clock(); var tuniform = { time: { type: 'f', value: 0.1 }, resolution: { type: 'v2', value: new THREE.Vector2() }, mouse: { type: 'v4', value: new THREE.Vector2() } }; // Mouse position in - 1 to 1 renderer.domElement.addEventListener('mousedown', function(e) { var canvas = renderer.domElement; var rect = canvas.getBoundingClientRect(); tuniform.mouse.value.x = (e.clientX - rect.left) / window.innerWidth * 2 - 1; tuniform.mouse.value.y = (e.clientY - rect.top) / window.innerHeight * -2 + 1; }); renderer.domElement.addEventListener('mouseup', function(e) { var canvas = renderer.domElement; var rect = canvas.getBoundingClientRect(); tuniform.mouse.value.z = (e.clientX - rect.left) / window.innerWidth * 2 - 1; tuniform.mouse.value.w = (e.clientY - rect.top) / window.innerHeight * -2 + 1; }); // resize canvas function window.addEventListener('resize',function() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); tuniform.resolution.value.x = window.innerWidth; tuniform.resolution.value.y = window.innerHeight; // Create Plane var material = new THREE.ShaderMaterial({ uniforms: tuniform, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent }); var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight, 40), material ); scene.add(mesh); // draw animation function render(time) { tuniform.time.value += clock.getDelta(); requestAnimationFrame(render); renderer.render(scene, camera); } render(); body { overflow: hidden; margin: 0; height: 100%; } <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script> <!-- THIS is OPENGL Shading language scripts --> <script id="vertex-shader" type="no-js"> void main() { gl_Position = vec4( position, 1.0 ); } </script> <script id="fragment-shader" type="no-js"> #ifdef GL_ES precision mediump float; #endif uniform float time; uniform vec2 mouse; uniform vec2 resolution; varying vec2 surfacePosition; const int NUM_STEPS = 8; const float PI = 3.1415; const float EPSILON = 1e-3; float EPSILON_NRM = 0.1 / resolution.x; // sea const int ITER_GEOMETRY = 3; const int ITER_FRAGMENT = 5; const float SEA_HEIGHT = 0.6; const float SEA_CHOPPY = 2.0; const float SEA_SPEED = 0.8; const float SEA_FREQ = 0.16; const vec3 SEA_BASE = vec3(0.1,0.19,0.22); const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6); const float SKY_INTENSITY = 1.0; #define SEA_TIME time * SEA_SPEED // math mat4 fromEuler(vec3 ang) { vec2 a1 = vec2(sin(ang.x),cos(ang.x)); vec2 a2 = vec2(sin(ang.y),cos(ang.y)); vec2 a3 = vec2(sin(ang.z),cos(ang.z)); mat4 m; m[0] = vec4(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x,0.0); m[1] = vec4(-a2.y*a1.x,a1.y*a2.y,a2.x,0.0); m[2] = vec4(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y,0.0); m[3] = vec4(0.0,0.0,0.0,1.0); return m; } vec3 rotate(vec3 v, mat4 m) { return vec3(dot(v,m[0].xyz),dot(v,m[1].xyz),dot(v,m[2].xyz)); } float hash( vec2 p ) { float h = dot(p,vec2(127.1,311.7)); return fract(sin(h)*43758.5453123); } float noise( in vec2 p ) { vec2 i = floor( p ); vec2 f = fract( p ); vec2 u = f*f*(3.0-2.0*f); return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), hash( i + vec2(1.0,0.0) ), u.x), mix( hash( i + vec2(0.0,1.0) ), hash( i + vec2(1.0,1.0) ), u.x), u.y); } // lighting float diffuse(vec3 n,vec3 l,float p) { return pow(dot(n,l) * 0.4 + 0.6,p); } float specular(vec3 n,vec3 l,vec3 e,float s) { float nrm = (s + 8.0) / (3.1415 * 8.0); return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; } // sky vec3 sky_color(vec3 e) { e.y = max(e.y,0.0); vec3 ret; ret.x = pow(1.0-e.y,2.0); ret.y = 1.0-e.y; ret.z = 0.6+(1.0-e.y)*0.4; return ret * SKY_INTENSITY; } // sea float sea_octave(vec2 uv, float choppy) { uv += noise(uv); vec2 wv = 1.0-abs(sin(uv)); vec2 swv = abs(cos(uv)); wv = mix(wv,swv,wv); return pow(1.0-pow(wv.x * wv.y,0.65),choppy); } float map(vec3 p) { float freq = SEA_FREQ; float amp = SEA_HEIGHT; float choppy = SEA_CHOPPY; vec2 uv = p.xz; uv.x *= 0.75; mat2 m = mat2(1.6,1.2,-1.2,1.6); float d, h = 0.0; for(int i = 0; i < ITER_GEOMETRY; i++) { d = sea_octave((uv+SEA_TIME)*freq,choppy); d += sea_octave((uv-SEA_TIME)*freq,choppy); h += d * amp; uv *= m; freq *= 1.9; amp *= 0.22; choppy = mix(choppy,1.0,0.2); } return p.y - h; } float map_detailed(vec3 p) { float freq = SEA_FREQ; float amp = SEA_HEIGHT; float choppy = SEA_CHOPPY; vec2 uv = p.xz; uv.x *= 0.75; mat2 m = mat2(1.6,1.2,-1.2,1.6); float d, h = 0.0; for(int i = 0; i < ITER_FRAGMENT; i++) { d = sea_octave((uv+SEA_TIME)*freq,choppy); d += sea_octave((uv-SEA_TIME)*freq,choppy); h += d * amp; uv *= m; freq *= 1.9; amp *= 0.22; choppy = mix(choppy,1.0,0.2); } return p.y - h; } vec3 sea_color(in vec3 p, in vec3 n, in vec3 eye, in vec3 dist) { float fresnel_o = 1.0 - max(dot(n,-eye),0.0); float fresnel = pow(fresnel_o,3.0) * 0.65; // reflection vec3 refl = sky_color(reflect(eye,n)); // color vec3 ret = SEA_BASE; ret = mix(ret,refl,fresnel); // wave peaks float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); ret += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; return ret; } // tracing vec3 getNormal(vec3 p, float eps) { vec3 n; n.y = map_detailed(p); n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; n.y = eps; return normalize(n); } float hftracing(vec3 ori, vec3 dir, out vec3 p) { float tm = 0.0; float tx = 1000.0; float hx = map(ori + dir * tx); if(hx > 0.0) return tx; float hm = map(ori + dir * tm); float tmid = 0.0; for(int i = 0; i < NUM_STEPS; i++) { tmid = mix(tm,tx, hm/(hm-hx)); p = ori + dir * tmid; float hmid = map(p); if(hmid < 0.0) { tx = tmid; hx = hmid; } else { tm = tmid; hm = hmid; } } return tmid; } // main void main(void) { vec2 uv = gl_FragCoord.xy / resolution.xy; uv = 1.0 - uv * 2.0; uv.x *= resolution.x / resolution.y; //uv = (surfacePosition+vec2(0., .5))*17. + 5E-3*(pow(length(surfacePosition+vec2(0. ,0.5)), -2.)); uv.y *= -1.; //uv.y += -2.; // ray vec3 ang = vec3(0.0,0.003, pow(time, 0.6)); ang = vec3(0.0,clamp(2.0-mouse.y*0.01,-0.3,PI),mouse.x*0.01); vec3 ori = vec3(0.0,3.5,time*.05); vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z -= length(uv) * 0.15; //dir = rotate(normalize(dir),ang); // tracing vec3 p; float dens = hftracing(ori,dir,p); vec3 dist = p - ori; vec3 n = getNormal(p, dot(dist,dist)*EPSILON_NRM); // color vec3 color = sea_color(p,n,dir,dist); vec3 light = normalize(vec3(0.0,1.0,0.8)); color += vec3(diffuse(n,light,80.0) * SEA_WATER_COLOR) * 0.12; color += vec3(specular(n,light,dir,60.0)); // post color = mix(sky_color(dir),color,pow(smoothstep(0.0,-0.05,dir.y),0.3)); color = pow(color,vec3(0.75)); gl_FragColor = vec4(color,1.0); } </script> External Demo https://jsfiddle.net/nanilab/uz6yo2w3/