# Projecting a Quad from light source and Normal Vector

This topic is 1370 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi All,

I'm having a decal mesh( a quad) which has a shadow texture. and I have a bike, which has its forward direction, I have the normal to the ground, and normal touching point to ground. I have also the direction of light.

My quad on the floor has 4 vertices, so I assumed as the figure Points C,D is set to be parallel with the front wheel and the back wheel.

I would like to get the maths and get the points A, B  with respect to the light direction.

My current code is : I would like to determine both vxPosSide2, and VxPosSide3

Quaternion rot = Quaternion.AngleAxis(Source.rotation.eulerAngles.y, normal) * Quaternion.FromToRotation(new Vector3(0, 1, 0), normal);
Vector3 pos = point + new Vector3(0, 0.1f, 0);
Color32 colorSideTexture = mRenderer.sharedMaterial.GetColor("_Color");

Vector3 vxPos1Side = pos + rot * (new Vector3(-1.4f, 0, -0.7f) * scale);
Vector3 vxPos2Side = pos + rot * (new Vector3(0, 0, -0.7f) * scale);
Vector3 vxPos3Side = pos + rot * (new Vector3(0, 0, 0.7f) * scale);
Vector3 vxPos4Side = pos + rot * (new Vector3(-1.4f, 0, 0.7f) * scale);

mDecalSide.SetVertex(0, vxPos1Side, colorSideTexture);
mDecalSide.SetVertex(1, vxPos2Side, colorSideTexture);
mDecalSide.SetVertex(2, vxPos3Side, colorSideTexture);
mDecalSide.SetVertex(3, vxPos4Side, colorSideTexture);

##### Share on other sites
I'm not sure I understood correctly. Your image is confusing.
The A and B vertices are to be displaced in the same direction as the light source, but limited to the ground.

For this, you just need the (normalized) look direction of the light source. This is usually the local positive Z direction of the object converted to world space.
It seems that you are using Unity. In that case, it would be something like the following:

Vector3 lightOffset = lightSource.transform.forward.normalized
lightOffset.y = 0 // Make the light vector projected to the ground.

Vector3 vxPos1Side = pos + rot * (new Vector3(-1.4f, 0, -0.7f) * scale);
Vector3 vxPos4Side = pos + rot * (new Vector3(-1.4f, 0, 0.7f) * scale);

Vector3 vxPos2Side = pos + rot * ( vxPos1Side + lightOffset * scale ) * scale );
Vector3 vxPos3Side = pos + rot * ( vxPos4Side + lightOffset * scale ) * scale );
This is untested and might need some fixes.
Essentially, the A and B points are actually the C and D points offset by the light vector but projected on the ground. Edited by Kryzon

##### Share on other sites

Kryzon, Thanks so much for reply.

I actually did that, but I need also to add "shearing" points A and B, not only translation, so that when I steer, the shadow also steers ( point A,B).

##### Share on other sites
First, I believe that a change in the naming convention is best.

Let A and B be the two points are positioned at either wheel of the bike.
To calculate their positions, you do the following:
- Obtain the normalized forward vector of the bike. Call it 'normBike.'
- Point A position is (bikePosition - normBike * scaleBike). Point B position is (bikePosition + normBike * scaleBike).

Let C and D be the two points that are offset from the bike based on the direction of the light source.
- Obtain the normalized forward vector of the light source. Call it 'normLight.'
- Point C position is (pointA + normLight * scaleShadow). Point D position is (pointB + normLight * scaleShadow).

This will account for any light position and bike orientation.

EDIT: There may be some confusion as to which values are in world space and which values are in the local space of the shadow mesh. In order to facilitate things, keep the shadow mesh transform placed at the world origin (0,0,0), and without any rotations or scaling. This way the vertex positions will all be in world space as well.

EDIT 2: The scale used for the points C and D ("scaleShadow"), which controls how "tall" the shadow is. is different than the scale used for points A and B ("scaleBike"), which controls how wide the shadow should be. Edited by Kryzon

##### Share on other sites

Hi Kryzon,

I have tried your formula as :

        Vector3 vxPos2Side = pos + rot * (new Vector3(0, 0, -0.7f) * scale);
Vector3 vxPos3Side = pos + rot * (new Vector3(0, 0, 0.7f) * scale);
Vector3 vxPos1Side = vxPos2Side + lightDir;
Vector3 vxPos4Side = vxPos3Side + lightDir;

It's working now,

but as you see in the picture, the yellow shadow which is from the hard shadow ( real time ), compared to the billboard shadow(my shadow).

Still I need shearing, or skewing it.

##### Share on other sites

Would you tell me please, the maths ( magic ) for adding the light vector to the vertices? why would I do that? " adding" two vectors ?

I also have a problem with blending that side projected shadow, and the top projected shadow. there are angles that both of them are shown to the viewer.:/

Edited by AhmedCoeia

