I have been working on an area lighting implementation in WebGL similar to this demo:

http://threejs.org/examples/webgldeferred_arealights.html

The above implementation in three.js was ported from the work of ArKano22 over on gamedev.net:

http://www.gamedev.net/topic/552315-glsl-area-light-implementation/

Though these solutions are very impressive, they both have a few limitations. The primary issue with ArKano22's original implementation is that the calculation of the diffuse term does not account for surface normals.

I have been augmenting this solution for some weeks now, working with the improvements by redPlant to address this problem. Currently I have normal calculations incorporated into the solution, BUT the result is also flawed.

Here is a sneak preview of my current implementation:

## Introduction

The steps for calculating the diffuse term for each fragment is as follows:

- Project the vertex onto the plane that the area light sits on, so that the projected vector is coincident with the light's normal/direction.
- Check that the vertex is on the correct side of the area light plane by comparing the projection vector with the light's normal.
- Calculate the 2D offset of this projected point on the plane from the light's center/position.
- Clamp this 2D offset vector so that it sits inside the light's area (defined by its width and height).
- Derive the 3D world position of the projected and clamped 2D point. This is the
**nearest point**on the area light to the vertex. - Perform the usual diffuse calculations that you would for a point light by taking the dot product between the the vertex-to-nearest-point vector (normalised) and the vertex normal.

## Problem

The issue with this solution is that the lighting calculations are done from the **nearest point** and do not account for other points on the lights surface that could be illuminating the fragment even more so. Let me try and explain why…

Consider the following diagram:

The area light is both perpendicular to the surface and intersects it. Each of the fragments on the surface will always return a **nearest point** on the area light where the surface and the light intersect. Since the surface normal and the vertex-to-light vectors are always perpendicular, the dot product between them is zero. Subsequently, the calculation of the diffuse contribution is zero despite there being a large area of light looming over the surface.

## Potential Solution

I propose that rather than calculate the light from the **nearest point** on the area light, we calculate it from a point on the area light that yields the greatest dot product between the vertex-to-light vector (normalised) and the vertex normal. In the diagram above, this would be the purple dot, rather than the blue dot.

## Help!

And so, this is where I need your help. In my head, I have a pretty good idea of how this point can be derived, but don't have the mathematical competence to arrive at the solution.

Currently I have the following information available in my fragment shader:

- vertex position
- vertex normal (unit vector)
- light position, width and height
- light normal (unit vector)
- light right (unit vector)
- light up (unit vector)
- projected point from the vertex onto the lights plane (3D)
- projected point offset from the lights center (2D)
- clamped offset (2D)
- world position of this clamped offset – the
**nearest point**(3D)

To put all this information into a visual context, I created this diagram (hope it helps):

To test my proposal, I need the **casting point** on the area light – represented by the red dots, so that I can perform the dot product between the vertex-to-casting-point (normalised) and the vertex normal. Again, this should yield the maximum possible contribution value.

# UPDATE!!!

I have created an interactive sketch over on CodePen that visualises the mathematics that I currently have implemented:

## http://codepen.io/wagerfield/pen/ywqCp

The relavent code that you should focus on is line **318**.

`castingPoint.location`

is an instance of `THREE.Vector3`

and is the missing piece of the puzzle. You should also notice that there are 2 values at the lower left of the sketch – these are dynamically updated to display the dot product between the relevant vectors.

I imagine that the solution would require another pseudo plane that aligns with the direction of the vertex normal AND is perpendicular to the light's plane, but I could be wrong!

2more comments