# Math help, calculate surrounding pyramid of a spotlight

Hello, i would require some math help.
I would like to calculate the 5 position of the pyramid on the GPU side.

What data is available?
Origin, Direction, Angle, Range

That should be enough i hope.

Point[0]=Origin; (Did that myself )

But i cant figure out how to get the other 4 points.

So itâs not really a point light but some kind of spot light?

Edit: a picture might save a lot of confusion.

Faster then i was able to edit the title

Yeah its a spot lightâŚ

Going to drawâŚ

If I get what I think you are asking forâŚ the rough algorithm might go like this:

``````vec3 left = cross(dir, vec3(0,1,0));
vec3 up = cross(left, dir);
float project = sin(angle);

vec3 upperLeft = left * project + up * project;
...continue for other corners.
``````

I could have the cross products flipped, I frequently do thatâŚ I can never remember if we are right handed or left handed until I try it.

Edit: note a spot light pointing directly up (or close) will have to be handled special. And you have to normalize the left and up vectors.

Edit2: and I left out the range projection along the dir vector and you will need to multiply project by that, too. Scale up the triangle.

upperLeft = dir * range + left * project * range + up * project * range;

Again faster then i can draw a damn image

Let me know if you need me to explain why/how what I wrote should workâŚ I can try.

I try to understand whats going on first. But some lecture is always nice

If you imagine you are staring down the direction vector then the pyramids far quad will extend left, right, up, down along vectors perpendicular to that direction vector.

A cross product between the âpure upâ vec3(0,1,0) and the direction vector will give you the perpendicular side vector when normalized. If youâve got the order of the cross product right then it will be pointing left.

A cross product between that vector and the direction vector will give you the relative up vector. Note: technically you now have a 3D rotation matrix, by the way.

At any rate, you now have the three axes or rotation for your pyramidâs space.

p = direction * range will give you the center of your blue âteeâ.

From there you want to go out based on your angleâŚ that will be the sine * range since range is essentially the scale of your triangle.

p += left * sine * range will give you the point where the blue line intersects the black between â1â and â2â in your drawing.

p += up * sine * range will give you point â1â

I called âsineâ âprojectâ in my original because you are using it to project down the vectors.

Edit: note that if you made a matrix from left, up, dir then the above is equivalent of a matrix mult with vec3(sin * range, sin * range, range)

Too much workâŚ Why not just precompute the pyramid vertices and then just multiply it by a rotation matrix? This is for the lighting geometry generator right?

I wanted to go the same route as i did with the point lights, and for that i have to calculate the vertices in the vertex shader

Based on the shader you showed me, you just transform a constant uniform vec4 array by the WorldViewProjectionMatrix, so canât you just set the transform of the spot light and get thus get the transformed points for free?

Works good with a pointlight since it always has the same shape, a spotlight however can have a varying angle.

Still, in its own space the corners are well defined (sin, sin, 1). If you multiplied that by its matrix you get the same thing I described above.

If you can pass the sin of the angle instead of the angle then you even avoid the sin() call in the shader.

thanks, looking good.

Got it even working without branching.

``````uniform mat4 g_WorldViewProjectionMatrix;
out vec4 lightColorInnerAngle;
uniform vec4 m_spotLightPositionAngle;
uniform vec4 m_spotLightDirectionRange;
uniform vec4 m_spotLightColorInnerAngle;
const float offsetMod[5]=float[5](0,1,1,1,1);
const vec3 upMod[5]=vec3[5](vec3(0),vec3(1),vec3(0),vec3(-1),vec3(0));
const vec3 leftMod[5]=vec3[5](vec3(0),vec3(0),vec3(1),vec3(0),vec3(-1));

void main(){
int vertexId=gl_VertexID;
lightColorInnerAngle=m_spotLightColorInnerAngle;
vec3 left=normalize(cross(m_spotLightDirectionRange.xyz,vec3(0,1,0)));
vec3 up=cross(left,m_spotLightDirectionRange.xyz);
vec3 mod=(left*leftMod[vertexId])+(up*upMod[vertexId]);
vec3 origin=m_spotLightPositionAngle.xyz;
vec3 centerPoint=origin+offsetMod[vertexId]*m_spotLightDirectionRange.xyz*m_spotLightDirectionRange.a;
float range=m_spotLightDirectionRange.a;
float sine=m_spotLightPositionAngle.a;
gl_Position=g_WorldViewProjectionMatrix*vec4(centerPoint+mod*sine*range,1);
}``````

Well, what is the world view projection matrix in this case. You could be doing a lot more work than required if itâs already oriented to the direction vector.

Then you could remove most of that code.

Sorry, donât get it. At most i might be able to skip the World*, since i already am in worldspace. But donât see a way to skip ViewProjection. But then its also late and my head is near to exploding.

If you have time you could also give me a hint on my last bug. (at least the last easily seeable)

While it works for like 70% of the random pointlights. The other 30% would require an inverted value.

I am like 99% sure it is this line:

``````float angleFallof = 1-clamp((currAngleCos-outerAngleCos)/(innerAngleCos-outerAngleCos), 0.0, 1.0);
``````