How to implement a horizontal billboard?

How can I implement a horizontal billboard?
(faces towards the camera only when moving the camera horizontally.)

I read Sprite and found that there was no option to do so.
I read the source code, but I couldn’t understand it because I’m new to Three.js.
I think I need to implement it myself using PlaneGeometry and MeshBasicMaterial.
How can I implement it?

Custom billboard shader with instances geometry.

I’m sorry, it was difficult for me to understand.
Could you please tell me the key points?

Create mesh billboard like default sprite geometry - plane with UV, offsets and custom material

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"]);

Changed code of placing one particle and one grass into mesh:

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


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

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;
vec3 vRotated=vec3(position.x*scale.x*cos(angle)-position.y*scale.y*sin(angle),position.y*scale.y*cos(angle)+position.x*scale.x*sin(angle),position.z);


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


vec3 localUpVector=vec3(0.0,1.0,0.0);
vec3 vLook=offset-cameraPosition;
vec3 vRight=normalize(cross(vLook,localUpVector));
vec3 vPosition=vRotated.x*vRight+vRotated.y*localUpVector+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;


}


`;

Other example: InstancedMesh + implement billboard

Thank you, I’ll try it!