How to make point always look at sphere zero point

I wanna let each point look at to zero point, but they always face to camera place.

const vert = `
uniform float uPointSize;
uniform float uTime;
uniform float uEnableAnimation;

attribute float type;
attribute float startTime;
attribute vec3 pointColor;

varying float vAlpha;
varying vec3 vColor;

void main() {
	float p = fract(uTime - startTime);
	vAlpha = 1.0 - p;
  gl_PointSize = p * uPointSize;

	vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
	gl_Position = projectionMatrix * mvPosition;

	vColor = vec3(pointColor);
}`;
const frag = `
uniform sampler2D uPointTexture;
varying float vAlpha;
varying vec3 vColor;

void main() {
  if (vAlpha <= 0.0) {
  	discard;
  }
  vec4 texColor = texture2D(uPointTexture, gl_PointCoord);
  gl_FragColor = vec4(vColor, texColor.a * vAlpha);
}`;

const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {
      value: 0,
    },
    uPointSize: {
      value: 50,
    },
    uPointTexture: {
      value: new THREE.TextureLoader().load(image),
    },
  },
  vertexShader: vert,
  fragmentShader: frag,
  vertexColors: true,
  transparent: true,
  depthWrite: false,
  side: THREE.DoubleSide,
});
const geometry = new THREE.BufferGeometry();
const positionSize = 20;
geometry.setAttribute(
  "position",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
geometry.setAttribute(
  "startTime",
  new THREE.BufferAttribute(new Float32Array(positionSize), 1)
);
geometry.setAttribute(
  "pointColor",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
const mesh = new THREE.Points(geometry, material);

position.setXYZ(0, 100, 100, 100);
position.needsUpdate = true;

here is full exmaple, https://codepen.io/p10y/pen/VwMMWvR

You need for custom billboard shader. Point mesh always face to camera like default sprite mesh.


Try this:

var vs=[];
var fs=[];
var mesh=[];
var tex=[];
var mat=[];

var texture_loader=new THREE.TextureLoader();


const canvas = document.querySelector('#app');
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  transparent:true,
  canvas: canvas,
  context: context,
});
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height, false);
const camera = new THREE.PerspectiveCamera(
  45,
  width / height,
  1,
  100000
);
camera.position.z = 500;
const scene = new THREE.Scene();
const pl1 = new THREE.PointLight(0xffffff, 0.8);
pl1.position.set(
  -200,
  200,
  200,
);
scene.add(pl1);
const clock = new THREE.Clock();
let elapsedTime;

const group = new THREE.Object3D();
group.add(
  new THREE.Mesh(
    new THREE.SphereGeometry(100, 32, 32),
    new THREE.MeshPhongMaterial({ 
      color: 0xfffaaa,
      transparent: true,
      opacity: 1,
    })
  )
);
scene.add(group);

group.add(
  new THREE.Line(
    new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(-100, 0, 0),
      new THREE.Vector3(0, 100, 0),
      new THREE.Vector3(100, 0, 0)
    ]), 
    new THREE.LineBasicMaterial({
      color: 0x0000ff
    })
  )
);

const vert = `
uniform float uPointSize;
uniform float uTime;
uniform float uEnableAnimation;

attribute float type;
attribute float startTime;
attribute vec3 pointColor;

varying float vAlpha;
varying vec3 vColor;

void main() {
	float p = fract(uTime - startTime);
	vAlpha = 1.0 - p;
  gl_PointSize = p * uPointSize;

	vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
	gl_Position = projectionMatrix * mvPosition;

	vColor = vec3(pointColor);
}`;
const frag = `
uniform sampler2D uPointTexture;
varying float vAlpha;
varying vec3 vColor;

void main() {
  if (vAlpha <= 0.0) {
  	discard;
  }
  vec4 texColor = texture2D(uPointTexture, gl_PointCoord);
  gl_FragColor = vec4(vColor, texColor.a * vAlpha);
}`;
 
const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {
      value: 0,
    },
    uPointSize: {
      value: 50,
    },
    uPointTexture: {
      value: new THREE.TextureLoader().load(image),
    },
  },
  vertexShader: vert,
  fragmentShader: frag,
  vertexColors: true,
  transparent: true,
  depthWrite: false,
  side: THREE.DoubleSide,
});



