I just solved it!
The difference is I don’t want all the billboards to be parallel to the camera forward vector. It looks fine in most cases, but in the case where the billboard is not close to the camera forward vector, it’s looks wrong since the billboard is facing a point elsewhere besides the camera position.
Vertex shader:
uniform vec2 size;
uniform vec3 center;
vec3 look = normalize(cameraPosition - center);
vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]);
vec3 billboardRight = cross(cameraUp, look);
vec3 billboardUp = cross(look, billboardRight);
vec3 pos = center + billboardRight * v.x * size.x + billboardUp * v.y * size.y;
return pos;
JS:
const fullWidth = 0.75;
const fullHeight = 0.25;
containerShape.moveTo(0, 0);
containerShape.lineTo(0, fullHeight);
containerShape.lineTo(fullWidth, fullHeight);
containerShape.lineTo(fullWidth, 0);
containerShape.lineTo(0, 0);
const containerGeo = new ShapeBufferGeometry(containerShape);
containerGeo.translate(-fullWidth / 2, -fullHeight / 2, 0);
const containerMaterial = new ShaderMaterial({
uniforms: {
size: {
value: new Vector2(fullWidth, fullHeight),
},
center: {
value: new Vector3(1, 0.5, 1),
},
},
});
This should be everything. Despite missing images and broken links, this: http://nehe.gamedev.net/article/billboarding_how_to/18011/ got me past the finish line.
EDIT 4/12/19
I found a pretty serious artifact with the above solution: http://prntscr.com/nayjic. If you are willing to accept having the billboard fixed along one axis, the code below solves the problem. Without setting the billboard up vector to a fixed value, I couldn’t fix the glitch.
If the camera is above the billboard (http://prntscr.com/naz4iy), then you can more easily tell the billboard isn’t really “looking” at the camera. If anyone has a solution to this, please post it!
vec3 billboard(vec3 v, mat4 view) {
vec3 look = cameraPosition - instanceOffset;
look.y = 0.0;
look = normalize(look);
// vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]);
vec3 billboardUp = vec3(0, 1, 0);
vec3 billboardRight = cross(billboardUp, look);
vec3 pos = instanceOffset + billboardRight * v.x * size.x + billboardUp * v.y * size.y;
return pos;
}