Is it possible to make this 'Water Shader Animation' not spherical? - javascript

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/

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 make a proper planar reflection using ogl with a perspective camera?

I'm trying to reflect a WebGL scene in a plane with ogl, as a mirror would do. I made a naive implementation of the three.js reflector but all I got is a distorted image that doesn't match what an actual reflection would look like.
I get the general idea of how the reflector works by rendering the scene from the virtual reflected point of view of the original camera, adjusting the projection matrix of the virtual camera to render only what projects onto the plane, and later mapping the resulting texture to the plane. But I don't have a complete understanding of all the involved computations (particularly for the two last steps I described).
All I did is copy line by line the three.js reflector and change function names or references, but I can't tell where I'm wrong or what I missed. I would really appreciate any help !
// objects/Reflector.js
import { Transform, Mesh, Plane, Program, RenderTarget, Camera, Vec3, Vec4, Quat, Mat4 } from 'ogl'
import { dot } from 'ogl/src/math/functions/Vec4Func'
import vertex from '../shaders/reflector.vert'
import fragment from '../shaders/reflector.frag'
import { setFromNormalAndCoplanarPoint, transformMat4 } from '../math/PlaneFunc'
import { getRotationMatrix } from '../math/Mat4Func'
import { reflect } from '../math/Vec3Func'
export class Reflector extends Mesh {
constructor(gl, {
scene,
camera,
renderSize = 512,
clipBias = 0
}) {
const renderTarget = new RenderTarget(gl, {
width: renderSize,
height: renderSize
})
super(gl, {
geometry: new Plane(gl),
program: new Program(gl, {
vertex,
fragment,
uniforms: {
textureMatrix: { value: new Mat4() },
diffuseMap: { value: renderTarget.texture }
}
})
})
this.viewCamera = camera
this.clipBias = clipBias
this.reflectionCamera = new Camera(gl)
this.reflectionPlane = new Vec4()
this.reflectionWorldMatrixInverse = new Mat4()
this.reflectionQuaternion = new Quat()
this.worldPosition = new Vec3()
this.normal = new Vec3()
this.view = new Vec3()
this.lookAtPosition = new Vec3()
this.rotationMatrix = new Mat4()
this.target = new Vec3()
this.textureMatrix = this.program.uniforms.textureMatrix.value
this.renderParameters = {
scene,
target: renderTarget,
camera: this.reflectionCamera
}
}
update() {
this.worldMatrix.getTranslation(this.worldPosition)
getRotationMatrix(this.rotationMatrix, this.worldMatrix)
this.normal
.set(0, 0, 1)
.applyMatrix4(this.rotationMatrix)
this.view.sub(this.worldPosition, this.viewCamera.worldPosition)
if (this.view.dot(this.normal) > 0) {
return
}
reflect(this.view, this.view, this.normal)
.negate()
.add(this.worldPosition)
getRotationMatrix(this.rotationMatrix, this.viewCamera.worldMatrix)
this.lookAtPosition.set(0, 0, -1)
this.lookAtPosition.applyMatrix4(this.rotationMatrix)
this.lookAtPosition.add(this.viewCamera.worldPosition)
this.target.sub(this.worldPosition, this.lookAtPosition)
reflect(this.target, this.target, this.normal)
.negate()
.add(this.worldPosition)
this.reflectionCamera.position.copy(this.view)
this.reflectionCamera.up
.set(0, 1, 0)
.applyMatrix4(this.rotationMatrix)
reflect(this.reflectionCamera.up, this.reflectionCamera.up, this.normal)
this.reflectionCamera.lookAt(this.target)
this.reflectionCamera.perspective({
near: this.viewCamera.near,
far: this.viewCamera.far,
fov: this.viewCamera.fov,
aspect: 1
})
this.reflectionCamera.worldMatrixNeedsUpdate = true
this.reflectionCamera.updateMatrixWorld()
this.reflectionWorldMatrixInverse.inverse(this.reflectionCamera.worldMatrix)
this.reflectionCamera.updateFrustum()
this.textureMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
)
this.textureMatrix
.multiply(this.reflectionCamera.projectionMatrix)
.multiply(this.reflectionWorldMatrixInverse)
.multiply(this.worldMatrix)
setFromNormalAndCoplanarPoint(this.reflectionPlane, this.normal, this.worldPosition)
transformMat4(this.reflectionPlane, this.reflectionPlane, this.reflectionWorldMatrixInverse)
const projectionMatrix = this.reflectionCamera.projectionMatrix
this.reflectionQuaternion.set(
(Math.sign(this.reflectionPlane.x) + projectionMatrix[8]) / projectionMatrix[0],
(Math.sign(this.reflectionPlane.y) + projectionMatrix[9]) / projectionMatrix[5],
-1,
(1 + projectionMatrix[10]) / projectionMatrix[14]
)
const f = 2 / dot(this.reflectionPlane, this.reflectionQuaternion)
this.reflectionPlane.x *= f
this.reflectionPlane.y *= f
this.reflectionPlane.z *= f
this.reflectionPlane.w *= f
projectionMatrix[2] = this.reflectionPlane.x
projectionMatrix[6] = this.reflectionPlane.y
projectionMatrix[10] = this.reflectionPlane.z + 1 - this.clipBias
projectionMatrix[14] = this.reflectionPlane.w
this.helper.position.copy(this.reflectionCamera.position)
this.helper.rotation.copy(this.reflectionCamera.rotation)
this.visible = false
this.gl.renderer.render(this.renderParameters)
this.visible = true
}
}
// shaders/reflector.vert
precision highp float;
attribute vec3 position;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 textureMatrix;
varying vec4 vUv;
void main() {
vUv = textureMatrix * vec4(position, 1.);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
}
// shaders/reflector.frag
precision highp float;
uniform sampler2D diffuseMap;
varying vec4 vUv;
void main() {
vec3 diffuse = texture2DProj(diffuseMap, vUv).rgb;
gl_FragColor = vec4(diffuse, 1.);
}
Here are some math functions available in three.js that I had to implement myself:
// math/Mat4Func.js
import { length } from 'ogl/src/math/functions/Vec3Func'
export function getRotationMatrix(out, m) {
const sX = 1 / length(m.slice(0, 3))
const sY = 1 / length(m.slice(4, 7))
const sZ = 1 / length(m.slice(8, 11))
out[0] = m[0] * sX
out[1] = m[1] * sX
out[2] = m[2] * sX
out[3] = 0
out[4] = m[4] * sY
out[5] = m[5] * sY
out[6] = m[6] * sY
out[7] = 0
out[8] = m[8] * sZ
out[9] = m[9] * sZ
out[10] = m[10] * sZ
out[11] = 0
out[12] = 0
out[13] = 0
out[14] = 0
out[15] = 1
return out
}
// math/PlaneFunc.js
import { normalFromMat4 } from 'ogl/src/math/functions/Mat3Func'
import {
dot,
normalize,
transformMat4 as transformMat4Vec3,
transformMat3 as transformMat3Vec3
} from 'ogl/src/math/functions/Vec3Func'
const normal = []
const normalMatrix = []
const coplanarPoint = []
export function transformMat4(out, p, m) {
normalFromMat4(normalMatrix, m)
getCoplanarPoint(coplanarPoint, p)
transformMat4Vec3(coplanarPoint, coplanarPoint, m)
transformMat3Vec3(normal, p, normalMatrix)
normalize(normal, normal)
setFromNormalAndCoplanarPoint(out, normal, coplanarPoint)
return out
}
export function getCoplanarPoint(out, p) {
out[0] = p[0] * -p[3]
out[1] = p[1] * -p[3]
out[2] = p[2] * -p[3]
return out
}
export function setFromNormalAndCoplanarPoint(out, n, c) {
out[0] = n[0]
out[1] = n[1]
out[2] = n[2]
out[3] = -dot(c, n)
return out
}
// math/Vec3Func.js
import { dot } from 'ogl/src/math/functions/Vec3Func'
export function reflect(out, a, b) {
const f = 2 * dot(a, b)
out[0] = a[0] - b[0] * f
out[1] = a[1] - b[1] * f
out[2] = a[2] - b[2] * f
return out
}

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>