var geometry = new THREE.BufferGeometry();
const positionSize = 20;
geometry.setAttribute(
  "position",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
geometry.setAttribute(
  "startTime",
  new THREE.BufferAttribute(new Float32Array(positionSize), 1)
);
geometry.setAttribute(
  "pointColor",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
mesh["points"] = new THREE.Points(geometry, material);
group.add(mesh["points"]);



const position = geometry.attributes.position;
Array.from(Array(positionSize)).forEach((_, i) => {
  position.setXYZ(
    i,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
  );
});
position.needsUpdate = true;


tex["one"]=texture_loader.load(image);
tex["two"]=texture_loader.load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABPUlEQVR42mNgoDpovvF/2v//qVtf8wpLEVatYuu75fv/////P/3/v+7RfwYGBib8Gt49uf/hLwMDA4MUA4MlP4NReCYhDfevnLr048lfBgYGBnc+Bgkzf8Ku4uAVKb3+/8v//////8898JuJoIYfn98sCVaa8IiBgYHh969vTGzc/AT1PL92/+VThh8MDK/vX2b68/MbMYF7cnblPQaGL08eMP3785sYDe4F7U++Mby7c5KBgYEhrGs2ftU6niEr3v6f+wMWDyzskj33/uNSLSAlm7Zg9X8WhtX1UxGiXXf+TnrxX8XaGU21VWz+1Df/57//X7D9IUSEES5XtPmmrofa+xcM+6dN+/LusbCcrkN6FBMTAzcvw45pe5bnu2KxXVrXPmrCqr6H/5f+/7/o///GM/+DWxeycQtRlHgBrNiEw8eNQ4gAAAAASUVORK5CYII=");


vs["sprite"]=`


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


float angle=time*rotation;
float p=fract(time-sin(length(offset)));


vec3 vRotated=vec3(position.x*scale.x*p*cos(angle)-position.y*scale.y*p*sin(angle),position.y*scale.y*p*cos(angle)+position.x*scale.x*p*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vColor.w=1.0-p;


vec3 eye=offset;
vec3 target=quaternion.xyz;
vec3 up=vec3(0.0,1.0,0.0);
vec3 zaxis=normalize(eye-target);


if(length(zaxis)==0.0){
// eye and target are in the same position
zaxis.z=1.0;
}
zaxis=normalize(zaxis);
vec3 xaxis=cross(up,zaxis);


if(length(xaxis)==0.0) {
// up and z are parallel
if (abs(up.z)==1.0){ zaxis.x +=0.0001; }
else{ zaxis.z +=0.0001; }
zaxis=normalize(zaxis);
xaxis=cross(up, zaxis);
}
xaxis=normalize(xaxis);
vec3 yaxis=cross(zaxis,xaxis);


float m11=xaxis.x,m12=yaxis.x,m13=zaxis.x,m21=xaxis.y,m22=yaxis.y,m23=zaxis.y,m31=xaxis.z,m32=yaxis.z,m33=zaxis.z;


float s,qx,qy,qz,qw;
float trace=m11+m22+m33;


if(trace>0.0){ s=0.5/sqrt(trace+1.0); qw=0.25/s; qx=(m32-m23)*s; qy=(m13-m31)*s; qz=(m21-m12)*s; }
else if(m11>m22 && m11>m33){ s=2.0*sqrt(1.0+m11-m22-m33); qw=(m32-m23)/s; qx=0.25*s; qy=(m12+m21)/s; qz=(m13+m31)/s; }
else if(m22>m33){ s=2.0*sqrt(1.0+m22-m11-m33); qw=(m13-m31)/s; qx=(m12+m21)/s; qy=0.25*s; qz=(m23+m32)/s; }
else{ s=2.0*sqrt(1.0+m33-m11-m22); qw=(m21-m12)/s; qx=(m13+m31)/s; qy=(m23+m32)/s; qz=0.25*s; }


vec4 vQuaternion=vec4(qx,qy,qz,qw);


vec3 vcV=cross(vQuaternion.xyz,vRotated);
vec3 vPosition=vcV*(2.0*vQuaternion.w)+(cross(vQuaternion.xyz,vcV)*2.0+vRotated);

gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);


}


`;


fs["sprite"]=`


const int count=2;
uniform sampler2D map[count];
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


if(num==0.0){ gl_FragColor=texture2D(map[0],vUv)*vColor; }
else if(num==1.0){ gl_FragColor=texture2D(map[1],vUv)*vColor; }


gl_FragColor.rgb*=gl_FragColor.a;
gl_FragColor.a*=vBlend;


}


`;


var particles=[];


function particles_update(){


particles=[];


var max_1=particles_flare_a.length;
particles.length=max_1;
for(var n=0;n<max_1;n++){
particles[n]=particles_flare_a[n];
}


var count=particles.length;
var item=camera.position;
var x=item.x;
var y=item.y;
var z=item.z;


for(var n=0;n<count;n++){
var item=particles[n].offset;
particles[n].d=Math.sqrt(Math.pow((x-item[0]),2)+Math.pow((y-item[1]),2)+Math.pow((z-item[2]),2));
}


particles.sort((a,b)=>b.d-a.d);


var offset=new Float32Array(count*3);
var scale=new Float32Array(count*2);
var quaternion=new Float32Array(count*4);
var rotation=new Float32Array(count);
var color=new Float32Array(count*4);
var blend=new Float32Array(count);
var texture=new Float32Array(count);


for(var n=0;n<count;n++){


// 1 VALUE
var item=particles[n];
rotation[n]=item.rotation;
texture[n]=item.texture;
blend[n]=item.blend;


// 2 VALUE
var p=n*2;
var one=p+1;
var i_scale=item.scale;
scale[p]=i_scale[0];
scale[one]=i_scale[1];


// 3 VALUE
var p=n*3;
var one=p+1;
var two=p+2;
var i_offset=item.offset;
offset[p]=i_offset[0];
offset[one]=i_offset[1];
offset[two]=i_offset[2];


// 4 VALUE
var p=n*4;
var one=p+1;
var two=p+2;
var three=p+3;
var i_color=item.color;
color[p]=i_color[0];
color[one]=i_color[1];
color[two]=i_color[2];
color[three]=i_color[3];
var i_quaternion=item.quaternion;
quaternion[p]=i_quaternion[0];
quaternion[one]=i_quaternion[1];
quaternion[two]=i_quaternion[2];
quaternion[three]=i_quaternion[3];


}


var item=mesh["sprite"].geometry.attributes;
item.offset=new THREE.InstancedBufferAttribute(offset,3).setUsage(THREE.DynamicDrawUsage);
item.scale=new THREE.InstancedBufferAttribute(scale,2).setUsage(THREE.DynamicDrawUsage);
item.quaternion=new THREE.InstancedBufferAttribute(quaternion,4).setUsage(THREE.DynamicDrawUsage);
item.rotation=new THREE.InstancedBufferAttribute(rotation,1).setUsage(THREE.DynamicDrawUsage);
item.color=new THREE.InstancedBufferAttribute(color,4).setUsage(THREE.DynamicDrawUsage);
item.blend=new THREE.InstancedBufferAttribute(blend,1).setUsage(THREE.DynamicDrawUsage);
item.texture=new THREE.InstancedBufferAttribute(texture,1).setUsage(THREE.DynamicDrawUsage);


mesh["sprite"].geometry._maxInstanceCount=count;


}


var geometry=new THREE.InstancedBufferGeometry();
geometry.setAttribute('position',new THREE.Float32BufferAttribute(new Float32Array([-0.5,0.5,0,-0.5,-0.5,0,0.5,0.5,0,0.5,-0.5,0,0.5,0.5,0,-0.5,-0.5,0]),3));
geometry.setAttribute('uv',new THREE.Float32BufferAttribute(new Float32Array([0,1,0,0,1,1,1,0,1,1,0,0]),2));
geometry.setAttribute('offset',new THREE.InstancedBufferAttribute(new Float32Array(),3));
geometry.setAttribute('scale',new THREE.InstancedBufferAttribute(new Float32Array(),2));
geometry.setAttribute('quaternion',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('rotation',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('color',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('blend',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('texture',new THREE.InstancedBufferAttribute(new Float32Array(),1));


mat["sprite"]=new THREE.ShaderMaterial({
uniforms:{
map:{value:[tex["one"],tex["two"]]},
time:{value:0}
},
vertexShader:vs["sprite"],
fragmentShader:fs["sprite"],
side:THREE.DoubleSide,
transparent:true,
depthWrite:false,
blending:THREE.CustomBlending,
blendEquation:THREE.AddEquation,
blendSrc:THREE.OneFactor,
blendDst:THREE.OneMinusSrcAlphaFactor
});


mesh["sprite"]=new THREE.Mesh(geometry,mat["sprite"]);
mesh["sprite"].frustumCulled=false;
//mesh["sprite"].matrixAutoUpdate=false;
//mesh["sprite"].updateMatrixWorld=function(){};
scene.add(mesh["sprite"]);


var particles_flare_a=[];


particles_flare_a.push({offset:[100,0,0],scale:[100,100],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:1});
particles_flare_a.push({offset:[-100,0,0],scale:[20,30],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:0});
particles_flare_a.push({offset:[100,100,0],scale:[60,40],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:1});


for(var n=0;n<10;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[100,100],quaternion:[0,0,0,1],rotation:10,color:[Math.random(),Math.random(),Math.random(),1],blend:1,texture:1});
}


for(var n=0;n<100;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[30,30],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:0});
}


const update = () => {
  const delta = clock.getDelta();
  elapsedTime = clock.elapsedTime;
  material.uniforms.uTime.value = elapsedTime;
  group.rotation.y += 0.01;
  particles_update();
  mat["sprite"].uniforms.time.value = elapsedTime;
  mesh["sprite"].rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(update);
}
update();

Thanks, Very appreciate!

Optimized vertex shader:

vs["sprite"]=`


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


float angle=time*rotation;
float p=fract(time-sin(length(offset)));


vec3 vRotated=vec3(position.x*scale.x*p*cos(angle)-position.y*scale.y*p*sin(angle),position.y*scale.y*p*cos(angle)+position.x*scale.x*p*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vColor.w=1.0-p;


vec3 localUpVector=vec3(0.0,1.0,0.0);
vec3 vector=quaternion.xyz-offset;
if(length(vector)==0.0){ vector.z=1.0; }
vec3 zaxis=normalize(vector);
vec3 xaxis=normalize(cross(localUpVector,zaxis));
vec3 yaxis=cross(zaxis,xaxis);
mat3 lookAtMatrix=mat3(xaxis,yaxis,zaxis);
vec3 vPosition=lookAtMatrix*vRotated;


gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);


}


`;

Full code:

var vs=[];
var fs=[];
var mesh=[];
var tex=[];
var mat=[];

var texture_loader=new THREE.TextureLoader();


const canvas = document.querySelector('#app');
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  transparent:true,
  canvas: canvas,
  context: context,
});
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height, false);
const camera = new THREE.PerspectiveCamera(
  45,
  width / height,
  1,
  100000
);
camera.position.z = 500;
const scene = new THREE.Scene();
const pl1 = new THREE.PointLight(0xffffff, 0.8);
pl1.position.set(
  -200,
  200,
  200,
);
scene.add(pl1);
const clock = new THREE.Clock();
let elapsedTime;

