Dado o triângulo ▲ ABC, dividimos o ângulo ACBAC com a linha AD, derivada do Teorema do Bisetor de Ângulos :
BA / BD = CA / CD O
ponto E representa nossa posição refinada objetiva no triângulo de inserção resultante desejado. Como se encontra na bissetriz de ângulo AD, é equidistante dos lados BA e CA, formando triângulos retângulos idênticos ▲ AFE e ▲ AGE. Agora podemos usar Seno para Triângulos Retos para encontrar o comprimento de AE:
EA = EG / Sin (AGEAG)
Isso é tudo o que precisamos, então vamos preparar um GLSL!
Começamos com todos os atributos típicos: matrizes de posição, normal e de transformação, mas como o sombreador de vértice funciona apenas em um único vértice, precisamos adicionar os vértices vizinhos como atributos adicionais. Dessa forma, cada vértice encontrará seu próprio "ponto E", criando o triângulo inserido resultante. (Nota: não os chamo de "B" e "C" aqui, porque eles ainda não estão no espaço da tela .)
attribute vec3 left; //vertex to the left of this vertex
attribute vec3 right; //vertex to the right of this vertex
Por falar em espaço na tela, também incluo a proporção da tela (e a uniformidade, caso a janela seja redimensionada).
Depois de preparar variáveis normais para o shader de fragmento e transformar a face em espaço de recorte, podemos começar a aplicar a matemática acima:
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;
}
Este código fornece os resultados abaixo.
Nota, há alguns borda casos que têm a ver com quase triângulos abatidos-Backface sendo capotou por este processo, e eu comecei a abordar isso no código, mas decidiu simplesmente evitar estes casos por agora. Talvez eu o revise quando concluir esse projeto.