Three.js camera control not working & WebGL shader - three.js

I found nice water simulation from codepen and modified it with help from other thread here (can't find it anymore though).
I have used three.js couple of times before, but now I just can't comprehend why camera positioning/rotation/aspect/etc isn't working. No matter what coordinates or angle I give to camera and use updateProjectionMatrix nothing happens, camera just stays in one place.
I commented out resize events etc, since they don't do anything also.
Entire code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
<style type="text/css">
body {
overflow: hidden;
margin: 0;
height: 100%;
}
</style>
<title></title>
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
// 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 = 200;
camera.rotate.z = 1.5707963268;
camera.updateProjectionMatrix();
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();
}//]]>
</script>
</head>
<body>
<!-- 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.5;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.1,0.19,0.22); //meren pohjaväri
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>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "uz6yo2w3"
}], "*")
}
</script>
</body>
</html>

There are so much try in this code that it's not even clear to me what you're trying to do, but I can give some hints :
Change the camera.rotate to camera.rotation l. 30
Your mouse events are commented. If you want to rotate the camera with the mouse, you're gonna have to add a mousemove event ;
By the way, l. 50 you send a vec4 but loads it as a vec2 l. 126 ;
When the window is resized, you may also want to update the new resolution to the shader ;
l. 304, change the Z component of the camera's origin from vec3 ori = vec3(0.0, 3.5, time * 5.0); to vec3 ori = vec3(0.0, 3.5, time * 5.0); so you can see the camera moving along the sea ;
l. 306, instead of dir = rotate(normalize(dir), ang); add the initial dir = normalize(dir) * fromEuler(ang); (ang is the angle of the camera) ;
l. 149, change your mat4 fromEuler(vec3 ang){...} to the initial mat3 fromEuler(vec3 ang){...} function ;
l. 301, just put vec3 ang = vec3(0.0, 0.0, 0.0); and play with it. You may use mouse coordinates in this function, depending on how you want the user interact with the camera.

Related

Decompose a GLSL mat4 to original RTS values within vertex shader to calculate a View UV Offset