const group = new THREE.Object3D();
group.add(
  new THREE.Mesh(
    new THREE.SphereGeometry(100, 32, 32),
    new THREE.MeshPhongMaterial({ 
      color: 0xfffaaa,
      transparent: true,
      opacity: 1,
    })
  )
);
scene.add(group);

group.add(
  new THREE.Line(
    new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(-100, 0, 0),
      new THREE.Vector3(0, 100, 0),
      new THREE.Vector3(100, 0, 0)
    ]), 
    new THREE.LineBasicMaterial({
      color: 0x0000ff
    })
  )
);

const vert = `
uniform float uPointSize;
uniform float uTime;
uniform float uEnableAnimation;

attribute float type;
attribute float startTime;
attribute vec3 pointColor;

varying float vAlpha;
varying vec3 vColor;

void main() {
	float p = fract(uTime - startTime);
	vAlpha = 1.0 - p;
  gl_PointSize = p * uPointSize;

	vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
	gl_Position = projectionMatrix * mvPosition;

	vColor = vec3(pointColor);
}`;
const frag = `
uniform sampler2D uPointTexture;
varying float vAlpha;
varying vec3 vColor;

void main() {
  if (vAlpha <= 0.0) {
  	discard;
  }
  vec4 texColor = texture2D(uPointTexture, gl_PointCoord);
  gl_FragColor = vec4(vColor, texColor.a * vAlpha);
}`;
 
