<p>This can be achieved without trigonometric functions by scaling down the <a href="http://mathworld.wolfram.com/Incircle.html" rel="nofollow noreferrer">incircle</a> of the triangle.</p>

<p><code>incircle()</code> computes the incircle of the triangle formed by the vertices <code>A,B,C</code>, it returns center and radius as <code>vec4</code>. The vertices <code>X=A,B,C</code> are then moved inwards by the fraction of their distance to the incircle center (<code>Q-X</code>) which is equal to the ratio of desired margin to incircle radius (<code>m/Q.w</code>).</p>

<pre class="lang-c prettyprint-override"><code>vec4 incircle(vec3 A, vec3 B, vec3 C) {
    float a = length(B - C), b = length(C - A), c = length(A - B);
    float abc = a + b + c;
    // http://mathworld.wolfram.com/Incenter.html
    vec3 I = (a * A + b * B + c * C) / abc;
    // http://mathworld.wolfram.com/Inradius.html
    float r = 0.5
            * sqrt((-a + b + c) * (a - b + c) * (a + b - c) / abc);
    return vec4(I, r);
}

vec3 A,B,C; // vertices
float m; // margin
vec4 Q = incircle(A,B,C);
A += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - A);
B += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - B);
C += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - C);
</code></pre>

CC BY-SA 4.0
2019-05-22T05:51:06.250Z
Very interesting, Adam! I hadn't heard about this function.
Jack
CC BY-SA 4.0
2019-05-20T12:46:58.460Z
2019-05-20T12:46:58.460Z
Adam
<p>Given triangle ▲ABC, we bisect angle ∠BAC with line AD, derived with <a href="https://en.wikipedia.org/wiki/Angle_bisector_theorem" rel="noreferrer">Angle Bisector Theorem</a>:</p>

<p>BA / BD = CA / CD
<a href="https://i.sstatic.net/kKiYW.png" rel="noreferrer"><img src="https://i.sstatic.net/kKiYW.png" alt="Triangle Inset Diagram"></a>
Point E represents our objective refined position on the desired resulting inset triangle. As it lies upon angle bisector AD, it is equidistant from sides BA &amp; CA, forming identical right triangles ▲AFE &amp; ▲AGE. We can now use <a href="https://en.wikibooks.org/wiki/Geometry/Right_Triangles_and_Pythagorean_Theorem" rel="noreferrer">Sine for Right Triangles</a> to find the length of AE:</p>

<p>AE = EG / Sin(∠EAG)</p>

<p>That's all the math we need, so let's cook up some GLSL!</p>

<p>We start off with all the typical attributes: position, normal, and transformation matrices, but since the vertex shader only works on a single vertex, we need to add the neighboring vertices as additional attributes. This way, each vertex will find its own "point E", creating the resulting inset triangle. (Note: I don't call them "B" &amp; "C" here, because they are not yet in <em>screen space</em>.)</p>

<pre><code>    attribute vec3 left; //vertex to the left of this vertex
    attribute vec3 right; //vertex to the right of this vertex
</code></pre>

<p>Speaking of screen space, I'm also including the aspect ratio of the display, (and making it a uniform, in case the window is resized.)</p>

<p>After preparing varying normals for the fragment shader, and transforming the face into clipping-space, we can get down to the business of applying the above math:</p>

<pre><code>        attribute vec3 left; //vertex to the left of this vertex
        attribute vec3 right; //vertex to the right of this vertex
        uniform float aspect;
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
            vNormal = normal;
            vUv = uv;

            mat4 xform= projectionMatrix * modelViewMatrix;
            vec4 A = xform * vec4( position, 1.0 );
            vec4 B = xform * vec4( left, 1.0 );
            vec4 C = xform * vec4( right, 1.0 );

            vec3 CB = C.xyz - B.xyz;
            vec2 BA = B.xy - A.xy;
            vec2 CA = C.xy - A.xy;
            float lengthBA = length(BA);
            float lengthCA = length(CA);
            float ratio = lengthBA / ( lengthBA + lengthCA );
            vec3 D = B.xyz + ratio * CB.xyz;
            vec3 AD = D - A.xyz;
            vec3 bisect = normalize(AD);

            float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
            float AE = 1.0/(sin(theta)*aspect);
            newPos.z += AE/length(AD) * (D.z - A.z);
            newPos.x += bisect.x*AE;
            newPos.y += bisect.y*AE;

            gl_Position = newPos;
        }