I need to get the rotation differences between the model and the camera, convert the values to radians/degrees, and pass it to the fragment shader.
For that I need to decompose and the Model rotation matrix and maybe the camera view matrix as well. I cannot seem to find a way to decompose mechanism suitable within a shader.
The rotation details goes into fragment shader to calculate uv offset.
original_rotation + viewing_angles to calculate a final sprite-like offset of the following texture and shown as billboards.
Ultimately UV should offset downwards (ex:H3 to A3) looking from down, upwards looking from up (ex:A3 to H3), left to right looking and viceversa looking from sides (ex: D1 to D8 and viceversa).
const vertex_shader = `
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec2 uv;
attribute mat4 instanceMatrix;
attribute float index;
attribute float texture_index;
uniform vec2 rows_cols;
uniform vec3 camera_location;
varying float vTexIndex;
varying vec2 vUv;
varying vec4 transformed_normal;
float normal_to_orbit(vec3 rotation_vector, vec3 view_vector){
rotation_vector = normalize(rotation_vector);
view_vector = normalize(view_vector);
vec3 x_direction = vec3(1.0,0,0);
vec3 y_direction = vec3(0,1.0,0);
vec3 z_direction = vec3(0,0,1.0);
float rotation_x_length = dot(rotation_vector, x_direction);
float rotation_y_length = dot(rotation_vector, y_direction);
float rotation_z_length = dot(rotation_vector, z_direction);
float view_x_length = dot(view_vector, x_direction);
float view_y_length = dot(view_vector, y_direction);
float view_z_length = dot(view_vector, z_direction);
//TOP
float top_rotation = degrees(atan(rotation_x_length, rotation_z_length));
float top_view = degrees(atan(view_x_length, view_z_length));
float top_final = top_view-top_rotation;
float top_idx = floor(top_final/(360.0/rows_cols.x));
//FRONT
float front_rotation = degrees(atan(rotation_x_length, rotation_z_length));
float front_view = degrees(atan(view_x_length, view_z_length));
float front_final = front_view-front_rotation;
float front_idx = floor(front_final/(360.0/rows_cols.y));
return abs((front_idx*rows_cols.x)+top_idx);
}
vec3 extractEulerAngleXYZ(mat4 mat) {
vec3 rotangles = vec3(0,0,0);
rotangles.x = atan(mat[2].z, -mat[1].z);
float cosYangle = sqrt(pow(mat[0].x, 2.0) + pow(mat[0].y, 2.0));
rotangles.y = atan(cosYangle, mat[0].z);
float sinXangle = sin(rotangles.x);
float cosXangle = cos(rotangles.x);
rotangles.z = atan(cosXangle * mat[1].y + sinXangle * mat[2].y, cosXangle * mat[1].x + sinXangle * mat[2].x);
return rotangles;
}
float view_index(vec3 position, mat4 mv_matrix, mat4 rot_matrix){
vec4 posInView = mv_matrix * vec4(0.0, 0.0, 0.0, 1.0);
// posInView /= posInView[3];
vec3 VinView = normalize(-posInView.xyz); // (0, 0, 0) - posInView
// vec4 NinView = normalize(rot_matrix * vec4(0.0, 0.0, 1.0, 1.0));
// float NdotV = dot(NinView, VinView);
vec4 view_normal = rot_matrix * vec4(VinView.xyz, 1.0);
float view_x_length = dot(view_normal.xyz, vec3(1.0,0,0));
float view_y_length = dot(view_normal.xyz, vec3(0,1.0,0));
float view_z_length = dot(view_normal.xyz, vec3(0,0,1.0));
// float radians = atan(-view_x_length, -view_z_length);
float radians = atan(view_x_length, view_z_length);
// float angle = radians/PI*180.0 + 180.0;
float angle = degrees(radians);
if (radians < 0.0) { angle += 360.0; }
if (0.0<=angle && angle<=360.0){
return floor(angle/(360.0/rows_cols.x));
}
return 0.0;
}
void main(){
vec4 original_normal = vec4(0.0, 0.0, 1.0, 1.0);
// transformed_normal = modelViewMatrix * instanceMatrix * original_normal;
vec3 rotangles = extractEulerAngleXYZ(modelViewMatrix * instanceMatrix);
// transformed_normal = vec4(rotangles.xyz, 1.0);
transformed_normal = vec4(camera_location.xyz, 1.0);
vec4 v = (modelViewMatrix* instanceMatrix* vec4(0.0, 0.0, 0.0, 1.0)) + vec4(position.x, position.y, 0.0, 0.0) * vec4(1.0, 1.0, 1.0, 1.0);
vec4 model_center = (modelViewMatrix* instanceMatrix* vec4(0.0, 0.0, 0.0, 1.0));
vec4 model_normal = (modelViewMatrix* instanceMatrix* vec4(0.0, 0.0, 1.0, 1.0));
vec4 cam_loc = vec4(camera_location.xyz, 1.0);
vec4 view_vector = normalize((cam_loc-model_center));
//float findex = normal_to_orbit(model_normal.xyz, view_vector.xyz);
float findex = view_index(position, base_matrix, combined_rot);
vTexIndex = texture_index;
vUv = vec2(mod(findex,rows_cols.x)/rows_cols.x, floor(findex/rows_cols.x)/rows_cols.y) + (uv / rows_cols);
//vUv = vec2(mod(index,rows_cols.x)/rows_cols.x, floor(index/rows_cols.x)/rows_cols.y) + (uv / rows_cols);
gl_Position = projectionMatrix * v;
// gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);
}
`
const fragment_shader = (texture_count) => {
var fragShader = `
precision highp float;
uniform sampler2D textures[${texture_count}];
varying float vTexIndex;
varying vec2 vUv;
varying vec4 transformed_normal;
void main() {
vec4 finalColor;
`;
for (var i = 0; i < texture_count; i++) {
if (i == 0) {
fragShader += `if (vTexIndex < ${i}.5) {
finalColor = texture2D(textures[${i}], vUv);
}
`
} else {
fragShader += `else if (vTexIndex < ${i}.5) {
finalColor = texture2D(textures[${i}], vUv);
}
`
}
}
//fragShader += `gl_FragColor = finalColor * transformed_normal; }`;
fragShader += `gl_FragColor = finalColor; }`;
// fragShader += `gl_FragColor = startColor * finalColor; }`;
// int index = int(v_TexIndex+0.5); //https://stackoverflow.com/questions/60896915/texture-slot-not-getting-picked-properly-in-shader-issue
//console.log('frag shader: ', fragShader)
return fragShader;
}
function reset_instance_positions() {
const dummy = new THREE.Object3D();
const offset = 500*4
for (var i = 0; i < max_instances; i++) {
dummy.position.set(offset-(Math.floor(i % 8)*500), offset-(Math.floor(i / 8)*500), 0);
dummy.updateMatrix();
mesh.setMatrixAt(i, dummy.matrix);
}
mesh.instanceMatrix.needsUpdate = true;
}
function setup_geometry() {
const geometry = new THREE.InstancedBufferGeometry().copy(new THREE.PlaneBufferGeometry(400, 400));
const index = new Float32Array(max_instances * 1); // index
for (let i = 0; i < max_instances; i++) {
index[i] = (i % max_instances) * 1.0 /* index[i] = 0.0 */
}
geometry.setAttribute("index", new THREE.InstancedBufferAttribute(index, 1));
const texture_index = new Float32Array(max_instances * 1); // texture_index
const max_maps = 1
for (let i = 0; i < max_instances; i++) {
texture_index[i] = (Math.floor(i / max_instances) % max_maps) * 1.0 /* index[i] = 0.0 */
}
geometry.setAttribute("texture_index", new THREE.InstancedBufferAttribute(texture_index, 1));
const textures = [texture]
const grid_xy = new THREE.Vector2(8, 8)
mesh = new THREE.InstancedMesh(geometry,
new THREE.RawShaderMaterial({
uniforms: {
textures: {
type: 'tv',
value: textures
},
rows_cols: {
value: new THREE.Vector2(grid_xy.x * 1.0, grid_xy.y * 1.0)
},
camera_location: {
value: camera.position
}
},
vertexShader: vertex_shader,
fragmentShader: fragment_shader(textures.length),
side: THREE.DoubleSide,
// transparent: true,
}), max_instances);
scene.add(mesh);
reset_instance_positions()
}
var camera, scene, mesh, renderer;
const max_instances = 64
function init() {
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight,1, 10000 );
camera.position.z = 1024;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
setup_geometry()
var canvas = document.createElement('canvas');
var context = canvas.getContext('webgl2');
renderer = new THREE.WebGLRenderer({
canvas: canvas,
context: context
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
var dataurl = "https://i.stack.imgur.com/accaU.png"
var texture;
var imageElement = document.createElement('img');
imageElement.onload = function(e) {
texture = new THREE.Texture(this);
texture.needsUpdate = true;
init();
animate();
};
imageElement.src = dataurl;
JSFiddle of work so far
So You got 4x4 transform matrix M used on xy plane QUAD and want to map its 4 corners (p0,p1,p2,p3) to your texture with "repaeat" like manner (crossing border from left/right/up/down will return right/left/down/up) based on direction of Z axis of the matrix.
You face 2 problems...
M rotation is 3 DOF and you want just 2 DOF (yaw,pitch) so if roll present the result might be questionable
if texture crosses borders you need to handle this in GLSL to avoid seems
so either do this in geometry shader and divide the quad to more if needed or use enlarged texture where you have the needed overlaps ...
Now if I did not miss something the conversion is like this:
const float pi=3.1415926535897932384626433832795;
vec3 d = normalize(z axis from M);
vec2 dd = normalize(d.xy);
u = atan2(dd.y,dd.x);
v = acos(d.z);
u = (u+pi)/(2.0*pi);
v = v/pi
The z axis extraction is just simple copy of 3th column/row (depends on your notation) from your matrix 'M' or transforming (1,0,0,0) by it. For more info see:
Understanding 4x4 homogenous transform matrices
In case of overlapped texture you need to add also this:
const float ov = 1.0/8.0; // overlap size
u = ov + (u/(ov+ov+1.0));
v = ov + (v/(ov+ov+1.0));
And the texture would look like:
In case your quads cover more than 1/8 of your original texture you need to enlarge the overlap ...
Now to handle the corners of QUAD instead of just axis you could translate the quad by distance l in Z+ direction in mesh local coordinates, apply the M on them and use those 4 points as directions to compute u,v in vertex shader. The l will affect how much of the texture area is used for quad ... This approach might even handle roll but did not test any of this yet...
After implementing it my fears was well grounded as any 2 euler angles affect each other so the result is OK on most of the directions but in edge cases the stuff get mirrored and or jumped in one or both axises probably due to area coverage difference between 3 DOF and 2 DOF (unless I made a bug in my code or the math was not computed correctly in vertex which happened to me before due to bug in drivers)
If you going for azimut/elevation that should be fine as its 2 DOF too the equation above shoul dwork for them too +/- some range conversion if needed.

How to Create a Multi-Colored Noise Shader

I need a shader that takes 3 input colors and generates noise as seen below.
It is easy to achieve in Blender with the help of the "Noise Texture" and the "Color Ramp" nodes.
I've found this gist, which might solve my problem. But I wasn't able to configure the colors. And also the noise looks a lot "sharper" than the result in Blender.
Will I need to write my own shader for this or is there a simpler way to achieve this effect with ThreeJS?
Basically, you need just two things:
Noise function in shader
A gradiental texture.
I chose FBM for noise and used .onBeforeCompile() to change a built-in material (Standard):
body{
overflow: hidden;
margin: 0;
}
<canvas id="cnvsGradient" width="300" height="50" style="position: absolute; margin: 10px; border: 1px solid aqua"/>
<script>
// https://github.com/yiwenl/glsl-fbm/blob/master/3d.glsl
const fbm = `
#define NUM_OCTAVES 5
float mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);}
float noise(vec3 p){
vec3 a = floor(p);
vec3 d = p - a;
d = d * d * (3.0 - 2.0 * d);
vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);
vec4 k1 = perm(b.xyxy);
vec4 k2 = perm(k1.xyxy + b.zzww);
vec4 c = k2 + a.zzzz;
vec4 k3 = perm(c);
vec4 k4 = perm(c + 1.0);
vec4 o1 = fract(k3 * (1.0 / 41.0));
vec4 o2 = fract(k4 * (1.0 / 41.0));
vec4 o3 = o2 * d.z + o1 * (1.0 - d.z);
vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x);
return o4.y * d.y + o4.x * (1.0 - d.y);
}
float fbm(vec3 x) {
float v = 0.0;
float a = 0.5;
vec3 shift = vec3(100);
for (int i = 0; i < NUM_OCTAVES; ++i) {
v += a * noise(x);
x = x * 2.0 + shift;
a *= 0.5;
}
return v;
}
`;
</script>
<script type="module">
console.clear();
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.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x444444);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
let g = new THREE.CylinderBufferGeometry(2, 1, 5, 6, 64);
let pos = g.attributes.position;
let v = new THREE.Vector3();
let axis = new THREE.Vector3(0, 1, 0);
for(let i = 0; i < pos.count; i++){
v.fromBufferAttribute(pos, i);
let ratio = (v.y - (-2.5)) / 5;
v.applyAxisAngle(axis, THREE.MathUtils.degToRad(60) * ratio);
pos.setXYZ(i, v.x, v.y, v.z);
}
g.computeVertexNormals();
let uniforms = {
tex: {
value: setGradient()
}
}
let m = new THREE.MeshStandardMaterial({
metalness: 0.25,
roughness: 0.75,
onBeforeCompile: shader => {
shader.uniforms.tex = uniforms.tex;
shader.vertexShader = `
varying vec3 vPos;
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
//vPos = (modelMatrix * vec4(position, 1.0)).xyz;
vPos = vec3(position);
`
);
//console.log(shader.vertexShader);
shader.fragmentShader = `
uniform sampler2D tex;
varying vec3 vPos;
${fbm}
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
float d = fbm(vPos * 0.5);
for(int i = 0; i < 4; i++){
d = fbm(vPos * (float(i) + 1.) * d);
}
vec3 col = texture(tex, vec2(d, 0.5)).rgb;
vec4 diffuseColor = vec4( col, opacity );`
);
//console.log(shader.fragmentShader);
}
});
let o = new THREE.Mesh(g, m);
scene.add(o);
window.addEventListener( 'resize', onWindowResize, false );
renderer.setAnimationLoop(() => {
o.rotation.y += 0.01;
renderer.render(scene, camera);
});
function setGradient(){
let canvas = document.getElementById('cnvsGradient');
let ctx = canvas.getContext('2d');
let gradient = ctx.createLinearGradient(0,0, 300,0);
gradient.addColorStop(0.15, 'yellow');
gradient.addColorStop(.5, 'red');
gradient.addColorStop(0.85, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return new THREE.CanvasTexture(canvas);
}
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( innerWidth, innerHeight );
}
</script>