##### Share on other sites
You are adding a vector to the position of a point (and this position is also a vector) because you want to displace\move\translate the point by that vector.

I suggest that you study linear algebra and trigonometry, as they are fundamental for game programming.
http://www.wildbunny.co.uk/blog/vector-maths-a-primer-for-games-programmers/
http://blog.wolfire.com/2009/07/linear-algebra-for-game-developers-part-1/
http://blog.wolfire.com/2009/07/linear-algebra-for-game-developers-part-2/
http://blog.wolfire.com/2010/07/Linear-algebra-for-game-developers-part-3/
http://blog.wolfire.com/2010/07/Linear-algebra-for-game-developers-part-4/

It may seem like "magic" for now, but there's a reason behind each calculation. Once you understand how it all works, you get three benefits:
1) You will understand the suggestions and support that people will give you on forums.
2) You will identify problems or errors in your programs and fix them by yourself.
3) You will design techniques and formulate calculations by yourself, without having to go through support forums and relying on others which is something that takes a lot of time.

Considering that you are also interested in blending the two shadows - and this depends on how Unity treats the rendering of shaders and materials - you need to ask for help on the official Unity support forums. Edited by Kryzon

##### Share on other sites

Hi Kryzon,

Today I tried another idea. I assumed points  C, D comes from ray to plan intersection. So I have the normal point of the plane, and the normal of the bike, light direction, and point A and C.  I tried that, but the shadow gets extended, and it gets very big, unlike your method.

Would you tell me how to get point C,D using pseudo code ? maybe I'm doing some wrong maths.

C =  RayToPlane(normal, touchPoint, lightDir.normalized, VxPos1)

D =  RayToPlane(normal, touchPoint, lightDir.normalized, VxPos2)

##### Share on other sites

Hello.
The ideal distance of points C and D (the points that are extruded outside of the motorcycle) is calculated with the HEIGHT of the motorcycle and the normalized VIEW DIRECTION of the light.
You can use the Y component of the normalized light view direction to retrieve the "angle" that the light is pointing down.

With the angle you can calculate its tangent, which is (oppositeSide / adjacentSide) or as it is in that triangle (b / Height). With this you can discover the length of "b" relative to the motorcycle height.

The expression "ASin( 1.0 + lightNorm.y )" manipulates the Arc Sinus function to result in degree values that go from zero (when the light is pointing straight down) to 90 (when the light is pointing to the horizon). Since it's a sun type light, it won't point up (that is, above the horizon) or else the entire world will become dark.
When you input values from that [0, 90] range in Tan(), you get values from [0, +infinity] respectively.
This means that the more the light points to the horizon, the larger the shadow mesh will be.

Consider "b" as the actual "shadow size." If you understand how the tangent function works, you will see that multiplying the tangent by the motorcycle height results in the ideal shadow distance for that particular direction of light.
Then you can position the points C and D with:



/* Null the Y component of the light direction to make the light direction parallel
to the ground, or the points C and D will go below the ground. */

groundLightDirection = lightNorm
groundLightDirection.y = 0

/* Move the points C and D away from the position of A and B by the direction
of "groundLightDirection" and as distant as "shadowSize." */

C = A + ( groundLightDirection * shadowSize )
D = B + ( groundLightDirection * shadowSize )
Edited by Kryzon

##### Share on other sites

Amazing Thanks so much

##### Share on other sites

I had a problem that I investigated, that the normal of the bike, is changing while driving in the terrain, and that affects that shadow, and we didn't account for in the above equations.

##### Share on other sites
The "normal" of the motorcycle is used when calculating the positions of the points A and B. I put the word normal in quotes because the motorcycle has a transform and a mesh, and each of these have several vectors that you could consider as normals. What I refer to as the normal of the motorcycle is the forward vector of the transform of the object.
It is expected to change with time as the motorcycle steers around, so the points A and B need to account for that.

You need the bottom-centre position of the motorcycle model (the point that is centred in the model, but placed on the ground).
With this centre position, you can calculate the points A and B based on the forward vector of the transform of the motorcycle, provided that the model actually faces the +Z direction.
This depends on how you have your model set. If it doesn't face the +Z direction then the mesh won't be pointing to the forward vector of its transform, and the points A and B will be placed incorrectly.

Once you make sure that the motorcycle model is facing the forward vector of its transform (the blue axis), you can calculate points A and B as follows:

bottomCentre	= [...] // Obtain the bottom-centre position of the motorcycle model in world coordinates.

modelDir	= motorcycle.transform.forward // Direction of the model.

/* Calculating points A and B based on the position "bottomCentre," the
direction "modelDir" and the distance "offset." */

offset = 2.57 // An arbitrary value that is HALF THE SPAN of the motorcycle model in 3D units.

