Учитывая треугольник ▲ ABC, мы делим пополам угол ACBAC с линией AD, полученной с помощью теоремы Бисектора об углах :
BA / BD = CA / CD
Точка E представляет нашу объективную уточненную позицию на желаемом результирующем врезанном треугольнике. Поскольку он лежит на биссектрисе угла AD, он равноудален от сторон BA и CA, образуя одинаковые прямоугольные треугольники ▲ AFE и ▲ AGE. Теперь мы можем использовать синус для прямоугольных треугольников, чтобы найти длину AE:
AE = EG / Sin (AGEAG)
Это все, что нам нужно, так что давайте приготовим немного GLSL!
Мы начнем со всех типичных атрибутов: матрицы положения, нормали и преобразования, но поскольку вершинный шейдер работает только с одной вершиной, нам нужно добавить соседние вершины в качестве дополнительных атрибутов. Таким образом, каждая вершина найдет свою собственную «точку E», создав результирующий треугольник вставки. (Примечание: я не называю их "B" и "C" здесь, потому что они еще не находятся на экране .)
attribute vec3 left; //vertex to the left of this vertex
attribute vec3 right; //vertex to the right of this vertex
Говоря о пространстве экрана, я также включаю соотношение сторон дисплея (и делаю его равномерным в случае изменения размера окна).
После подготовки различных нормалей к фрагментному шейдеру и преобразования лица в пространство отсечения, мы можем приступить к применению приведенной выше математики:
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;
}
Этот код дает нам результаты ниже.
Обратите внимание, что есть несколько краевых случаи , имеющие отношение к почти Backface-забиты треугольникам быть перевернуты этим процессом, и я начал решать это в коде, но решил просто избежать этих случаев сейчас. Возможно, я вернусь к нему, когда закончу этот проект.