</code></pre>

<p>This code gives us the results below.</p>

<p><a href="https://i.sstatic.net/0Gu9i.png" rel="noreferrer"><img src="https://i.sstatic.net/0Gu9i.png" alt="Screen Shot"></a></p>

<p>Note, there are a few edge-cases having to do with <em>almost</em> backface-culled triangles being flipped by this process, and I began to address this in code, yet decided to simply avoid these cases for now. Perhaps I'll revisit it when I get this project done.</p>

CC BY-SA 3.0
2017-05-13T03:36:59.053Z
Nice work figuring this out! Really like the mathematical description at the beginning.
user1118321
CC BY-SA 3.0
2017-05-12T17:50:49.733Z
2017-05-13T13:07:48.043Z
2017-05-13T13:07:48.043Z
Jack
<p>I need to offset all (blue) triangles, each independently of the others, using the vertex-shader. In order to manipulate the triangle as a whole, I've created custom (vec3) attributes for each vertex (red) representing the leftward (purple) and rightward (green) neighboring vertices. From this, I need to derive the orange point, equidistant (in <strong><em>screen space</em></strong>) from both adjoining edges. With three such orange points derived from each triangle, the processed (orange) triangle is passed on to the fragment shader.</p>

<p><a href="https://i.sstatic.net/hN8dz.png" rel="noreferrer"><img src="https://i.sstatic.net/hN8dz.png" alt="per vertex operation"></a>
<a href="https://i.sstatic.net/tmoMe.png" rel="noreferrer"><img src="https://i.sstatic.net/tmoMe.png" alt="offset triangles"></a></p>

<p>Ideally, the triangle will be culled (as in backfacing/ not rendered) if the offsets negate any available space within the triangle, such as in the second triangle in the second picture.</p>

<p>I'm using <a href="http://threejs.org/docs/#api/core/BufferGeometry" rel="noreferrer">THREE.BufferGeometry()</a> as my data structure.</p>

<p>Here is a screen shot of the effect I'm aiming for:</p>

<p><a href="https://i.sstatic.net/W9AzA.png" rel="noreferrer"><img src="https://i.sstatic.net/W9AzA.png" alt="enter image description here"></a></p>

CC BY-SA 3.0
2017-05-09T22:18:30.857Z
Could you add a bit more about the wider context? Are the offset triangles to remain attached as in the original mesh? Does "culled" mean the original triangle is discarded, or just that the offsetting is abandoned, leaving the triangle at its original size?
trichoplax is on Codidact now
CC BY-SA 3.0
2017-05-09T22:32:41.903Z
So... how does this work with meshes? Because in a mesh, a vertex has more than 2 neighbors. Or is this just for individual triangles?
Nicol Bolas
CC BY-SA 3.0
2017-05-09T23:02:06.493Z
My implementation is such that all triangles are laid out in a continuous buffer:
[P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] with neighboring points also laid out explicitly at attributes. This way, each vertex of each face can be calculated and manipulated without affecting neighboring faces. Nicol Bolas, yes, dealing with each triangle separately.
Jack
CC BY-SA 3.0
2017-05-11T17:38:10.983Z
trichoplax - "Culled" means thrown out, not rendered, as in a back-facing, single-sided primitive.
Jack
CC BY-SA 3.0
2017-05-11T17:42:38.940Z
Both of you seem to suggest that the GPU sees faces as "tethered" to other faces. As far as I know, this is not the case. The GPU sees faces as separate entities. Faces only appear to be connected because congruent vertices from neighboring faces typically have identical attributes. One common exception is face normals in a flat-shaded mesh.
Jack
CC BY-SA 3.0
2017-05-12T16:44:45.493Z
@Jackalope: "*Both of you seem to suggest that the GPU sees faces as "tethered" to other faces.*" That's because, generally speaking, this is true. Most meshes don't merely have neighboring triangles use "identical attributes"; they reuse *the same vertices*. This could be through triangle lists that use the same index multiple times, or through triangle strips, or whatever. But generally speaking, meshes reuse neighboring vertices. Your meshes do not, but your specific case doesn't change the general case. That's why I asked for clarification.
Nicol Bolas
CC BY-SA 3.0
2017-05-12T17:14:54.217Z
Thanks for that, Nicol Bolas. I was unclear about that. I'd thought that vertex data was usually expanded to explicit values before going to the GPU, ala STL-style rather than OBJ-style notation.
Jack
<shader><three.js>
How can I offset/shrink a triangular polygon in GLSL?
Jack
# How can I offset/shrink a triangular polygon in GLSL?