THREE.JS Metaballs animation at fragment shader

I have the code where I am trying to make a metaballs motion transition between particles like Shadertoy sketch https://www.shadertoy.com/view/wtd3Wn
So, in the end, I need something like this:
Eventually, it has to be animated as well.
To do this, I have created another shader [Joint] with width/height equal to the distance between particles and it's already got their position, so if you call
gl_FragColor = vec4(1.0 - circle(fragCoord, startPos, 16.0 ) * circle(fragCoord, endPos, 16.0 ) );
it would overdraw circles at their positions.
But when I am trying to draw metaballs, I've got nothing.
Pretty sure that I am doing something wrong with fragCoord initialization or I have to tweak these parameters:
const float threshold = 3.0;
const float density = 1000.0;
const float norm = 2.0;
and
balls[0] = setMetaball(vec2(0.0, 0.0), 0.2, vec3(0.0, 0.0, 1.0));
balls[1] = setMetaball(startPos, 0.05, vec3(0.0, 0.0, 1.0));
balls[2] = setMetaball(endPos, 0.1, vec3(0.0, 1.0, 0.0));
balls[3] = setMetaball(endPos, 0.05, vec3(0.0, 1.0, 0.0));
positions and radiuses
THREE.TOUCH = {};
var circularPoint = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128px" height="128px"><circle cx="64" cy="64" r="62" fill="white" /></svg>';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(35, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 20);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xEEEEEE, 1.0);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var N = 4,
verts = [],
colors = [],
radius = [];
verts = [
new THREE.Vector3(0.0, 0.0, -4.0),
new THREE.Vector3(-4.0, 0.0, 0.0),
new THREE.Vector3(4.0, 0.0, 0.0),
new THREE.Vector3(0.0, 0.0, 4.0)
];
colors.push(1.0, 0., 1.0);
colors.push(1.0, 0., 1.0);
colors.push(0.847, 0.332, 0.347);
colors.push(0.457, 0.695, 0.675);
for (var i = 0; i < N; i++) {
radius.push(0.5);
}
var pointsGeometry = new THREE.BufferGeometry().setFromPoints(verts);
pointsGeometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));
pointsGeometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(radius), 1));
var pointsMaterial = new THREE.ShaderMaterial({
uniforms: {
viewport: {
value: window.innerHeight * window.devicePixelRatio
},
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
resolution: {
value: [innerWidth * 2, innerHeight * 2]
}
},
vertexShader: document.getElementById("vertexParticle").textContent,
fragmentShader: document.getElementById("fragmentParticle").textContent,
transparent: true
})
var verts2 = [],
colors2 = [],
radius2 = [];
var dist = new THREE.Vector3(-4.0, 0.0, 0.0).distanceTo(new THREE.Vector3(0.0, 0.0, -4.0));
for (var i = 0; i < 1; i++) {
for (var j = i + 1; j < 2; j++) {
var dist = verts[i].distanceTo(verts[j]);
var nv = new THREE.Vector3(verts[i].x, verts[i].y, verts[i].z);
verts2.push(nv.lerp(verts[j], 0.5));
radius2.push(dist / 2.0 + 0.5);
}
}
var jointsGeometry = new THREE.BufferGeometry().setFromPoints(verts2);
jointsGeometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(radius2), 1));
var jointsMaterial = new THREE.ShaderMaterial({
uniforms: {
viewport: {
value: innerHeight * devicePixelRatio
},
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
start: {
value: verts[0]
},
end: {
value: verts[1]
},
resolution: {
value: [innerWidth * 2, innerHeight * 2]
}
},
vertexShader: document.getElementById("vertexJoint").textContent,
fragmentShader: document.getElementById("fragmentJoint").textContent,
transparent: true
})
jointsMaterial.extensions.derivatives = true;
jointsMaterial.extensions.fragDepth = true;
jointsMaterial.extensions.drawBuffers = true;
var points = new THREE.Points(pointsGeometry, pointsMaterial);
scene.add(points);
var joints = new THREE.Points(jointsGeometry, jointsMaterial);
scene.add(joints);
renderer.setAnimationLoop(function() {
renderer.render(scene, camera)
});
body { overflow: hidden; margin: 0; }
<html>
<head>
<meta charset="utf-8">
<title>THREE.JS METABALLS</title>
<meta name="description" content="Ehno based on D3.JS | THREE.JS stack.">
<meta name="keywords" content="HTML,CSS,CSV,JavaScript,D3.JS,THREE.JS">
<meta name="author" content="Vladimir V. KUCHINOV">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js"></script>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script type="x-shader/x-vertex" id="vertexParticle">
#define PI 3.141592
attribute float radius;
attribute vec3 color;
uniform float viewport;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = viewport * radius * PI / -mvPosition.z;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentParticle">
varying vec3 vColor;
uniform sampler2D texture;
uniform vec2 resolution;
void main() {
gl_FragColor = vec4(vColor, 1.) * texture2D(texture, gl_PointCoord);
if (gl_FragColor.a < 0.1) discard;
}
</script>
<script type="x-shader/x-vertex" id="vertexJoint">
#define PI 3.141592
attribute float radius;
uniform float viewport;
uniform vec2 resolution;
uniform vec3 start;
uniform vec3 end;
varying vec2 outStart;
varying vec2 outEnd;
varying vec2 vUv;
vec2 projectWorldCoordinates(vec3 in_){
vec4 p = projectionMatrix * modelViewMatrix * vec4(in_.xy, in_.z, 1.0);
return p.xy / p.w;
}
void main() {
vUv = uv;
outStart = projectWorldCoordinates(start);
outEnd = projectWorldCoordinates(end);
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = viewport * radius * PI / -mvPosition.z;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentJoint">
uniform vec2 resolution;
varying vec2 outStart;
varying vec2 outEnd;
varying vec2 vUv;
const float threshold = 3.0;
const float density = 1000.0;
const float norm = 2.0;
vec2 startPos;
vec2 endPos;
float pixelPower;
vec2 fragCoord;
vec4 mixColor;
struct Metaball{
vec2 position;
float radius;
vec3 color;
float power;
};
Metaball balls[4];
float Norm(float num_) { return pow(num_, norm); }
Metaball setMetaball(vec2 position_, float radius_, vec3 color_){
Metaball m;
m.position = position_;
m.radius = radius_;
m.color = color_;
vec2 pixelPosition = fragCoord.xy / resolution.xy;
pixelPosition.x = pixelPosition.x * resolution.x / resolution.y;
vec2 distanceVector = pixelPosition - position_;
distanceVector = vec2(abs(distanceVector.x), abs(distanceVector.y));
float normDistance = Norm(distanceVector.x) + Norm(distanceVector.y);
m.power = Norm(radius_) / normDistance;
return m;
}
vec4 drawMetaballs(){
vec4 color = vec4(0.);
vec3 val;
int powerMeta = 0;
float maxPower = 0.0;
balls[0] = setMetaball(vec2(0.0, 0.0), 0.2, vec3(0.0, 0.0, 1.0));
balls[1] = setMetaball(startPos, 0.05, vec3(0.0, 0.0, 1.0));
balls[2] = setMetaball(endPos, 0.1, vec3(0.0, 1.0, 0.0));
balls[3] = setMetaball(endPos, 0.05, vec3(0.0, 1.0, 0.0));
for(int i = 0; i < 4; i++){
pixelPower += balls[i].power;
if(maxPower < balls[i].power){
maxPower = balls[i].power;
powerMeta = i;
}
balls[i].power *= balls[i].radius;
}
val = vec3(0.);
for(int i = 0; i < 4; i++){
vec2 pixelPosition = fragCoord / resolution.xy;
pixelPosition.x = pixelPosition.x * resolution.x / resolution.y;
vec2 distanceVector = pixelPosition - balls[i].position;
distanceVector = vec2(abs(distanceVector.x), abs(distanceVector.y));
float normDistance = Norm(distanceVector.x) + Norm(distanceVector.y);
balls[i].power = Norm(balls[i].radius) / normDistance;
val += balls[i].color * (balls[i].power / maxPower);
}
if(pixelPower < threshold || pixelPower > threshold + Norm(density))
{
val = vec3(0.0);
}
color = vec4(val, 1.0);
return color;
}
float circle( vec2 _st, vec2 _center, float _radius ){
const float thickness = 8.0;
float dist = length(_st - _center);
return smoothstep(0.0, thickness / 2.0, abs(_radius - dist));
}
void main() {
vec2 fragCoord = vec2(gl_FragCoord.x, gl_FragCoord.y);
vec2 uv = (fragCoord * 2.0 - resolution.xy) / resolution.y;
startPos = (outStart.xy * 0.5 + 0.5) * resolution.xy;
endPos = (outEnd.xy * 0.5 + 0.5) * resolution.xy;
vec4 mb = drawMetaballs();
gl_FragColor = mb; //vec4(1.0 - circle(fragCoord, startPos, 16.0 ) * circle(fragCoord, endPos, 16.0 ) );
}
</script>
</body>
</html>
Done.
<html>
<head>
<meta charset="utf-8">
<title>THREE.JS METABALLS</title>
<meta name="description" content="Ehno based on D3.JS | THREE.JS stack.">
<meta name="keywords" content="HTML,CSS,CSV,JavaScript,D3.JS,THREE.JS">
<meta name="author" content="Vladimir V. KUCHINOV">
<style>
body { overflow: hidden; margin: 0; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/103/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<!--
<script src="LineMaterial.js"></script>
<script src="LineSegments2.js"></script>
<script src="LineSegmentsGeometry.js"></script>
<script src="LineGeometry.js"></script>
<script src="Line2.js"></script>
-->
<script type="x-shader/x-vertex" id="vertexParticle">
#define PI 3.141592
attribute float radius;
attribute vec3 color;
uniform float viewport;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = viewport * radius * PI / -mvPosition.z;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentParticle">
varying vec3 vColor;
uniform sampler2D texture;
uniform vec2 resolution;
void main() {
gl_FragColor = vec4(vColor, 1.) * texture2D(texture, gl_PointCoord);
if (gl_FragColor.a < 0.5) discard;
}
</script>
<script type="x-shader/x-vertex" id="vertexJoint">
#define PI 3.141592
attribute float radius;
uniform float viewport;
uniform vec2 resolution;
uniform vec3 start;
uniform vec3 end;
varying vec2 outStart;
varying vec2 outEnd;
varying float size;
vec2 projectWorldCoordinates(vec3 in_){
vec4 p = projectionMatrix * modelViewMatrix * vec4(in_.xy, in_.z, 1.0);
return p.xy / p.w;
}
void main() {
outStart = projectWorldCoordinates(start);
outEnd = projectWorldCoordinates(end);
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = viewport * radius * PI / -mvPosition.z;
size = viewport * radius * PI / -mvPosition.z;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentJoint">
uniform vec2 resolution;
uniform float time;
varying vec2 outStart;
varying vec2 outEnd;
varying float size;
const float threshold = 3.0;
const float density = 1000.0;
const float norm = 2.0;
vec2 startPos;
vec2 endPos;
float pixelPower;
vec2 fragCoord;
vec4 mixColor;
struct Metaball{
vec2 position;
float radius;
vec4 color;
float power;
};
Metaball balls[4];
float Norm(float num_) { return pow(num_, norm); }
Metaball setMetaball(vec2 position_, float radius_, vec4 color_, vec2 uv_){
Metaball m;
m.position = position_;
m.radius = radius_;
m.color = color_;
vec2 distanceVector = uv_ - position_;
distanceVector = vec2(abs(distanceVector.x), abs(distanceVector.y));
float normDistance = Norm(distanceVector.x) + Norm(distanceVector.y);
m.power = Norm(radius_) / normDistance;
return m;
}
vec4 drawMetaballs(vec2 p0_, vec2 p1_, vec2 uv_){
vec4 val;
int powerMeta = 0;
float maxPower = 0.0;
vec2 p0 = startPos;
vec2 p1 = endPos;
vec2 d0 = mix(p0, p1, -(sin(time) - 1.0) / 4.);
vec2 d1 = mix(p1, p0, -(sin(time) - 1.0) / 4.);
float rad = size / 8.0;
balls[0] = setMetaball(p0, rad, vec4(0.0, 1.0, 1.0, 1.0), uv_);
balls[1] = setMetaball(d0, rad * 0.75, vec4(0.0, 1.0, 1.0, 1.0), uv_);
balls[2] = setMetaball(p1, rad, vec4(1.0, 0.0, 1.0, 1.0), uv_);
balls[3] = setMetaball(d1, rad * 0.75, vec4(1.0, 0.0, 1.0, 1.0), uv_);
for(int i = 0; i < 4; i++){
pixelPower += balls[i].power;
if(maxPower < balls[i].power){
maxPower = balls[i].power;
powerMeta = i;
}
balls[i].power *= balls[i].radius;
}
val = vec4(0.);
for(int i = 0; i < 4; i++){
vec2 distanceVector = uv_ - balls[i].position;
distanceVector = vec2(abs(distanceVector.x), abs(distanceVector.y));
float normDistance = Norm(distanceVector.x) + Norm(distanceVector.y);
balls[i].power = Norm(balls[i].radius) / normDistance;
val += balls[i].color * (balls[i].power / maxPower);
}
if(pixelPower < threshold || pixelPower > threshold + Norm(density))
{
val = vec4(0.0);
}
return vec4(val);
}
float DigitBin( const int x_ )
{
return x_==0?480599.0:x_==1?139810.0:x_==2?476951.0:x_==3?476999.0:x_==4?350020.0:x_==5?464711.0:x_==6?464727.0:x_==7?476228.0:x_==8?481111.0:x_==9?481095.0:0.0;
}
float PrintValue( const vec2 position_, const float string_, const float maxDigits_, const float decimal_ )
{
if ((position_.y < 0.0) || (position_.y >= 1.0)) return 0.0;
float fLog10Value = log2(abs(string_)) / log2(10.0);
float fBiggestIndex = max(floor(fLog10Value), 0.0);
float fDigitIndex = maxDigits_ - floor(position_.x);
float fCharBin = 0.0;
if(fDigitIndex > (-decimal_ - 1.01)) {
if(fDigitIndex > fBiggestIndex) {
if((string_ < 0.0) && (fDigitIndex < (fBiggestIndex+1.5))) fCharBin = 1792.0;
} else {
if(fDigitIndex == -1.0) {
if(decimal_ > 0.0) fCharBin = 2.0;
} else {
float fReducedRangeValue = string_;
if(fDigitIndex < 0.0) { fReducedRangeValue = fract( string_ ); fDigitIndex += 1.0; }
float fDigitValue = (abs(fReducedRangeValue / (pow(10.0, fDigitIndex))));
fCharBin = DigitBin(int(floor(mod(fDigitValue, 10.0))));
}
}
}
return floor(mod((fCharBin / pow(2.0, floor(fract(position_.x) * 4.0) + (floor(position_.y * 5.0) * 4.0))), 2.0));
}
float smooth_circle(float st, vec2 pos, float rad, vec2 uv)
{
return 1.0 - smoothstep(rad * rad - st, rad * rad, dot(uv - pos, uv - pos));
}
void main() {
vec2 fontSize = vec2(0.1, -0.1);
fragCoord = -1.0 + 2.0 * gl_PointCoord;
vec2 fragCoord2 = vec2(gl_FragCoord.x, gl_FragCoord.y);
vec2 uv = (fragCoord2 * 2.0 - resolution.xy) / resolution.y;
startPos = (outStart.xy * 0.5 + 0.5) * resolution.xy;
endPos = (outEnd.xy * 0.5 + 0.5) * resolution.xy;
vec3 debug = vec3( PrintValue( (fragCoord + vec2(1.25, -0.75)) / fontSize, gl_FragCoord.x, 5.0, 2.0) );
//startPos, endPos in screen coords (pixels)
vec3 circ = vec3(smooth_circle(1.0, startPos, 10.0, fragCoord2));
vec3 circ2 = vec3(smooth_circle(1.0, endPos, 10.0, fragCoord2));
//local: -1.0...1.0;
vec3 circ3 = vec3(smooth_circle(0.0, vec2(-1.), 0.25, fragCoord));
debug = debug + circ + circ2 + circ3;
gl_FragColor = vec4(drawMetaballs(startPos, endPos, fragCoord2));
}
</script>
<script>
THREE.TOUCH = {};
var joints, uniforms;
var circularPoint = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128px" height="128px"><circle cx="64" cy="64" r="62" fill="white" /></svg>';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 200);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xEEEEEE, 1.0);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var N = 4,
verts = [],
colors = [],
radius = [];
verts = [
new THREE.Vector3(0.0, 0.0, -4.0),
new THREE.Vector3(-4.0, 0.0, 0.0),
new THREE.Vector3(4.0, 0.0, 0.0),
new THREE.Vector3(0.0, 0.0, 4.0)
];
colors.push(0.0, 1.0, 1.0);
colors.push(1.0, 0., 1.0);
colors.push(0.847, 0.332, 0.347);
colors.push(0.457, 0.695, 0.675);
for (var i = 0; i < N; i++) {
radius.push(0.5);
}
var pointsGeometry = new THREE.BufferGeometry().setFromPoints(verts);
pointsGeometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));
pointsGeometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(radius), 1));
var pointsMaterial = new THREE.ShaderMaterial({
uniforms: {
viewport: {
value: window.innerHeight * window.devicePixelRatio
},
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
resolution: {
value: [innerWidth * window.devicePixelRatio, innerHeight * window.devicePixelRatio]
}
},
vertexShader: document.getElementById("vertexParticle").textContent,
fragmentShader: document.getElementById("fragmentParticle").textContent,
transparent: true
})
var verts2 = [],
colors2 = [],
radius2 = [];
var dist = new THREE.Vector3(-4.0, 0.0, 0.0).distanceTo(new THREE.Vector3(0.0, 0.0, -4.0));
for (var i = 0; i < 1; i++) {
for (var j = i + 1; j < 2; j++) {
var dist = verts[i].distanceTo(verts[j]);
var nv = new THREE.Vector3(verts[i].x, verts[i].y, verts[i].z);
verts2.push(nv.lerp(verts[j], 0.5));
radius2.push(dist / 2.0 + 0.5);
}
}
var jointsGeometry = new THREE.BufferGeometry().setFromPoints(verts2);
jointsGeometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(radius2), 1));
uniforms = {
viewport: {
value: innerHeight * devicePixelRatio
},
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
time: { value: 0.0 },
start: {
value: verts[0]
},
end: {
value: verts[1]
},
resolution: {
value: new THREE.Vector2(window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio)
}
};
var jointsMaterial = new THREE.ShaderMaterial({
uniforms,
vertexShader: document.getElementById("vertexJoint").textContent,
fragmentShader: document.getElementById("fragmentJoint").textContent,
transparent: true
})
jointsMaterial.extensions.derivatives = true;
jointsMaterial.extensions.fragDepth = true;
jointsMaterial.extensions.drawBuffers = true;
var points = new THREE.Points(pointsGeometry, pointsMaterial);
scene.add(points);
joints = new THREE.Points(jointsGeometry, jointsMaterial);
scene.add(joints);
renderer.setAnimationLoop(function(timestamp) {
uniforms[ "time" ].value = timestamp / 1000;
renderer.render(scene, camera)
});
</script>
</body>
</html>