const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {
      value: 0,
    },
    uPointSize: {
      value: 50,
    },
    uPointTexture: {
      value: new THREE.TextureLoader().load(image),
    },
  },
  vertexShader: vert,
  fragmentShader: frag,
  vertexColors: true,
  transparent: true,
  depthWrite: false,
  side: THREE.DoubleSide,
});



var geometry = new THREE.BufferGeometry();
const positionSize = 20;
geometry.setAttribute(
  "position",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
geometry.setAttribute(
  "startTime",
  new THREE.BufferAttribute(new Float32Array(positionSize), 1)
);
geometry.setAttribute(
  "pointColor",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
mesh["points"] = new THREE.Points(geometry, material);
group.add(mesh["points"]);



const position = geometry.attributes.position;
Array.from(Array(positionSize)).forEach((_, i) => {
  position.setXYZ(
    i,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
  );
});
position.needsUpdate = true;


tex["one"]=texture_loader.load(image);
tex["two"]=texture_loader.load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABPUlEQVR42mNgoDpovvF/2v//qVtf8wpLEVatYuu75fv/////P/3/v+7RfwYGBib8Gt49uf/hLwMDA4MUA4MlP4NReCYhDfevnLr048lfBgYGBnc+Bgkzf8Ku4uAVKb3+/8v//////8898JuJoIYfn98sCVaa8IiBgYHh969vTGzc/AT1PL92/+VThh8MDK/vX2b68/MbMYF7cnblPQaGL08eMP3785sYDe4F7U++Mby7c5KBgYEhrGs2ftU6niEr3v6f+wMWDyzskj33/uNSLSAlm7Zg9X8WhtX1UxGiXXf+TnrxX8XaGU21VWz+1Df/57//X7D9IUSEES5XtPmmrofa+xcM+6dN+/LusbCcrkN6FBMTAzcvw45pe5bnu2KxXVrXPmrCqr6H/5f+/7/o///GM/+DWxeycQtRlHgBrNiEw8eNQ4gAAAAASUVORK5CYII=");


vs["sprite"]=`


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


float angle=time*rotation;
float p=fract(time-sin(length(offset)));


vec3 vRotated=vec3(position.x*scale.x*p*cos(angle)-position.y*scale.y*p*sin(angle),position.y*scale.y*p*cos(angle)+position.x*scale.x*p*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vColor.w=1.0-p;


vec3 localUpVector=vec3(0.0,1.0,0.0);
vec3 vector=quaternion.xyz-offset;
if(length(vector)==0.0){ vector.z=1.0; }
vec3 zaxis=normalize(vector);
vec3 xaxis=normalize(cross(localUpVector,zaxis));
vec3 yaxis=cross(zaxis,xaxis);
mat3 lookAtMatrix=mat3(xaxis,yaxis,zaxis);
vec3 vPosition=lookAtMatrix*vRotated;


gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);


}


`;


fs["sprite"]=`


const int count=2;
uniform sampler2D map[count];
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


if(num==0.0){ gl_FragColor=texture2D(map[0],vUv)*vColor; }
else if(num==1.0){ gl_FragColor=texture2D(map[1],vUv)*vColor; }


gl_FragColor.rgb*=gl_FragColor.a;
gl_FragColor.a*=vBlend;


}


`;


var particles=[];


function particles_update(){


particles=[];


var max_1=particles_flare_a.length;
particles.length=max_1;
for(var n=0;n<max_1;n++){
particles[n]=particles_flare_a[n];
}


var count=particles.length;
var item=camera.position;
var x=item.x;
var y=item.y;
var z=item.z;


for(var n=0;n<count;n++){
var item=particles[n].offset;
particles[n].d=Math.sqrt(Math.pow((x-item[0]),2)+Math.pow((y-item[1]),2)+Math.pow((z-item[2]),2));
}


particles.sort((a,b)=>b.d-a.d);


var offset=new Float32Array(count*3);
var scale=new Float32Array(count*2);
var quaternion=new Float32Array(count*4);
var rotation=new Float32Array(count);
var color=new Float32Array(count*4);
var blend=new Float32Array(count);
var texture=new Float32Array(count);


for(var n=0;n<count;n++){


// 1 VALUE
var item=particles[n];
rotation[n]=item.rotation;
texture[n]=item.texture;
blend[n]=item.blend;


// 2 VALUE
var p=n*2;
var one=p+1;
var i_scale=item.scale;
scale[p]=i_scale[0];
scale[one]=i_scale[1];


// 3 VALUE
var p=n*3;
var one=p+1;
var two=p+2;
var i_offset=item.offset;
offset[p]=i_offset[0];
offset[one]=i_offset[1];
offset[two]=i_offset[2];


// 4 VALUE
var p=n*4;
var one=p+1;
var two=p+2;
var three=p+3;
var i_color=item.color;
color[p]=i_color[0];
color[one]=i_color[1];
color[two]=i_color[2];
color[three]=i_color[3];
var i_quaternion=item.quaternion;
quaternion[p]=i_quaternion[0];
quaternion[one]=i_quaternion[1];
quaternion[two]=i_quaternion[2];
quaternion[three]=i_quaternion[3];


}


var item=mesh["sprite"].geometry.attributes;
item.offset=new THREE.InstancedBufferAttribute(offset,3).setUsage(THREE.DynamicDrawUsage);
item.scale=new THREE.InstancedBufferAttribute(scale,2).setUsage(THREE.DynamicDrawUsage);
item.quaternion=new THREE.InstancedBufferAttribute(quaternion,4).setUsage(THREE.DynamicDrawUsage);
item.rotation=new THREE.InstancedBufferAttribute(rotation,1).setUsage(THREE.DynamicDrawUsage);
item.color=new THREE.InstancedBufferAttribute(color,4).setUsage(THREE.DynamicDrawUsage);
item.blend=new THREE.InstancedBufferAttribute(blend,1).setUsage(THREE.DynamicDrawUsage);
item.texture=new THREE.InstancedBufferAttribute(texture,1).setUsage(THREE.DynamicDrawUsage);


mesh["sprite"].geometry._maxInstanceCount=count;


}


var geometry=new THREE.InstancedBufferGeometry();
geometry.setAttribute('position',new THREE.Float32BufferAttribute(new Float32Array([-0.5,0.5,0,-0.5,-0.5,0,0.5,0.5,0,0.5,-0.5,0,0.5,0.5,0,-0.5,-0.5,0]),3));
geometry.setAttribute('uv',new THREE.Float32BufferAttribute(new Float32Array([0,1,0,0,1,1,1,0,1,1,0,0]),2));
geometry.setAttribute('offset',new THREE.InstancedBufferAttribute(new Float32Array(),3));
geometry.setAttribute('scale',new THREE.InstancedBufferAttribute(new Float32Array(),2));
geometry.setAttribute('quaternion',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('rotation',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('color',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('blend',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('texture',new THREE.InstancedBufferAttribute(new Float32Array(),1));


mat["sprite"]=new THREE.ShaderMaterial({
uniforms:{
map:{value:[tex["one"],tex["two"]]},
time:{value:0}
},
vertexShader:vs["sprite"],
fragmentShader:fs["sprite"],
side:THREE.DoubleSide,
transparent:true,
depthWrite:false,
blending:THREE.CustomBlending,
blendEquation:THREE.AddEquation,
blendSrc:THREE.OneFactor,
blendDst:THREE.OneMinusSrcAlphaFactor
});


mesh["sprite"]=new THREE.Mesh(geometry,mat["sprite"]);
mesh["sprite"].frustumCulled=false;
//mesh["sprite"].matrixAutoUpdate=false;
//mesh["sprite"].updateMatrixWorld=function(){};
scene.add(mesh["sprite"]);


var particles_flare_a=[];


particles_flare_a.push({offset:[100,0,0],scale:[100,100],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:1});
particles_flare_a.push({offset:[-100,0,0],scale:[20,30],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:0});
particles_flare_a.push({offset:[100,100,0],scale:[60,40],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:1});


for(var n=0;n<10;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[100,100],quaternion:[0,0,0,1],rotation:10,color:[Math.random(),Math.random(),Math.random(),1],blend:1,texture:1});
}


for(var n=0;n<100;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[30,30],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:0});
}


const update = () => {
  const delta = clock.getDelta();
  elapsedTime = clock.elapsedTime;
  material.uniforms.uTime.value = elapsedTime;
  group.rotation.y += 0.01;
  particles_update();
  mat["sprite"].uniforms.time.value = elapsedTime;
  mesh["sprite"].rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(update);
}
update();

Most optimized vertex shader:

vs["sprite"]=`


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


float angle=time*rotation;
float p=fract(time-sin(length(offset)));


vec3 vRotated=vec3(position.x*scale.x*p*cos(angle)-position.y*scale.y*p*sin(angle),position.y*scale.y*p*cos(angle)+position.x*scale.x*p*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vColor.w=1.0-p;


vec3 localUpVector=vec3(0.0,1.0,0.0);
vec3 vLook=normalize(quaternion.xyz-cameraPosition);
vec3 vRight=normalize(cross(vLook,localUpVector));
vec3 vUp=normalize(cross(vLook,vRight));
vec3 vPosition=vRight*vRotated.x+vUp*vRotated.y+vLook*vRotated.z;


gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);
}
`;

Full code:

var vs=[];
var fs=[];
var mesh=[];
var tex=[];
var mat=[];

var texture_loader=new THREE.TextureLoader();


const canvas = document.querySelector('#app');
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  transparent:true,
  canvas: canvas,
  context: context,
});
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height, false);
const camera = new THREE.PerspectiveCamera(
  45,
  width / height,
  1,
  100000
);
camera.position.z = 500;
const scene = new THREE.Scene();
const pl1 = new THREE.PointLight(0xffffff, 0.8);
pl1.position.set(
  -200,
  200,
  200,
);
scene.add(pl1);
const clock = new THREE.Clock();
let elapsedTime;

