Archive 17/01/2023.

Rotate node on local Z facing camera

Dave82

Hi ! I’m trying to create a flashlight beam effect (A Custom geometry quad always facing the camera)
The problem is i need to rotate this beamNode only on z axis and leave the other axes untouched.
No matter how i tried so far sometimes it works but if i rotate the flashlight in some direction the up axis is changing and i don’t know how to calculate it.

This code works if the flashlight has 0,0,0 rotation

Vector3 p = camNode.position - beamNode.get_worldPosition();
p.z = 0.0f;
Quaternion q;
q.FromRotationTo(p , Vector3(0,1,0));
beamNode.rotation = q;
// small correction needed :
Vector3 r = beamNode.rotation.get_eulerAngles();
r.z = 360.0f - r.z;
beamNode.rotation = Quaternion(r.x , r.y , r.z);

Once i rotate my flashlight in some direction the beamNode rotation is wrong

Lumak

How about:

p.z = 0.0f;
p.Normalize();
beamNode.rotation = Quaternion(Vector3::FORWARD, p);

Wouldn’t that work?

Dave82

How about:

p.z = 0.0f;
p.Normalize();
beamNode.rotation = Quaternion(Vector3::FORWARD, p);

Wouldn’t that work?

That again works only if my flashlightNode rotation is set to 0,0,0. once i rotate my flashlight (the flashlight is parent of beamNode) the rotation is invalid

Lumak

Then you’ll need to work with worldRotation. Pseudo code would look something like:

  1. deltaRotation = beamNode.getworldrotation - Quaternion(Vector3::FORWARD, p); // or use Inverse() but the function operator would do the same thing.
  2. beamNode.setworldrotation = beamnode.getworldrotation * deltaRotation ();

Edit: I thought more about this, and you might have to substitute flashlightNode in 1) to get the proper orientation.

Dave82

Still doesn’t work…I get back to it tomorrow. I know i could use a billboard set and set face camera mode , but i want extra control per vertex alpha and calculate the beamMesh opacity based on the angle between the camera direction and flashlight direction.If the angle is smaller the beam is more visible and if the angle is bigger the lens flare is more visible
something like this.

Dave82

Still no luck , any ideas appreciated.

Lumak

Here is something else that you can try.

Vector3 p = camNode.position - beamNode.get_worldPosition();
p.z = 0.0f;
p.Normalize();
Quaternion q(beamNode.GetWorldDirection(), p); // delta rotation
beamNode.SetRotation( beamNode.GetRotation() * q); // add delta rot

If that doesn’t work, try debugging it using debug lines - visually seeing what’s going on may help.

Dave82

This rotates the beamNode constantly like a fan and changes the direction according to direction between the camera and the node.
Usually i always draw my ideas on a piece of paper first (vectors , points) and when i’m 100% sure what i want to achieve i just do the math for it.This technique worked so far but this is just beyond my knowledge.
I also tried to apply the Z euler rotation only

beamNode.rotation = Quaternion(0,0,angle);

Where i tried to calculate angle different ways and it didn’t worked.This rotates the node on local Z in the desired direction but the angle value is always wrong.

Lumak

Created a test case to set the beamNode in the direction that the camera is facing:

Hierarchy:
-rightHand
–flashlightNode
—beamNode

code:

Node *beamNode = characterNode->GetChild("beamnode", true);
if (beamNode)
{
    Vector3 camFwd = cameraNode_->GetWorldDirection();
    Vector3 lgtFwd = beamNode->GetWorldDirection();
    Quaternion q(lgtFwd, camFwd);
    beamNode->SetRotation(beamNode->GetRotation() * q);
}

I know you’re trying to orient the beamNode based on cam pos, but ideally, the above computation would be similar to the one that I provided previously.

Modanung

I think what you’re looking for is something like this:

node_->LookAt(node_->GetWorldPosition() + node_->GetDirection(),
              node_->GetWorldPosition() - cameraNode->GetWorldPosition());

I expected to find a FaceCameraMode for this, but haven’t.

To make the beam fade away you could do something like:

node_->GetComponent<StaticModel>()->GetMaterial()
    ->SetShaderParameter("MatDiffColor", Color::WHITE * Clamp(0.9f - Abs(
    1.0f - node_->GetDirection().Angle(
    node_->GetWorldPosition() - cameraNode->GetWorldPosition()) / 90.0f),
    0.0f, 1.0f));
Dave82

After a long struggling, i found the solution.The proper way doing it was (like i first thought) rotate only on z euler.The only problem was i didn’t take node rotation into account.It goes like this :

Vector3 diff = camNode.get_worldPosition() - beamNode.get_worldPosition(); diff.Normalize(); diff = node.rotation.Inverse() * diff; float angleZ = Atan2(diff.y , diff.x) + 90; beamNode.set_rotation(Quaternion(0,0,angleZ));

What bothers me in this code is that Atan2 should return the angle in radians and i should convert it to degrees first but it just works without it for some reason (no need for angleZ * M_RADTODEG)… strange.

Dave82

"I think what you’re looking for is something like this:

node_->LookAt(node_->GetWorldPosition() + node_->GetDirection(),
cameraNode->GetWorldPosition() - node_->GetWorldPosition());"

Yes ,this works too ! Thanks.And it is more elegant than mine version.

Modanung

And then you’ll end up with something like this:

Dave82

Or something like this

Dave82

Wow ! This flashlight effect looks really cool ! I made a 3 min video of playing with the flashlight, :slight_smile: Also the dynamic lights use inverse square attenuation and looks way better than the default ramp texture (thanks to dragonCASTjosh !)
https://github.com/urho3d/Urho3D/issues/1478

Here’s the video.

Modanung

That looks really good indeed! :slight_smile:
One thing that would be an improvement is using something like the soft particles technique. A shader that would make the beam fade to fully transparent before it crosses a surface. A blurred cross section of the world geometry as alpha texture seems like a good option to me. But I’m no shader expert.