<p>I took some time and got the square specular from the example posted to work. I was able to split, rotate and square it. It looks like using max instead of min is the key. The results are fairly unstable, they work for key frame animations I'm sure but they move a lot.</p>
<p>I also took some time to work with the step version of square. It actually is more of a cut off function that produces a perfectly round specular highlight like a spot. Setting the cut off in the step to 0.99 gave a very nice spot.</p>
<p>Finally I worked on yet another version of the square spec highlight. This one actually generates a perfectly square highlight using a box filter(again using the step function). It contains the actual highlight inside a box, so if parameterized it could generate a wide range of effects. It could also be rotated. ( I won't be coding that up) I also include the step version that creates the perfect spot. A similar technique could be used on the square version to get a perfect square for the highlight.</p>
<p>Here is the code: This code has been tested and works. I even created a shader toy to show it but have decided to keep it private mostly because the code is a conglomeration of several small tests.</p>
<pre><code>float boxFilter(float x, float size) {
  return step(size, x) - step(2.0 * size, x);
}

float squareSpecular(vec3 halfVec, vec3 surfaceNormal)
{ 
   float angle = dot(halfVec, surfaceNormal);
   angle = clamp(angle, 0.0, 1.0);
   float specular = pow(angle, 50.);
   //return step(0.99, angle) * specular; // return a &quot;spot&quot;
   float specSize = 0.3; // Size of the square highlight
   float specSquare = boxFilter(halfVec.x, specSize) * boxFilter(halfVec.y, specSize);

   return specular * specSquare;
}
</code></pre>
<p>I included stretching, splitting and translating. ddu and ddv must be recomputed using the new halfway vector to combine the effects.</p>
<p>Here is the code for the versions from the paper:</p>
<pre><code>float sgn(float x )
{
  if( x== 0.0 ) return 1.0; 
  return x/abs(x);
}

float Specular( vec3 n, vec3 h )
{
   const float shininess = 20.0;
   vec3 du = cross(n, vec3(0,0,1));
   vec3 dv = cross(du, n);

   float ddu = dot(h, du);
   float ddv = dot(h, dv);

   //vec3 hprimed = h + 0.5*dv + 0.5*du; // translation
   //vec3 hprimed = h - (0.95*ddu * du); // stretching / lengthing
   // vec3 hprimed = h - (0.7 * sgn(ddu) * du) - (0.0 *sgn(ddv)*dv); // splitting

   float sigma = 0.7;
   float theta = max( acos(ddu), acos(ddv));
   float sqrnorm = pow( sin(2.0*theta), 14.);
   vec3 hprimed = h - sigma * sqrnorm * (ddu*du + ddv*dv);

   return pow( clamp( dot( n, normalize(hprimed) ), 0.0, 1.0 ),shininess);
}
</code></pre>
<p>Many other shapes are possible...stars, ragged, filtered, the list goes on and on.</p>

CC BY-SA 4.0
2023-02-11T02:09:00.387Z
This code seems to just threshold the specular highlight beyond a certain value. What I'm looking for is something that would change the shape of the specular highlight
mbl
CC BY-SA 4.0
2023-02-11T02:13:14.117Z
I added a video that shows what I'm trying to do, the technique in the video is what' described in the paper
mbl
CC BY-SA 4.0
2023-02-11T13:02:43.280Z
It was not my intent to give a copy and paste function, but to discuss how to go about it and give some pseudocode towards that end. I have reformulated the code but again I have not tested it.
pmw1234
CC BY-SA 4.0
2023-02-11T17:03:24.070Z
Another question would be: does the shape has to be rotation variant? Meaning, when rolling the camera, does the shape shouldn't roll with respect to world orientation
Thomas
CC BY-SA 4.0
2023-02-11T19:29:00.783Z
The rotation matrix could come from upper 3x3 matrix of the camera view matrix. I would expect that to roll the highlight with the camera. Or the rotation itself could be fixed, fixing the highlight at a particular angle.
pmw1234
CC BY-SA 4.0
2023-02-15T01:55:36.897Z
@mbl I completely rewrote my answer and included tested working code.
pmw1234
CC BY-SA 4.0
2023-02-15T22:15:18.230Z
Thank you so much for figuring this out! I managed to replicate both of your methods and I'm happy this is finally settled. You're right that they're very unstable, but for the use case I have in mind the method from the paper will do.
mbl
CC BY-SA 4.0
2023-02-10T14:43:35.850Z
2023-02-15T01:52:00.410Z
2023-02-15T01:52:00.410Z
pmw1234
<p>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.</p>
<p>I tried following the approach described in the paper &quot;stylized highlights for cartoon rendering and animation&quot; by Ken-ichi Anjyo, but I am having difficulty parsing the math notation they are using.</p>
<p>The idea is to start with a simple blinn-phong specular highlight, and apply transformations to its half vector <code>H</code>. The vectors <span class="math-container">$\textbf{du}$</span> and <span class="math-container">$\textbf{dv}$</span> represent the tangent and bitangent vectors at the pixel normal.</p>
<blockquote>
<p>The squaring operation, which we
denote by <span class="math-container">$sqr(H)$</span>, makes a highlight area more square
shaped. We define this operation for a highlight vector
<span class="math-container">$H$</span> as follows:</p>
</blockquote>
<p><span class="math-container">$\theta := \min(\cos^{-1}(\textbf{H}, \textbf{du}), \cos^{-1}(\textbf{H}, \textbf{dv}))$</span></p>
<p><span class="math-container">$\text{sqrnorm} := \sin(2\theta)^n$</span></p>
<p><span class="math-container">$\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})$</span></p>
<p><span class="math-container">$sqr(\textbf{H}) := \frac{\textbf{H}^{\wedge}}{||\textbf{H}^{\wedge}||}$</span></p>
<blockquote>
<p>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.</p>
</blockquote>
<p>I tried implementing the previous equation in HLSL like so:</p>
<pre><code>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));
</code></pre>
<p>But this is just my best guess, the end result looks wrong. The expected result is a more square shaped specular highlight:
<a href="https://i.sstatic.net/tAud4.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/tAud4.png" alt="enter image description here" /></a></p>
<p>What I get instead is something that looks like this:
<a href="https://i.sstatic.net/XISvL.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/XISvL.png" alt="enter image description here" /></a></p>
<p>You can see specular squaring in motion here: <a href="https://youtu.be/svhMHGIT4bI?t=66" rel="nofollow noreferrer">https://youtu.be/svhMHGIT4bI?t=66</a></p>

