Instancing with Compute Shader: manually calculating rotation

Happy Friday!

I’m making a particle system driven by a computer shader. I got most of the technique from Bruno Imbrizi’s Curl Noise video. In Bruno’s tutorial, he has the boxes rotate to face the direction they are moving by setting “Rotate to Vector” in the Geometry instancing. My problem is that I want to have other things affecting the rotation of the instances, so I would rather create the same effect only using the Rotate OP input of the Geometry instancing.

I’ve attached the simplest possible recreation of my actual particle system. It might be hard to see in this video, but it’s 16 cones moving around, but they’re always facing the camera. I want them to face the direction they are moving. And I need to do this only using the Rotate OP, not the Rotate To Vector OP.

https://vimeo.com/620085989
SimpleParticles.toe (6.7 KB)

The simplified shader that is controlling the instancing is:

layout (local_size_x = 8, local_size_y = 8) in;

#define M_PI 3.1415926535897932384626433832795
#define HALF_PI (M_PI/2.0)
#define TWO_PI (M_PI*2.0)

vec3 lookAt(vec3 v1, vec3 v2) {
	// What goes here to make the instances look "forward"?
	return vec3(0,0,0);
}

void main()
{
	vec3 pos = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	vec3 vel = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	vec3 acc = texelFetch(sTD2DInputs[2], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	
	acc *= 0.001;					// We just need a tiny bit of the acceleration
	acc += -pos * 0.001; 			// attract them back towards the center just a bit.

	vel += acc;
	pos += vel;
	vel *= 0.96; 					// friction - slow 'em down a bit.

	vec3 rot = lookAt(pos, pos+vel);

	imageStore(mTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(pos,1)));
	imageStore(mTDComputeOutputs[1], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(vel,1)));
	imageStore(mTDComputeOutputs[2], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(rot,1)));
}

So my question is: how do I calculate the rotation? My research so far has led me to believe that it is either very simple or somewhat complex. I know I should probably be using quaternions instead of euler angles, which would mean I would need to store both the quaternion in an output buffer in order to do that calculations, and then another buffer with the quat translated to euler angles so that I can feed that to the Rotate OP in the Instancing tab…

As you can see, I’m a bit confused. Any help would be greatly appreciated. Thanks in advance.

I’ve come around to the idea that I need to store the rotation as quaternions, but also output another buffer with euler angles to feed to the instancer. So I’ve updated my project and shader to reflect that:

SimpleParticles.toe (8.1 KB)

layout (local_size_x = 8, local_size_y = 8) in;

vec3 to_euler(vec4 quat) {
	// need something here too
	return vec3(0,0,0);
}

vec4 lookAt(vec3 a, vec3 b) {
	// what goes here?
	return vec4(1,1,1,1);
}

void main()
{
	vec3 pos = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	vec3 vel = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	vec4 quat = texelFetch(sTD2DInputs[2], ivec2(gl_GlobalInvocationID.xy), 0);
	vec3 acc = texelFetch(sTD2DInputs[3], ivec2(gl_GlobalInvocationID.xy), 0).xyz;
	
	acc *= 0.001;					// We just need a tiny bit of the acceleration
	acc += -pos * 0.001; 			// attract them back towards the center just a bit.

	vel += acc;
	pos += vel;
	vel *= 0.96; 					// friction - slow 'em down a bit.

	quat = lookAt(pos, pos+vel);

	vec3 rot = degrees(to_euler(quat));

	imageStore(mTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(pos,1)));
	imageStore(mTDComputeOutputs[1], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(vel,1)));
	imageStore(mTDComputeOutputs[2], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(quat));
	imageStore(mTDComputeOutputs[3], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(vec4(rot,1)));
}

Hey Jeff,

One thing that I think is a bit easier is to not feed the rotation to the instancer but instead calculate and apply the rotation in a GLSL MAT. this way you don’t have to deal with quaternions or euler angles.
You can start with a phong or pbr and output a GLSL MAT and then edit it.
And there’s a convenient
mat3 TDRotateToVector(vec3 forward, vec3 up);
You’ll need to apply the rotation to the normal too.
see (Write a GLSL Material - Derivative)
You can pass extra values you need with
vec4 TDInstanceCustomAttrib0(); (also in the wiki)
depending on the overall motion you may need to store the previous up vector in the sim to avoid any sudden flipping

otherwise for the method you were thinking of there might be some glsl library online or you could try converting the functions from GitHub - g-truc/glm: OpenGL Mathematics (GLM) to glsl

Hey Vincent – thanks so much! I will give the GLSL MAT a try and report back.

I did look at glm, starting with the quaternion class, and tried to get a sense for what I would be getting into if I attempted to port to GLSL. It would be quite a bit of work, but I haven’t eliminated it as a possibility yet. I’m just surprised there are no existing GLSL math libraries (that I could find).

