I am creating a custom shading model in Unreal Engine and I would like to create a square shaped specular highlight, without changing the shape of the light itself.
I tried following the approach described in the paper "stylized highlights for cartoon rendering and animation" by Ken-ichi Anjyo, but I am having difficulty parsing the math notation they are using.
The idea is to start with a simple blinn-phong specular highlight, and apply transformations to its half vector H
. The vectors $\textbf{du}$ and $\textbf{dv}$ represent the tangent and bitangent vectors at the pixel normal.
The squaring operation, which we denote by $sqr(H)$, makes a highlight area more square shaped. We define this operation for a highlight vector $H$ as follows:
$\theta := \min(\cos^{-1}(\textbf{H}, \textbf{du}), \cos^{-1}(\textbf{H}, \textbf{dv}))$
$\text{sqrnorm} := \sin(2\theta)^n$
$\textbf{H}^\wedge := \textbf{H} - \sigma \times \text{sqrnorm}(\langle \textbf{H}, \textbf{du}\rangle \textbf{du} + \langle \textbf{H}, \textbf{dv}\rangle \textbf{dv})$
$sqr(\textbf{H}) := \frac{\textbf{H}^{\wedge}}{||\textbf{H}^{\wedge}||}$
where integer n and positive number σ(0.0 ≤σ≤ 1.0) are given. The highlight area would then be square shaped along the du- and dv-axis. With a rotation operation, we can get the highlight area square shaped in a desired direction. With a larger n, the area becomes more sharpened, whereas σ prescribes the magnitude of the squared area.
I tried implementing the previous equation in HLSL like so:
float3 H = normalize(ToCamera + ToLight);
float3 du = cross(N, float3(0.f, 1.f, 0.f)); // Tangent.
float3 dv = cross(N, du); // Bitangent.
// Square the highlight.
float theta = min(acos(dot(H, du)), acos(H, dv));
float sqrnorm = pow(sin(2.f * theta), n);
float3 H_hat = H - sigma * sqrnorm * dot(H, du) * du + dot(H, dv) * dv;
H_hat = normalize(H_hat);
return round(pow(dot(N, H_hat), SpecularPower));
But this is just my best guess, the end result looks wrong. The expected result is a more square shaped specular highlight:
What I get instead is something that looks like this:
You can see specular squaring in motion here: https://youtu.be/svhMHGIT4bI?t=66
view
direction of light
andnormal
. The orientation is missing. Normally this kind of reflection is created by so called environment mapping. There you have a cubemap texture into which the environment is rendered. In your case you would insert the two rectangular light points into this cubemap to get the wanted results $\endgroup$