const group = new THREE.Object3D();
group.add(
  new THREE.Mesh(
    new THREE.SphereGeometry(100, 32, 32),
    new THREE.MeshPhongMaterial({ 
      color: 0xfffaaa,
      transparent: true,
      opacity: 1,
    })
  )
);
scene.add(group);

group.add(
  new THREE.Line(
    new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(-100, 0, 0),
      new THREE.Vector3(0, 100, 0),
      new THREE.Vector3(100, 0, 0)
    ]), 
    new THREE.LineBasicMaterial({
      color: 0x0000ff
    })
  )
);

const vert = `
uniform float uPointSize;
uniform float uTime;
uniform float uEnableAnimation;

attribute float type;
attribute float startTime;
attribute vec3 pointColor;

varying float vAlpha;
varying vec3 vColor;

void main() {
	float p = fract(uTime - startTime);
	vAlpha = 1.0 - p;
  gl_PointSize = p * uPointSize;

	vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
	gl_Position = projectionMatrix * mvPosition;

	vColor = vec3(pointColor);
}`;
const frag = `
uniform sampler2D uPointTexture;
varying float vAlpha;
varying vec3 vColor;

void main() {
  if (vAlpha <= 0.0) {
  	discard;
  }
  vec4 texColor = texture2D(uPointTexture, gl_PointCoord);
  gl_FragColor = vec4(vColor, texColor.a * vAlpha);
}`;
 