(Asked by: username_0 on 2017-05-09)

I need to offset all (blue) triangles, each independently of the others, using the vertex-shader. In order to manipulate the triangle as a whole, I've created custom (vec3) attributes for each vertex (red) representing the leftward (purple) and rightward (green) neighboring vertices. From this, I need to derive the orange point, equidistant (in ***screen space***) from both adjoining edges. With three such orange points derived from each triangle, the processed (orange) triangle is passed on to the fragment shader.

[![per vertex operation](https://i.sstatic.net/hN8dz.png)](https://i.sstatic.net/hN8dz.png)
[![offset triangles](https://i.sstatic.net/tmoMe.png)](https://i.sstatic.net/tmoMe.png)

Ideally, the triangle will be culled (as in backfacing/ not rendered) if the offsets negate any available space within the triangle, such as in the second triangle in the second picture.

I'm using [THREE.BufferGeometry()](http://threejs.org/docs/#api/core/BufferGeometry) as my data structure.

Here is a screen shot of the effect I'm aiming for:

[![enter image description here](https://i.sstatic.net/W9AzA.png)](https://i.sstatic.net/W9AzA.png)

**Tags:** shader, three.js

> **Comments:**
> **username_1**: Could you add a bit more about the wider context? Are the offset triangles to remain attached as in the original mesh? Does "culled" mean the original triangle is discarded, or just that the offsetting is abandoned, leaving the triangle at its original size?  
> **username_2**: So... how does this work with meshes? Because in a mesh, a vertex has more than 2 neighbors. Or is this just for individual triangles?  
> **username_0**: My implementation is such that all triangles are laid out in a continuous buffer: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] with neighboring points also laid out explicitly at attributes. This way, each vertex of each face can be calculated and manipulated without affecting neighboring faces. username_2 username_2, yes, dealing with each triangle separately.  
> **username_0**: username_1 - "Culled" means thrown out, not rendered, as in a back-facing, single-sided primitive.  
> **username_0**: Both of you seem to suggest that the GPU sees faces as "tethered" to other faces. As far as I know, this is not the case. The GPU sees faces as separate entities. Faces only appear to be connected because congruent vertices from neighboring faces typically have identical attributes. One common exception is face normals in a flat-shaded mesh.  
> **username_2**: @Jackalope: "*Both of you seem to suggest that the GPU sees faces as "tethered" to other faces.*" That's because, generally speaking, this is true. Most meshes don't merely have neighboring triangles use "identical attributes"; they reuse *the same vertices*. This could be through triangle lists that use the same index multiple times, or through triangle strips, or whatever. But generally speaking, meshes reuse neighboring vertices. Your meshes do not, but your specific case doesn't change the general case. That's why I asked for clarification.  
> **username_0**: Thanks for that, username_2 username_2. I was unclear about that. I'd thought that vertex data was usually expanded to explicit values before going to the GPU, ala STL-style rather than OBJ-style notation.  

---

## 2 answers

### Answer by username_0 on 2017-05-12. Score: 9 | Accepted answer

Given triangle ▲ABC, we bisect angle ∠BAC with line AD, derived with [Angle Bisector Theorem](https://en.wikipedia.org/wiki/Angle_bisector_theorem):

BA / BD = CA / CD
[![Triangle Inset Diagram](https://i.sstatic.net/kKiYW.png)](https://i.sstatic.net/kKiYW.png)
Point E represents our objective refined position on the desired resulting inset triangle. As it lies upon angle bisector AD, it is equidistant from sides BA & CA, forming identical right triangles ▲AFE & ▲AGE. We can now use [Sine for Right Triangles](https://en.wikibooks.org/wiki/Geometry/Right_Triangles_and_Pythagorean_Theorem) to find the length of AE:

AE = EG / Sin(∠EAG)

That's all the math we need, so let's cook up some GLSL!

We start off with all the typical attributes: position, normal, and transformation matrices, but since the vertex shader only works on a single vertex, we need to add the neighboring vertices as additional attributes. This way, each vertex will find its own "point E", creating the resulting inset triangle. (Note: I don't call them "B" & "C" here, because they are not yet in *screen space*.)

```
    attribute vec3 left; //vertex to the left of this vertex
    attribute vec3 right; //vertex to the right of this vertex

```

Speaking of screen space, I'm also including the aspect ratio of the display, (and making it a uniform, in case the window is resized.)

After preparing varying normals for the fragment shader, and transforming the face into clipping-space, we can get down to the business of applying the above math:

```
        attribute vec3 left; //vertex to the left of this vertex
        attribute vec3 right; //vertex to the right of this vertex
        uniform float aspect;
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
            vNormal = normal;
            vUv = uv;

            mat4 xform= projectionMatrix * modelViewMatrix;
            vec4 A = xform * vec4( position, 1.0 );
            vec4 B = xform * vec4( left, 1.0 );
            vec4 C = xform * vec4( right, 1.0 );

            vec3 CB = C.xyz - B.xyz;
            vec2 BA = B.xy - A.xy;
            vec2 CA = C.xy - A.xy;
            float lengthBA = length(BA);
            float lengthCA = length(CA);
            float ratio = lengthBA / ( lengthBA + lengthCA );
            vec3 D = B.xyz + ratio * CB.xyz;
            vec3 AD = D - A.xyz;
            vec3 bisect = normalize(AD);

            float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
            float AE = 1.0/(sin(theta)*aspect);
            newPos.z += AE/length(AD) * (D.z - A.z);
            newPos.x += bisect.x*AE;
            newPos.y += bisect.y*AE;

            gl_Position = newPos;
        }

```

This code gives us the results below.

[![Screen Shot](https://i.sstatic.net/0Gu9i.png)](https://i.sstatic.net/0Gu9i.png)

Note, there are a few edge-cases having to do with *almost* backface-culled triangles being flipped by this process, and I began to address this in code, yet decided to simply avoid these cases for now. Perhaps I'll revisit it when I get this project done.

> **Comments:**
> **username_4**: Nice work figuring this out! Really like the mathematical description at the beginning.  

---

### Answer by username_3 on 2019-05-20. Score: 0

This can be achieved without trigonometric functions by scaling down the [incircle](http://mathworld.wolfram.com/Incircle.html) of the triangle.

`incircle()` computes the incircle of the triangle formed by the vertices `A,B,C`, it returns center and radius as `vec4`. The vertices `X=A,B,C` are then moved inwards by the fraction of their distance to the incircle center (`Q-X`) which is equal to the ratio of desired margin to incircle radius (`m/Q.w`).

```c
vec4 incircle(vec3 A, vec3 B, vec3 C) {
    float a = length(B - C), b = length(C - A), c = length(A - B);
    float abc = a + b + c;
    // http://mathworld.wolfram.com/Incenter.html
    vec3 I = (a * A + b * B + c * C) / abc;
    // http://mathworld.wolfram.com/Inradius.html
    float r = 0.5
            * sqrt((-a + b + c) * (a - b + c) * (a + b - c) / abc);
    return vec4(I, r);
}

vec3 A,B,C; // vertices
float m; // margin
vec4 Q = incircle(A,B,C);
A += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - A);
B += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - B);
C += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - C);

```

> **Comments:**
> **username_0**: Very interesting, username_3! I hadn't heard about this function.  

---

