There is that one last bit that resists me. The normals having some little issues. I can’t seem to find a solution for this one. If someone here is comfortable with normals calculation, feel free to lend a hand.
It would be pretty cool to conclude this cleanly. Later if all is fixed, after some clean up. I think I should make a small repo for it. Having dual quaternion skinning available for Three.js with the new TSL transpiler should be pretty interesting for anyone that wishes to animate characters properly within Three.js. 
https://codesandbox.io/p/devbox/glsl-dqs-to-tsl-2j7lkn
For reference there is some weird behaviour where normalLocal.assign()
ends up doing nothing. And I found and used this work around: TSL: Using a custom vertexNode is breaking the normalLocal of a BatchedMesh · Issue #29393 · mrdoob/three.js
But overall everything else works. It’s a +100fps gain from webGL to webGPU on my end (maybe more technically since I’m capped at 144). 
Without trying with your code (cannot fork and edit it), here is what I do in another project, but it is for positionNode
/normalNode
:
positionGeometry
→ some calculations → positionNode
normalGeometry
→ the same calculations → transformNormalToView(...)
→ normalNode
So I process them the same way, except that the normal is additionally passed through this magic transform function.
// calculate vertices of bent body surface
function tslPositionNode( options ) {
...
return disfigure( positionGeometry );
}
// calculate normals of bent body surface
function tslNormalNode( options ) {
...
return transformNormalToView( disfigure(normalGeometry) ).xyz;
}
In another project of mine I “sample” two vectors tangent to the surface and calculate the normal, but again I pass it through transformNormalToView
, something like this:
rotator.normal = Fn( ( params ) => {
...
var pos = surfacePos( position, params );
var posU = surfacePos( position.add( tangent ), params );
var posV = surfacePos( position.add( bitangent ), params );
var dU = sub( posU, pos ),
dV = sub( posV, pos );
return transformNormalToView( cross( dU, dV ).normalize() );
} );
3 Likes
Ok I see, that’s good info!
Also what do you think about the colours it shows currently?
My understanding is that it’s world space normals and I need to feed them as tangent space. And functions like transformNormalToView
help that. I am on the right path?
I’m really just familiar with tangent space normals that should look mostly just purple. And here I think it’s also what I should achieve. When I use transformNormalToView()
it makes it so the normals shifts depending on your view. Which isn’t supposed to happen?
Also there is this green hue on top of the moving part. So far I’ve been assuming it’s related to world space.
With the
transformNormalToView()
I get this result. Also as mentioned, the normals shift along the view. The blue & pink tint is kinda always facing the viewport.
This is how normals look when treated as colors (much less saturated). Do you normalize your normals?
1 Like
Indeed it was correct. I was confused because I didn’t expect the lights to affect the look of the normals. But the parts that were confusing me we related to the lighting setup of the scene. And on my app I was testing it on some dark clothing of a character and the saturation was baiting me.
Thanks a lot for you assistance! That was very helpful and overall a very instructive.
I think that’s a nice project that is now running smoothly 
That was definitely worth the struggle 
100+fps gained is pretty cool 
3 Likes