const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {
      value: 0,
    },
    uPointSize: {
      value: 50,
    },
    uPointTexture: {
      value: new THREE.TextureLoader().load(image),
    },
  },
  vertexShader: vert,
  fragmentShader: frag,
  vertexColors: true,
  transparent: true,
  depthWrite: false,
  side: THREE.DoubleSide,
});



var geometry = new THREE.BufferGeometry();
const positionSize = 20;
geometry.setAttribute(
  "position",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
geometry.setAttribute(
  "startTime",
  new THREE.BufferAttribute(new Float32Array(positionSize), 1)
);
geometry.setAttribute(
  "pointColor",
  new THREE.BufferAttribute(new Float32Array(positionSize * 3), 3)
);
mesh["points"] = new THREE.Points(geometry, material);
group.add(mesh["points"]);



const position = geometry.attributes.position;
Array.from(Array(positionSize)).forEach((_, i) => {
  position.setXYZ(
    i,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
    Math.random() * 210 - 105,
  );
});
position.needsUpdate = true;


tex["one"]=texture_loader.load(image);
tex["two"]=texture_loader.load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABPUlEQVR42mNgoDpovvF/2v//qVtf8wpLEVatYuu75fv/////P/3/v+7RfwYGBib8Gt49uf/hLwMDA4MUA4MlP4NReCYhDfevnLr048lfBgYGBnc+Bgkzf8Ku4uAVKb3+/8v//////8898JuJoIYfn98sCVaa8IiBgYHh969vTGzc/AT1PL92/+VThh8MDK/vX2b68/MbMYF7cnblPQaGL08eMP3785sYDe4F7U++Mby7c5KBgYEhrGs2ftU6niEr3v6f+wMWDyzskj33/uNSLSAlm7Zg9X8WhtX1UxGiXXf+TnrxX8XaGU21VWz+1Df/57//X7D9IUSEES5XtPmmrofa+xcM+6dN+/LusbCcrkN6FBMTAzcvw45pe5bnu2KxXVrXPmrCqr6H/5f+/7/o///GM/+DWxeycQtRlHgBrNiEw8eNQ4gAAAAASUVORK5CYII=");


vs["sprite"]=`


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


float angle=time*rotation;
float p=fract(time-sin(length(offset)));


vec3 vRotated=vec3(position.x*scale.x*p*cos(angle)-position.y*scale.y*p*sin(angle),position.y*scale.y*p*cos(angle)+position.x*scale.x*p*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vColor.w=1.0-p;


vec3 localUpVector=vec3(0.0,1.0,0.0);
vec3 vLook=normalize(quaternion.xyz-cameraPosition);
vec3 vRight=normalize(cross(vLook,localUpVector));
vec3 vUp=normalize(cross(vLook,vRight));
vec3 vPosition=vRight*vRotated.x+vUp*vRotated.y+vLook*vRotated.z;


gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);


}


`;


fs["sprite"]=`


const int count=2;
uniform sampler2D map[count];
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


if(num==0.0){ gl_FragColor=texture2D(map[0],vUv)*vColor; }
else if(num==1.0){ gl_FragColor=texture2D(map[1],vUv)*vColor; }


gl_FragColor.rgb*=gl_FragColor.a;
gl_FragColor.a*=vBlend;


}


`;


var particles=[];


function particles_update(){


particles=[];


var max_1=particles_flare_a.length;
particles.length=max_1;
for(var n=0;n<max_1;n++){
particles[n]=particles_flare_a[n];
}


var count=particles.length;
var item=camera.position;
var x=item.x;
var y=item.y;
var z=item.z;


for(var n=0;n<count;n++){
var item=particles[n].offset;
particles[n].d=Math.sqrt(Math.pow((x-item[0]),2)+Math.pow((y-item[1]),2)+Math.pow((z-item[2]),2));
}


particles.sort((a,b)=>b.d-a.d);


var offset=new Float32Array(count*3);
var scale=new Float32Array(count*2);
var quaternion=new Float32Array(count*4);
var rotation=new Float32Array(count);
var color=new Float32Array(count*4);
var blend=new Float32Array(count);
var texture=new Float32Array(count);


for(var n=0;n<count;n++){


// 1 VALUE
var item=particles[n];
rotation[n]=item.rotation;
texture[n]=item.texture;
blend[n]=item.blend;


// 2 VALUE
var p=n*2;
var one=p+1;
var i_scale=item.scale;
scale[p]=i_scale[0];
scale[one]=i_scale[1];


// 3 VALUE
var p=n*3;
var one=p+1;
var two=p+2;
var i_offset=item.offset;
offset[p]=i_offset[0];
offset[one]=i_offset[1];
offset[two]=i_offset[2];


// 4 VALUE
var p=n*4;
var one=p+1;
var two=p+2;
var three=p+3;
var i_color=item.color;
color[p]=i_color[0];
color[one]=i_color[1];
color[two]=i_color[2];
color[three]=i_color[3];
var i_quaternion=item.quaternion;
quaternion[p]=i_quaternion[0];
quaternion[one]=i_quaternion[1];
quaternion[two]=i_quaternion[2];
quaternion[three]=i_quaternion[3];


}


var item=mesh["sprite"].geometry.attributes;
item.offset=new THREE.InstancedBufferAttribute(offset,3).setUsage(THREE.DynamicDrawUsage);
item.scale=new THREE.InstancedBufferAttribute(scale,2).setUsage(THREE.DynamicDrawUsage);
item.quaternion=new THREE.InstancedBufferAttribute(quaternion,4).setUsage(THREE.DynamicDrawUsage);
item.rotation=new THREE.InstancedBufferAttribute(rotation,1).setUsage(THREE.DynamicDrawUsage);
item.color=new THREE.InstancedBufferAttribute(color,4).setUsage(THREE.DynamicDrawUsage);
item.blend=new THREE.InstancedBufferAttribute(blend,1).setUsage(THREE.DynamicDrawUsage);
item.texture=new THREE.InstancedBufferAttribute(texture,1).setUsage(THREE.DynamicDrawUsage);


mesh["sprite"].geometry._maxInstanceCount=count;


}


var geometry=new THREE.InstancedBufferGeometry();
geometry.setAttribute('position',new THREE.Float32BufferAttribute(new Float32Array([-0.5,0.5,0,-0.5,-0.5,0,0.5,0.5,0,0.5,-0.5,0,0.5,0.5,0,-0.5,-0.5,0]),3));
geometry.setAttribute('uv',new THREE.Float32BufferAttribute(new Float32Array([0,1,0,0,1,1,1,0,1,1,0,0]),2));
geometry.setAttribute('offset',new THREE.InstancedBufferAttribute(new Float32Array(),3));
geometry.setAttribute('scale',new THREE.InstancedBufferAttribute(new Float32Array(),2));
geometry.setAttribute('quaternion',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('rotation',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('color',new THREE.InstancedBufferAttribute(new Float32Array(),4));
geometry.setAttribute('blend',new THREE.InstancedBufferAttribute(new Float32Array(),1));
geometry.setAttribute('texture',new THREE.InstancedBufferAttribute(new Float32Array(),1));


mat["sprite"]=new THREE.ShaderMaterial({
uniforms:{
map:{value:[tex["one"],tex["two"]]},
time:{value:0}
},
vertexShader:vs["sprite"],
fragmentShader:fs["sprite"],
side:THREE.DoubleSide,
transparent:true,
depthWrite:false,
blending:THREE.CustomBlending,
blendEquation:THREE.AddEquation,
blendSrc:THREE.OneFactor,
blendDst:THREE.OneMinusSrcAlphaFactor
});


mesh["sprite"]=new THREE.Mesh(geometry,mat["sprite"]);
mesh["sprite"].frustumCulled=false;
//mesh["sprite"].matrixAutoUpdate=false;
//mesh["sprite"].updateMatrixWorld=function(){};
scene.add(mesh["sprite"]);


var particles_flare_a=[];


particles_flare_a.push({offset:[100,0,0],scale:[100,100],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:1});
particles_flare_a.push({offset:[-100,0,0],scale:[20,30],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:0});
particles_flare_a.push({offset:[100,100,0],scale:[60,40],quaternion:[0,0,0,1],rotation:0,color:[1,1,1,1],blend:1,texture:1});


for(var n=0;n<10;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[100,100],quaternion:[0,0,0,1],rotation:10,color:[Math.random(),Math.random(),Math.random(),1],blend:1,texture:1});
}


for(var n=0;n<100;n++){
particles_flare_a.push({offset:[Math.random()*210-105,Math.random()*210-105,Math.random()*210-105],scale:[30,30],quaternion:[0,0,0,1],rotation:0.01,color:[1,1,1,1],blend:1,texture:0});
}


const update = () => {
  const delta = clock.getDelta();
  elapsedTime = clock.elapsedTime;
  material.uniforms.uTime.value = elapsedTime;
  group.rotation.y += 0.01;
  particles_update();
  mat["sprite"].uniforms.time.value = elapsedTime;
  mesh["sprite"].rotation.y += 0.01;
  renderer.render(scene, camera);
  requestAnimationFrame(update);
}
update();
1 Like