So I made my phong material and I am passing position and velocity via “Instancing3”. Here’s my vertex shader.

out Vertex
{
	vec4 color;
	vec3 worldSpacePos;
	vec3 worldSpaceNorm;
	flat int cameraIndex;
} oVert;

uniform float uTime;

void main()
{
	// Calculate the rotation based on the velocity, passed in from the instancer
	vec3 pos = TDInstanceCustomAttrib0().xyz;				// shouldn't need this?
	vec3 vel = TDInstanceCustomAttrib1().xyz;
	vec3 forward = normalize(vel);
	vec3 right = cross(forward, vec3(0., 1., 0.));
	vec3 up = cross(forward, right);
	mat3 rot = TDRotateToVector(forward, up);
	//mat3 rot = TDRotateOnAxis(uTime, vec3(0,1,0));

	vec4 worldSpacePos = TDDeform(P);
	worldSpacePos.xyz *= rot;								// Rotate the vertex
	vec3 uvUnwrapCoord = TDInstanceTexCoord(TDUVUnwrapCoord());
	gl_Position = TDWorldToProj(worldSpacePos, uvUnwrapCoord);


	int cameraIndex = TDCameraIndex();
	oVert.cameraIndex = cameraIndex;
	oVert.worldSpacePos.xyz = worldSpacePos.xyz;
	oVert.color = TDInstanceColor(Cd);
	vec3 worldSpaceNorm = normalize(TDDeformNorm(N));
	worldSpaceNorm.xyz *= rot;								// Rotate the normal
	oVert.worldSpaceNorm.xyz = worldSpaceNorm;
}

I think I’m misunderstanding about the order of transformations or something. It seems they are rotating around the center point. Does anything jump out at you about my vertex shader? I’m pretty much at the limits of my understanding, so pardon if it’s something very dumb :slight_smile:

radial
SimpleParticles.toe (10.6 KB)

Hey Jeff, you were pretty close, you want
vec4 worldSpacePos = TDDeform(rot*P);
P is in model space, and rotations are applied from the origin

and then the translation from the instances will be applied as part of TDDeform().
alternatively you could remove the positions from the instance parameters and do
vec4 worldSpacePos = pos + TDDeform(rot*P);
otherwise you don’t need pos indeed

and
vec3 worldSpaceNorm = TDDeformNorm(rot*N);
(not sure that makes any difference though since TDDeformNorm() doesn’t apply translation)

Just a quick update while I continue to troubleshoot: With the changes you recommended, it seems to be about 80% working as expected. They’re mostly pointing in the direction that they are traveling, but seem to be Tokyo Drifting sometimes… This happens regardless of which OP does the positioning. I’m trying to recognize a pattern in order to troubleshoot, but not seeing anything yet. If these boids were actually my endpoint, I’d just call it a day, but it will be much more noticeable in my actual project.

Boids

I’m so excited just to have progress though! Thanks for you continued tips.
SimpleParticles.toe (8.8 KB)

I went pretty far down the road of using a GLSL MAT for positioning and rotation. It was very cool to play with, so I’m glad I did it. One thing that should have been obvious but stumped me for a while is that the order of multiplication is very important. My particles were getting all skewed until I rearranged it like this: TDDeform(pos + P * scale * rot)

	vec3 pos = TDInstanceCustomAttrib0().xyz;
	vec3 forward = TDInstanceCustomAttrib1().xyz;
	vec3 scale = TDInstanceCustomAttrib2().xyz;
	vec3 right = cross(forward, vec3(0., 1.0, 0.));
	vec3 up = cross(forward, right);
	mat3 rot = TDRotateToVector(forward, up);

	// First deform the vertex and normal
	// TDDeform always returns values in world space
	vec4 worldSpacePos = TDDeform(pos + P * scale * rot);

But, in the end, I went back to doing everything in a compute shader. I’m honestly still not sure specifically where I got stuck before, but I had to shift my thinking from outputting a rotation buffer to outputting a “forward” buffer. Then I just feed this buffer to the “Rotate to OP” field in the instancer. Et voila. Here is the result (still tweaking, but close): POWER Particles

Not sure any of this will make any sense to anyone, but I hope it helps someone some day. Thanks again, @vinz99!

1 Like

Hey Jeff, glad you were able to achieve your goal! The video you shared looks great.
The instancer had fewer options when I started out so I kinda got into the habit of tweaking the GLSL MAT, and as you say the transformation order is very important so I like to see it explicitly, but yeah all the better if you can achieve what you need without tweaking it and stick to the standard MATs.
You can actually combine xyz rots + forward vector with the instancer nowadays, and choosing which transformation happens first, though it can get a bit confusing.