Cylindrical billboarding around an arbitrary axis in geometry shader

I found an answer on this site relating to this question already, but it doesn’t seem applicable in the context of my project.

Basically I’d like to create a method which fits this signature:

float3x3 AxisBillboard(float3 source, float3 target, float3 axis) 

That is to say, when given a source point (i.e an object’s position in world space), a target (camera position in world space), and an axis (the object’s up vector, which is not necessarily the global y axis), it produces a 3×3 rotation matrix by which I can multiply the vertices of my point so that it’s properly rotated.

I’ve found many solutions and tutorials online which work nicely assuming I only want to rotate around the y axis.

For example, here’s a solution which billboards around the global y axis:

float3 dir = normalize(target - source);      float angleY = atan2(dir.x, dir.z); c = cos(angleY); s = sin(angleY);      float3x3 rotYMatrix; rotYMatrix[0].xyz = float3(c, 0, s); rotYMatrix[1].xyz = float3(0, 1, 0); rotYMatrix[2].xyz = float3(-s, 0, c); 

For context, I’m working on a grass shader, and each individual blade of grass should be billboarded to face the camera while remaining aligned with the normal of the terrain.