CC BY-SA 4.0
2023-02-10T07:59:07.193Z
This is quite untypical, because the Blinn-Phong model works with the vectors `view` `direction of light` and `normal`. 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
Thomas
<rendering><shading><specular>
How can I transform the circular blinn-phong specular into a square shaped highlight?
mbl
# How can I transform the circular blinn-phong specular into a square shaped highlight?

(Asked by: username_0 on 2023-02-09)

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:
[![enter image description here](https://i.sstatic.net/tAud4.png)](https://i.sstatic.net/tAud4.png)

What I get instead is something that looks like this:
[![enter image description here](https://i.sstatic.net/XISvL.png)](https://i.sstatic.net/XISvL.png)

You can see specular squaring in motion here: <https://youtu.be/svhMHGIT4bI?t=66>

**Tags:** rendering, shading, specular

> **Comments:**
> **username_1**: This is quite untypical, because the Blinn-Phong model works with the vectors `view` `direction of light` and `normal`. 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  

---

## 1 answers

### Answer by username_2 on 2023-02-10. Score: 1 | Accepted answer

I took some time and got the square specular from the example posted to work. I was able to split, rotate and square it. It looks like using max instead of min is the key. The results are fairly unstable, they work for key frame animations I'm sure but they move a lot.

I also took some time to work with the step version of square. It actually is more of a cut off function that produces a perfectly round specular highlight like a spot. Setting the cut off in the step to 0.99 gave a very nice spot.

Finally I worked on yet another version of the square spec highlight. This one actually generates a perfectly square highlight using a box filter(again using the step function). It contains the actual highlight inside a box, so if parameterized it could generate a wide range of effects. It could also be rotated. ( I won't be coding that up) I also include the step version that creates the perfect spot. A similar technique could be used on the square version to get a perfect square for the highlight.

Here is the code: This code has been tested and works. I even created a shader toy to show it but have decided to keep it private mostly because the code is a conglomeration of several small tests.

```
float boxFilter(float x, float size) {
  return step(size, x) - step(2.0 * size, x);
}

float squareSpecular(vec3 halfVec, vec3 surfaceNormal)
{ 
   float angle = dot(halfVec, surfaceNormal);
   angle = clamp(angle, 0.0, 1.0);
   float specular = pow(angle, 50.);
   //return step(0.99, angle) * specular; // return a "spot"
   float specSize = 0.3; // Size of the square highlight
   float specSquare = boxFilter(halfVec.x, specSize) * boxFilter(halfVec.y, specSize);

   return specular * specSquare;
}

```

I included stretching, splitting and translating. ddu and ddv must be recomputed using the new halfway vector to combine the effects.

Here is the code for the versions from the paper:

```
float sgn(float x )
{
  if( x== 0.0 ) return 1.0; 
  return x/abs(x);
}

float Specular( vec3 n, vec3 h )
{
   const float shininess = 20.0;
   vec3 du = cross(n, vec3(0,0,1));
   vec3 dv = cross(du, n);

   float ddu = dot(h, du);
   float ddv = dot(h, dv);

   //vec3 hprimed = h + 0.5*dv + 0.5*du; // translation
   //vec3 hprimed = h - (0.95*ddu * du); // stretching / lengthing
   // vec3 hprimed = h - (0.7 * sgn(ddu) * du) - (0.0 *sgn(ddv)*dv); // splitting

   float sigma = 0.7;
   float theta = max( acos(ddu), acos(ddv));
   float sqrnorm = pow( sin(2.0*theta), 14.);
   vec3 hprimed = h - sigma * sqrnorm * (ddu*du + ddv*dv);

   return pow( clamp( dot( n, normalize(hprimed) ), 0.0, 1.0 ),shininess);
}

```

Many other shapes are possible...stars, ragged, filtered, the list goes on and on.

> **Comments:**
> **username_0**: This code seems to just threshold the specular highlight beyond a certain value. What I'm looking for is something that would change the shape of the specular highlight  
> **username_0**: I added a video that shows what I'm trying to do, the technique in the video is what' described in the paper  
> **username_2**: It was not my intent to give a copy and paste function, but to discuss how to go about it and give some pseudocode towards that end. I have reformulated the code but again I have not tested it.  
> **username_1**: Another question would be: does the shape has to be rotation variant? Meaning, when rolling the camera, does the shape shouldn't roll with respect to world orientation  
> **username_2**: The rotation matrix could come from upper 3x3 matrix of the camera view matrix. I would expect that to roll the highlight with the camera. Or the rotation itself could be fixed, fixing the highlight at a particular angle.  
> **username_2**: @username_0 I completely rewrote my answer and included tested working code.  
> **username_0**: Thank you so much for figuring this out! I managed to replicate both of your methods and I'm happy this is finally settled. You're right that they're very unstable, but for the use case I have in mind the method from the paper will do.  

---