A = bottomCentre - ( modelDir * offset ) // Position point A on the back extremity of the model.
B = bottomCentre + ( modelDir * offset ) // Position point B on the front extremity of the model.
After reading the Unity documentation, the shorthand vectors of a transform (up, right, left, back, front and forward) are always normalized, so there's no need to write ".normalized" after "transform.forward."
Additionally, a way to easily get the bottom-centre position of the motorcycle is to attach a child game object (an empty one, without a mesh) to the motorcycle object, and position the child at that bottom-centre position. The child will move along with the motorcycle, and any time you need that bottom-centre position you just need to retrieve the position of the child with "bottomCentre.transform.position." Edited by Kryzon

##### Share on other sites

Kyrzon, that's what I already did, but I missed the center point of the model. I used the touching point of the normal to the surface each frame, and it seems there is a problem when the biker jumps, because I lose the touching point.  Your idea of putting a game object, is really NICE!

##### Share on other sites

I thought about it in other way, that I could do ray to plan intersection as shown in the figure. but I still can't figure out how would I get the points parallel to the wheels.

If I have a touching point and a normal of the bike that I use for the terrain, I would get a plane.. correct?

What considerations should I have to achieve the ray to plane intersection?

Thanks so much

##### Share on other sites
Is the motorcycle navigating through an irregular environment (with ramps etc.), or is it planar?

If the terrain is planar, performing a ray-cast from that "bottomCentre" object downwards should obtain the point on the ground, in the middle of the motorcycle, where the shadow should be placed.
Instead of calculating the A and B points with the position of "bottomCentre," you would calculate them with the point of contact of the ray-cast.

Since these points are extruded with the motorcycle forward vector, they will naturally be aligned to the wheels as the wheels are also aligned to the forward vector. Edited by Kryzon

##### Share on other sites

The terrain is not planar, it has ramps. so ray cast wouldn't car for the "bottomCentre" for ramps that are higher than the normal distance between the bike and the floor :/.

and that's my current problem right now.

##### Share on other sites
Hello.
The simplest way to account for this is to align the shadow mesh to the normal of the point of contact of the raycast below the motorcycle.
However, this is an approximation and there will be instances where the shadow mesh will go off a ramp and into the air. The user will notice when the shadow mesh "realigns" itself, as this happens on a single frame.

There's no way to account for that if not for generating the shadow mesh dynamically: adding more segments to the mesh so that it wraps the different surface directions (I believe that this is called a "towel mesh").
Blizzard does this with the "Warcraft 3" and the "World of Warcraft" games for the spell-targetting marker, but even then it's not perfect:

It would be much more convenient to just use the native hard-edged shadow maps that Unity offers.

I reiterate that you would be better served on the Unity official forums, as the developers there have lots of experience with these implementation difficulties and know the shortcuts that you can take with the engine.
It's free to create an account there, and here are some thread examples:

##### Share on other sites

Hi Kryzon,

I'm trying to translate the quad based on my how much my charterer has leaned towards the light direction. So I had the dot product between light direction and up vector, then I tried to use that value to scale up two vertices of a quad, but it doesn't work at all. When I stop lighting in the scene, I still get cosine angles from the dot product.

float leaningAngle = Mathf.Abs(Vector3.Dot(lightDir.normalized, mUp.GetVector3().normalized));
Global.Log("leaningAngle" + leaningAngle);

// leaning left
if (mLean > 0 && lerp < 0)
{
vertexLeftOffset = mLean; // squeeze left side
vertexRightOffset = leaningAngle; // extend the other side

}

// right leaning
if (mLean < 0 && lerp < 0)
{

vertexRightOffset = mLean; // squeeze right side quad
vertexLeftOffset = leaningAngle; // scale the shadow based on light's direction, left side

}

Vector3 vxPos1Top = pos + rot * (new Vector3(-offsetX + vertexLeftOffset, 0, offsetY) * scale); // 1,2 vertices or on its left
Vector3 vxPos2Top = pos + rot * (new Vector3(-offsetX + vertexLeftOffset, 0, -offsetY) * scale);
Vector3 vxPos3Top = pos + (mForward.GetVector3().normalized * mMiddleVerticesScale; // two middle vertices
Vector3 vxPos4Top = pos - (mForward.GetVector3().normalized * mMiddleVerticesScale;
Vector3 vxPos5Top = pos + rot * (new Vector3(offsetX + vertexRightOffset, 0, offsetY) * scale); // 5,6 vertices are on the right of the bikeer
Vector3 vxPos6Top = pos + rot * (new Vector3(offsetX + vertexRightOffset, 0, -offsetY) * scale);


##### Share on other sites

Hi Kyron,

Today I just wanted to project the light onto the quad and use its value for translating the vertices.

I'm currently having 6 vertices,  two quads,

The points are

A  X  C

B  Y  D

Point X, Y are coming from two ray casting downwards.

C =  X + RightVector

D =  Y +  RightVector

A =   X- RightVector

B =  Y - RightVector

Now I want to translate the vertices based on lightDirectionVector, how would I do that ?

##### Share on other sites

This topic is 1370 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account