THREE.JS Mouse interaction with shader

I have simple point cloud shader, which renders points as circles on screen.
vertexShader:
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPosition;
uniform sampler2D texture;
uniform vec2 mouse;
uniform vec2 resolution;
attribute vec3 position;
attribute float radius;
attribute vec3 color;
varying vec3 vColor;
void main() {
vColor = color;
vec3 pos = position;
vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
gl_Position = projected;
gl_PointSize = radius;
}
fraqShader:
precision mediump float;
varying vec3 vColor;
uniform sampler2D texture;
uniform float useColor;
uniform vec2 mouse;
uniform vec2 resolution;
void main() {
float mx = mouse.x / resolution.x;
float my = mouse.y / resolution.y;
float d = sqrt((gl_PointCoord.x - mx)*(gl_PointCoord.x - mx) + (gl_PointCoord.y - mx)*(gl_PointCoord.y - mx));
if (useColor == 1.) {
gl_FragColor = vec4(vColor, 1.0);
} else {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
gl_FragColor = gl_FragColor * texture2D(texture, gl_PointCoord);
if(d < 0.1) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); }
}
The question is: Is possible to make a mouse interaction with these circles, I mean if the distance from current mouse position is less than point radius then the color would be different?
I have tried to set mouse.x and mouse.y to event_.clientX and event_.clinetY, then pass it to the shader trying to calculate distance:
float mx = mouse.x / resolution.x;
float my = mouse.y / resolution.y;
float d = sqrt((gl_PointCoord.x - mx)*(gl_PointCoord.x - mx) + (gl_PointCoord.y - mx)*(gl_PointCoord.y - mx));
But it doesn't work. Is there any solutions?/
gl_FragCoord.xy contains the window coordinates of the fragment. The lower left is (0,0) and the upper right is the width and height of the viewport in pixels.
Probably you have to flip the y coordinate of the mouse coordinates, because at screen coordinates the upper left is (0, 0) and the bottom right is the width and height of the window:
vec2 mc = vec2(mouse.x, u_resolution.y - mouse.y);
float d = length((mc - gl_FragCoord.xy) / u_resolutuon.xy);
var circularPoint = "data:image/png;base64,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eG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDEtMjFUMTU6MzM6NDErMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI5YjZjYzMtNWNmNy00MzllLWEwNWEtZTkzYTdhY2M4NjlhIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRlNDg0MDNlLTU2MzUtNDJjOC05MWM0LWY2ZTgyMzM5NDA4OSIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlp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";
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xEEEEEE, 1.0);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var N = 2560;
var pallete = ["#FF6138", "#FFFF9D", "#BEEB9F", "#79BD8F", "#00A388"];
var verts = [], colors = [], rad = [], size = [], id = [], enabled = [];
for (let i = 0; i < N; i++) {
verts.push(getXYZ().multiplyScalar(5));
size.push(0.25 + Math.random() * 1.25);
rad.push(size[size.length - 1] * 1.0E-1 );
colors.push.apply(colors, randomRGB());
enabled.push(1);
var indx = new THREE.Color().setHex((i + 1));
id.push(indx.r, indx.g, indx.b);
}
var geometry = new THREE.BufferGeometry().setFromPoints(verts);
geometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));
geometry.addAttribute("id", new THREE.BufferAttribute(new Float32Array(id), 3));
geometry.addAttribute("size", new THREE.BufferAttribute(new Float32Array(size), 1));
geometry.addAttribute("rad", new THREE.BufferAttribute(new Float32Array(rad), 1));
geometry.addAttribute("enabled", new THREE.BufferAttribute(new Float32Array(enabled), 1));
var material = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
ori: {
value: new THREE.Vector3()
},
dir: {
value: new THREE.Vector3()
},
scale: {
value: window.innerHeight / 2
}
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
alphaTest: 0.9
})
var last_id = 0;
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var points = new THREE.Points(geometry, material);
scene.add(points);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var inverseMatrix = new THREE.Matrix4();
var ray = new THREE.Ray();
pickingScene = new THREE.Scene();
pickingTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
pickingTexture.texture.minFilter = THREE.LinearFilter;
pickingScene.add(points.clone());
renderer.domElement.addEventListener("mousemove", onMouseMove, false);
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
inverseMatrix.getInverse(points.matrixWorld);
ray.copy(raycaster.ray).applyMatrix4(inverseMatrix);
material.uniforms.ori.value = ray.origin;
material.uniforms.dir.value = ray.direction;
renderer.render(pickingScene, camera, pickingTexture);
var pixelBuffer = new Uint8Array(4);
renderer.readRenderTargetPixels(
pickingTexture, event.clientX, pickingTexture.height - event.clientY,
1, 1, pixelBuffer);
var id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | (pixelBuffer[2]);
if(id < N){
last_id = id;
console.log("highlighted node: " + id);
for(var i = 0; i < N; i++){ if(i != (id)) { enabled[i] = 0.0; } }
points.geometry.attributes.enabled.needsUpdate = true;
}else if(id != last_id){
for(var i = 0; i < N; i++){ enabled[i] = 1.0; }
points.geometry.attributes.enabled.needsUpdate = true;
}
}
renderer.setAnimationLoop(() => { renderer.render(scene, camera) });
function getXYZ(){
var n = 1E1;
var rho = Math.random();
var theta = Math.random() * Math.PI * 2;
var phi = Math.random() * Math.PI * 2;
var x = rho * Math.cos(phi) * Math.sin(theta);
var y = rho * Math.sin(phi) * Math.sin(theta);
var z = rho * Math.cos(theta);
return new THREE.Vector3(x, y, z);
}
function randomRGB() {
var i = Math.floor(Math.random() * 5);
var hex = pallete[i];
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [
parseInt(result[1], 16) / 255,
parseInt(result[2], 16) / 255,
parseInt(result[3], 16) / 255
] : null;
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.98.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.98.0/examples/js/controls/OrbitControls.js"></script>
<script type='x-shader/x-vertex' id='vertexShader'>
uniform vec3 ori;
uniform vec3 dir;
attribute float rad;
attribute float size;
attribute vec3 color;
uniform float scale;
attribute float enabled;
attribute vec3 id;
varying vec3 vColor;
vec3 closestPointToPoint() {
vec3 target = position - ori;
float distance = dot(target, dir);
return dir * distance + ori;
}
void main() {
vColor = color;
if (length(position - closestPointToPoint()) < rad) if(enabled == 1.) { vColor = id; }
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( scale / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragmentShader'>
varying vec3 vColor;
uniform sampler2D texture;
void main() {
gl_FragColor = vec4(vColor, 1.);
gl_FragColor = gl_FragColor * texture2D(texture, gl_PointCoord);
if (gl_FragColor.a < 0.1) discard;
}
</script>
That's the result I was looking for, but any tweaks are welcome.
Thanks to prisoner849, it's mainly his code.

Incorrect depthTexture with SSAO

I’ve been puzzled lately as I’ve been attempting to get a THREE.DepthTexture to work with the Ambient Occlusion shader. I’ve had it working before with RGBA unpacking, but after reading about Matt Deslauriers’s project, Audiograph, I decided to attempt the method he described for a potential performance boost:
Historically in ThreeJS, you would render your scene with
MeshDepthMaterial to a WebGLRenderTarget, and then unpack to a linear
depth value when sampling from the depth target. This is fairly
expensive and often unnecessary, since many environments support the
WEBGL_depth_texture extension.
Matt Deslauriers, Audiograph
After attempting this method, I somehow ended up with this weird unwanted effect in which lines are all over the terrain:
I have setup a small example below in which I have replicated the issue. I feel it’s something very obvious that I’m simply glossing over.
I hope someone here is able to point out what I’m missing so that I can get the ambient occlusion working in a way that is a little bit more performant!
Many thanks in advance.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 2000);
const pivot = new THREE.Object3D();
pivot.add(camera);
scene.add(pivot);
camera.position.set(0, 250, 500);
camera.lookAt(pivot.position);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
let supportsExtension = false;
if (renderer.extensions.get('WEBGL_depth_texture')) {
supportsExtension = true;
}
document.body.appendChild(renderer.domElement);
const createCube = () => {
const geo = new THREE.BoxGeometry(500, 500, 500);
const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const obj = new THREE.Mesh(geo, mat);
obj.position.y = -(obj.geometry.parameters.height / 2);
scene.add(obj);
}
const createSphere = () => {
const geo = new THREE.SphereGeometry(100, 12, 8);
const mat = new THREE.MeshBasicMaterial({ color: 0xff00ff });
const obj = new THREE.Mesh(geo, mat);
obj.position.y = obj.geometry.parameters.radius;
scene.add(obj);
}
// Create objects
createCube();
createSphere();
const composer = new THREE.EffectComposer(renderer);
const target = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
target.texture.format = THREE.RGBFormat;
target.texture.minFilter = THREE.NearestFilter;
target.texture.magFilter = THREE.NearestFilter;
target.texture.generateMipmaps = false;
target.stencilBuffer = false;
target.depthBuffer = true;
target.depthTexture = new THREE.DepthTexture();
target.depthTexture.type = THREE.UnsignedShortType;
function initPostProcessing() {
composer.addPass(new THREE.RenderPass( scene, camera ));
const pass = new THREE.ShaderPass({
uniforms: {
"tDiffuse": { value: null },
"tDepth": { value: target.depthTexture },
"resolution": { value: new THREE.Vector2( 512, 512 ) },
"cameraNear": { value: 1 },
"cameraFar": { value: 100 },
"onlyAO": { value: 0 },
"aoClamp": { value: 0.5 },
"lumInfluence": { value: 0.5 }
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
});
pass.material.precision = 'highp';
composer.addPass(pass);
pass.uniforms.tDepth.value = target.depthTexture;
pass.uniforms.cameraNear.value = camera.near;
pass.uniforms.cameraFar.value = camera.far;
composer.passes[composer.passes.length - 1].renderToScreen = true;
}
initPostProcessing();
const animate = () => {
requestAnimationFrame( animate );
pivot.rotation.y += 0.01;
renderer.render( scene, camera, target );
composer.render();
}
animate();
html, body { margin: 0; }
canvas { display: block; width: 100%; height: 100%; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/shaders/CopyShader.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform float cameraNear;
uniform float cameraFar;
uniform bool onlyAO; // use only ambient occlusion pass?
uniform vec2 resolution; // texture width, height
uniform float aoClamp; // depth clamp - reduces haloing at screen edges
uniform float lumInfluence; // how much luminance affects occlusion
uniform sampler2D tDiffuse;
uniform highp sampler2D tDepth;
varying vec2 vUv;
// #define PI 3.14159265
#define DL 2.399963229728653 // PI * ( 3.0 - sqrt( 5.0 ) )
#define EULER 2.718281828459045
// user variables
const int samples = 4; // ao sample count
const float radius = 5.0; // ao radius
const bool useNoise = false; // use noise instead of pattern for sample dithering
const float noiseAmount = 0.0003; // dithering amount
const float diffArea = 0.4; // self-shadowing reduction
const float gDisplace = 0.4; // gauss bell center
highp vec2 rand( const vec2 coord ) {
highp vec2 noise;
if ( useNoise ) {
float nx = dot ( coord, vec2( 12.9898, 78.233 ) );
float ny = dot ( coord, vec2( 12.9898, 78.233 ) * 2.0 );
noise = clamp( fract ( 43758.5453 * sin( vec2( nx, ny ) ) ), 0.0, 1.0 );
} else {
highp float ff = fract( 1.0 - coord.s * ( resolution.x / 2.0 ) );
highp float gg = fract( coord.t * ( resolution.y / 2.0 ) );
noise = vec2( 0.25, 0.75 ) * vec2( ff ) + vec2( 0.75, 0.25 ) * gg;
}
return ( noise * 2.0 - 1.0 ) * noiseAmount;
}
float readDepth( const in vec2 coord ) {
float cameraFarPlusNear = cameraFar + cameraNear;
float cameraFarMinusNear = cameraFar - cameraNear;
float cameraCoef = 2.0 * cameraNear;
return cameraCoef / ( cameraFarPlusNear - texture2D( tDepth, coord ).x * cameraFarMinusNear );
}
float compareDepths( const in float depth1, const in float depth2, inout int far ) {
float garea = 2.0; // gauss bell width
float diff = ( depth1 - depth2 ) * 100.0; // depth difference (0-100)
// reduce left bell width to avoid self-shadowing
if ( diff < gDisplace ) {
garea = diffArea;
} else {
far = 1;
}
float dd = diff - gDisplace;
float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );
return gauss;
}
float calcAO( float depth, float dw, float dh ) {
float dd = radius - depth * radius;
vec2 vv = vec2( dw, dh );
vec2 coord1 = vUv + dd * vv;
vec2 coord2 = vUv - dd * vv;
float temp1 = 0.0;
float temp2 = 0.0;
int far = 0;
temp1 = compareDepths( depth, readDepth( coord1 ), far );
// DEPTH EXTRAPOLATION
if ( far > 0 ) {
temp2 = compareDepths( readDepth( coord2 ), depth, far );
temp1 += ( 1.0 - temp1 ) * temp2;
}
return temp1;
}
void main() {
highp vec2 noise = rand( vUv );
float depth = readDepth( vUv );
float tt = clamp( depth, aoClamp, 1.0 );
float w = ( 1.0 / resolution.x ) / tt + ( noise.x * ( 1.0 - noise.x ) );
float h = ( 1.0 / resolution.y ) / tt + ( noise.y * ( 1.0 - noise.y ) );
float ao = 0.0;
float dz = 1.0 / float( samples );
float z = 1.0 - dz / 2.0;
float l = 0.0;
for ( int i = 0; i <= samples; i ++ ) {
float r = sqrt( 1.0 - z );
float pw = cos( l ) * r;
float ph = sin( l ) * r;
ao += calcAO( depth, pw * w, ph * h );
z = z - dz;
l = l + DL;
}
ao /= float( samples );
ao = 1.0 - ao;
vec3 color = texture2D( tDiffuse, vUv ).rgb;
vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );
float lum = dot( color.rgb, lumcoeff );
vec3 luminance = vec3( lum );
vec3 final = vec3( color * mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) ); // mix( color * ao, white, luminance )
float depth2 = readDepth(vUv);
if ( onlyAO ) {
final = vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) ); // ambient occlusion only
}
// gl_FragColor = vec4( vec3( readDepth( vUv) ), 1.0 ); // Depth
gl_FragColor = vec4( final, 1.0 );
}
</script>
I'd love to hear what is causing my Ambient Occlusion to not render properly!
If you are using a perspective camera and relying on the depth map for any purpose -- that includes SSAO and shadows -- be careful of your choice of camera.near and camera.far -- especially near. ( That would be shadow.camera.near if you are dealing with shadows.)
Push the near plane out as far as is reasonable for your use case. You will achieve the best results if your scene is positioned near the front of the frustum.
three.js r.86

Resources