THREE.JS | GLSL Set particle colour by scene texture

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,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAMVWlDQ1BEaXNwbGF5AABIiZVXd1RTdxt+7khCQtiIgoywlyiiIENmmIKAbHARkgBhhHhJUHFbShWsW0RxVLQqYrXVCkgdiFpcxb3q+FCLo1KLA7fy/ZFAbf3O953vd8699z3P+7zPO+49OXkBvXUihaKA1AcK5UomISJEkJaeIeDcAwES2nCBi0hcrAiOj48BgL7n38/LKyAA4KKbSKEo+Nz/X4+BRFosBoh4AFmSYnEhQPwI0GViBaME2N4AbKcqFUqAPQGAMZOWngGwFQCMc9R2GQDjLLVdDcCYSUoQAuydgBZfJGJyAN0mAIIScY4S0L0GwF0ukckBPS0AAeJckQTQiwQwpLCwSALoKQE4ZX2ik/M3zax+TZEop99W9wIA0AqVFSsKRNP/z3H871NYoOrL4QCAn8tEJgAwBohr+UXRCQD4ANElz4qNA2AIEK9lEkBtk7xcVWSymk+ai4uFGQBMANJdIgqNBmAOkOHygtgYDZ6VLQuPAqAPkNNkyqgkTewCaXFYokZzHVOUENdnZzPCYE3sLhEDaPjHVPnJwRr9a7nSqD79F6W5SanqmileiSwlFoAuQJkU5ydGqzmUXWmuMLaPw6gSkgHYAZSvVB4RotanJmUz4QkaPlNY3NcvtSBXFhWrsdcqc5MiNTo7xaKwRACDAKpJKg9O7tORFqfF9PUikYaGqXunzknlyZp+qQ6FMiRBE/tMURCv4dM8aUFEAgAbgDYvLknUxNIBSiZJ847oWIUyPkldJ52VJxoTr66HnoYYCBEKAVQQIAtFyIOsvauxCwKNJxwiMMiBFG4apC8iFSIwkEOERJTiD8ghRXF/XAhEYCBFCeT40I+q727IhggMSiBFMfJxHwwKEY0CSKECAynk/dlS8BsYyD7LLkYRClAEBrL/gAVDiBgNourTFej1Mdlh7FB2JDuc7Uyb0QG0Hx1DB9BBdADtQXvTPn3V/sVn3WedZ91lXWZ1sK5Pls1n/tGPAGPRAZVmVlJkfdoz7UB70J50CO1PB9A+ENAmtBnc6JG0Nx1MB9J+tCftA6GmchU+1/5bD59MXcPjunNJ7kBuENfpn5G6Lrqe/SpSyP82IXWtWf1zFfZ7/plf+MmkJShC9D+Z1AJqL9VGHaFOUgeoRgiow1QTdYY6SDV+8hX9BgY5/dkSIIUc+SiA7LN8Ik1OBlIUu9e7P3J/r/YppdOUACAsUkxnZDm5SkGwQlEgFUTJxUOHCDzch/sAaekZAvXP1HMTEAAIk1N/YVNaAJ8KgMj5CxPZAvvvA0Yv/8JsnwH8pcDBc2IVU6LGaABggQc9GMMUlrCFE9zgAS/4IQhhGIM4JCEdkyBGLgrBYCpmYh7KUYmlWIW12IjN2I7vsAeNOIAj+BmncQ6XcQMd6MRjdOMl3hEEwSF0CCPClLAi7AlXwoPwJgKIMCKGSCDSiUwih5ATKmIm8QVRSSwn1hKbiDriB2I/cYQ4SZwnrhN3iEfEM+ItSZF80pi0IB3IYaQ3GUxGk0nkRDKHnEKWkmXkYrKarCV3kg3kEfI0eZnsIB+TPRQobcqEsqbcKG9KSMVRGVQ2xVCzqQqqiqqldlHNVBt1keqguqg3NJs2ogW0G+1HR9LJtJieQs+mF9Fr6e10A32MvkjfobvpjywdljnLleXLimKlsXJYU1nlrCrWVtY+1nHWZVYn6yWbzTZhO7JHsSPZ6ew89gz2IvZ69m52C/s8+x67h8PhmHJcOf6cOI6Io+SUc9ZwdnIOcy5wOjmvtbS1rLQ8tMK1MrTkWvO1qrR2aB3SuqD1QOsdV59rz/XlxnEl3OncJdwt3GbuWW4n9x3PgOfI8+cl8fJ483jVvF2847ybvOfa2to22j7a47Rl2nO1q7W/1z6hfUf7Dd+Q78IX8ifwVfzF/G38Fv51/nMdHR0HnSCdDB2lzmKdOp2jOrd1Xusa6Q7VjdKV6M7RrdFt0L2g+0SPq2evF6w3Sa9Ur0pvr95ZvS59rr6DvlBfpD9bv0Z/v/5V/R4DI4PhBnEGhQaLDHYYnDR4aMgxdDAMM5QYlhluNjxqeM+IMrI1EhqJjb4w2mJ03KjTmG3saBxlnGdcafydcbtx9wDDASMHpAyYNqBmwMEBHSaUiYNJlEmByRKTPSZXTN4OtBgYPFA6cOHAXQMvDHw1aPCgoEHSQRWDdg+6POitqcA0zDTfdJlpo+ktM9rMxWyc2VSzDWbHzboGGw/2GyweXDF4z+BfzUlzF/ME8xnmm83PmPdYWFpEWCgs1lgcteiyNLEMssyzXGl5yPKRlZFVgJXMaqXVYavfBQMEwYICQbXgmKDb2tw60lplvcm63fqdjaNNss18m902t2x5tt622bYrbVttu+2s7MbazbSrt/vVnmvvbZ9rv9q+zf6Vg6NDqsNXDo0ODx0HOUY5ljrWO9500nEKdJriVOt0yZnt7O2c77ze+ZwL6eLpkutS43LWlXT1cpW5rnc9P4Q1xGeIfEjtkKtufLdgtxK3erc7Q02GxgydP7Rx6JNhdsMyhi0b1jbso7une4H7Fvcbww2Hjxk+f3jz8GceLh5ijxqPSyN0RoSPmDOiacTTka4jpSM3jLzmaeQ51vMrz1bPD16jvBivXV6PRtmNyhy1btRVb2PveO9F3id8WD4hPnN8Dvi88fXyVfru8f3Tz80v32+H38PRjqOlo7eMvudv4y/y3+TfESAIyAz4JqAj0DpQFFgbeDfINkgStDXoQbBzcF7wzuAnIe4hTMi+kFdCX+EsYUsoFRoRWhHaHmYYlhy2Nux2uE14Tnh9eHeEZ8SMiJZIVmR05LLIq1EWUeKouqjuMaPGzBpzLJofnRi9NvpujEsME9M8lhw7ZuyKsTdj7WPlsY1xiIuKWxF3K94xfkr8T+PY4+LH1Yy7nzA8YWZCW6JR4uTEHYkvk0KSliTdSHZKViW3puilTEipS3mVGpq6PLUjbVjarLTT6WbpsvSmDE5GSsbWjJ7xYeNXje+c4DmhfMKViY4Tp008OclsUsGkg5P1Josm781kZaZm7sh8L4oT1Yp6sqKy1mV1i4Xi1eLHkiDJSskjqb90ufRBtn/28uyHOf45K3Ie5QbmVuV2yYSytbKneZF5G/Ne5cflb8vvLUgt2F2oVZhZuF9uKM+XHyuyLJpWdF7hqihXdEzxnbJqSjcTzWwtJoonFjcpjZUK5RmVk+pL1Z2SgJKaktdTU6bunWYwTT7tzHSX6QunPygNL/12Bj1DPKN1pvXMeTPvzAqetWk2MTtrdusc2zllczrnRszdPo83L3/eL/Pd5y+f/+KL1C+ayyzK5pbd+zLiy/py3XKm/OpXfl9tXEAvkC1oXzhi4ZqFHyskFacq3SurKt8vEi869fXwr6u/7l2cvbh9ideSDUvZS+VLrywLXLZ9ucHy0uX3Voxd0bBSsLJi5YtVk1edrBpZtXE1b7VqdUd1THXTGrs1S9e8X5u79nJNSM3udebrFq57tV6y/sKGoA27NlpsrNz49hvZN9c2RWxqqHWordrM3lyy+f6WlC1t33p/W7fVbGvl1g/b5Ns6tidsP1Y3qq5uh/mOJfVkvar+0c4JO899F/pd0y63XZt2m+yu/B7fq77//YfMH67sid7Tutd7764f7X9ct89oX0UD0TC9obsxt7GjKb3p/P4x+1ub/Zr3/TT0p20HrA/UHBxwcMkh3qGyQ72HSw/3tChauo7kHLnXOrn1xtG0o5eOjTvWfjz6+Imfw38+2hbcdviE/4kDJ31P7j/lfarxtNfphjOeZ/b94vnLvnav9oazo842nfM513x+9PlDFwIvHLkYevHnS1GXTl+OvXz+SvKVa1cnXO24Jrn28HrB9ae/lvz67sbcm6ybFbf0b1XdNr9d+y/nf+3u8Oo4eCf0zpm7iXdv3BPfe/xb8W/vO8vu69yvemD1oO6hx8MDj8Ifnft9/O+djxWP33WV/2Hwx7onTk9+/DPozzPdad2dT5mnvc8WPTd9vu3FyBetPfE9t18Wvnz3quK16evtb7zftL1Nffvg3dT3nPfVH5w/NH+M/nizt7C3VyFiRAAACgCZnQ082wbopANG5wDeePWeBwAg1LspoP4P8p9t9S4IAPACtgUByXOBmBZgQwtgPxfgtwDxAJKCQI4Y0X9pTnH2CA+1Fp8BWK97e59bAJxm4APT2/tufW/vhy0AdR1omaLeLwGArQ98owsAJ9unfrYo/hut3X80KW0+GQAAAAlwSFlzAAALEwAACxMBAJqcGAAABrRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQwIDc5LjE2MDQ1MSwgMjAxNy8wNS8wNi0wMTowODoyMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0yOVQyMjozODowNCswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDQtMjlUMjI6Mzg6MDQrMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+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>

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>

Categories