How to know if a real time light is being rendered or not in c#?

Hi, basically I have a script which controls light intensity to create flickers and other effects. But I only want the effect to be active if the light emitted by the Light is visible (or if the light itself is visible on camera). For 3D objects (and sprites) I know there’s the Renderer component which can say if an object is visible or not inside the camera’s frustum. But, what about real time lights? How’d I know if the light is in sight? or still affecting some part of the visible screen? Is there some way to know? Though, If possible I’d like to avoid having all the lights raycasting for the player…

The way I would do it is with Unity’s ‘GeometryUtility’ class’s static method, ‘CalculateFrustumPlanes’. Once I have the frustum’s planes stored, I’d use a frustum vs. sphere intersection method for point lights with the sphere’s radius set to the range of the light, and for the sake of simplicity I’d use an AABB for spot lights (you could also use a cone, or another frustum volume for them, but an AABB should work fine in most cases). Unity provides ‘GeometryUtility.TestPlanesAABB(…)’ for testing a frustum against an AABB, but you’ll have to write your own intersection method for anything else as far as I’m aware.

Here’s a method for checking if a sphere is intersecting a frustum. I haven’t tested it yet, so if it doesn’t work let me know. Also, here’s the site I ported it from: http://www.rastertek.com/dx11tut16.html

bool CheckSphere(Plane[] planes, Vector3 center, float radius)
{
   for(int i = 0; i < planes.Length; i++)
   {
     if(planes[i].normal.x * center.x + planes[i].normal.y * center.y +
       planes[i].normal.z * center.z + planes[i].distance < -radius)
     {
       return false;
     }
   }
   
   return true;
}
1 Like

Thanks, I’m somewhat new to programming, so I haven’t delved too much into the GeometryUtility class. I’ll give it a read and see what I get.

Anyway, your idea sounds good. But, would this be faster than a less accurate raycast? As far as I’ve heard, methods like CalculateFrustrumPlanes and such are not very nice to have in an update function when performance is important…

If you only calculate the frustum planes once per frame it shouldn’t be too performance heavy. If you look at the method ‘ConstructFrustum’ in the link I posted above, you can see how frustum planes can be calculated (it’s in C++, but it shouldn’t be too difficult to see what’s going on in it from a performance standpoint). Unity’s ‘CalculateFrustrumPlanes’ might also be slightly more optimized than that code. For raycasting, it depends on your scene, and what kind of raycasting you’re doing. If you mean collision raycasting, it would be slower than frustum culling in most cases, but it might give a more desirable effect in certain scenes. For instance, scenes with lights without shadows will shine through walls, so if you perform a raycast and hit a wall, you could fade the light out, but if it reaches the camera you can fade it in. However, as you said, this wouldn’t be too accurate.

Another idea that’s quicker to setup and might be worth trying is to first check if the distance between the camera and the light are within a valid range. If the light is in range than you could take the dot product of the camera’s forward vector, and the difference between the light position and the camera position. With the dot product value, you could either check if the value is > 0 (less than 90 degrees), or you could take the acos of the value and check if it’s less than half of the the camera’s field of view. Below is some sample code of how you might do this. It wouldn’t be as accurate as frustum culling, but it might be more optimal and useful in certain cases (it does use up to three trig functions however, so it still might be better to use frustum culling, although you could optimize it to only use one trig function per call). I haven’t tested it yet, so again, if you use it, let me know if there are any issues.

// Parameter 'fovExtention' helps take into account light range for culling.
bool InSight(float fovExtention = 0.0f)
{
    Vector3 diff = light.transform.position - camera.transform.position;
    float dist = diff.magnitude;

    if(dist < Mathf.Max(light.range, 1e-5f)) // Overlapping or nearly zero?
        return true;

    // Within range of camera.
    if(dist < camera.farClipPlane + light.range)
    {
        // If your aspect ratio <= 1.0f,
        // than you'd use the (default) vertical fov.
        // Otherwise you'd use the horizontal fov.
        float halfFov;

        if(camera.aspect <= 1.0f)
            halfFov = (camera.fieldOfView + fovExtention) * Mathf.Deg2Rad * 0.5f;
        else
        {
            // You could optimize this by having the horizontal fov
            // calculated only once per frame in your camera class.
            float angle = (camera.fieldOfView + fovExtention) * Mathf.Deg2Rad;
            halfFov = Mathf.Atan(Mathf.Tan(angle * 0.5f) * camera.aspect);
        }

        float d = Vector3.Dot(camera.transform.forward, diff);

        // Within camera's field of view?
        if(Mathf.Acos(d / dist) < halfFov)
            return true;
    }

    return false;
}
1 Like

Hey, thanks, I’ll give a go to you latest idea today! But, really man, thanks. I must admit I’ve forgotten a lot of my math and trigonometry already (I’m more of a designer/artist), so your examples are golden to me.

Oh, and by the way, would it be alright if you could show me an example of how to use your first method? I’d really like to understand both methods if possible.

Hey, I got you latest idea to work quiet fast, just had to do a few changes to fit my setup and it works great! :smile: But, still, I’d really love to know how you’d use your first method too!

The first method is for point lights specifically. The way you’d use it is by cycling through each light in your scene, and passing its world position, and its range into ‘CheckSphere(…)’. The first parameter ‘Plane[ ] planes’ is where you’d pass the frustum planes calculated with ‘CalculateFrustumPlanes’.

Here’s a quick example of its use (I also updated the first method to a sightly more compact form):

Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);

// Test each light against the camera's frustum planes.
foreach(Light light in lights)
{
    light.enabled = CheckSphere(planes, light.transform.position, light.range);
}
1 Like

Oooh, so you’d only need one check for all of them? like a global check instead of a per object check? Nice! I was using the other method to test per light, but I guess both methods can be used for all lights in a foreach, cool :smile:

Edit: Ah, but, would there be any big difference in performance by using both this methods per object? I have a bool to check if the effects should run or not in a specific light. So all I need to pass them is that bool and they’ll stop doing the effect, that way I don’ have to disable the lights. I was planing to get a list of lights which I’ll initialize per scene to make sure I always have all the lights references. And I was planing to have the option to let the light change it’s own state, separate from the other lights. Oh, ummm but I guess I could add a check inside the foreach to verify the state of the light.