Вектор нормали к поверхности можно рассчитать приблизительно по частной производной от положения пространства вида в шейдере frgament. Частную производную можно получить по функциям dFdx
и dFdy
. Для этого требуется OpenGL es 3.0 или расширение OES_standard_derivatives
:
in vec3 view_position;
void main()
{
vec3 normalvector = cross(dFdx(view_position), dFdy(view_position));
nv = normalize(normalvector * sign(normalvector.z));
.....
}
В общем случае можно вычислить вектор нормали поверхности в геометрическом шейдере (начиная с OpenGL ES 3.2).
Например, если вы рисуете треугольники, вы получаете все три точки в геометрическом шейдере.
Три точки определяют плоскость, из которой можно вычислить вектор нормали.
Вы просто должны быть осторожны, если точки расположены по часовой стрелке или против часовой стрелки.
Нормальный вектор треугольника - это нормализованное перекрестное произведение двух векторов, определенных
по угловым точкам треугольника.
Смотрите следующий пример для треугольников против часовой стрелки:
Вершинный шейдер
#version 400
layout (location = 0) in vec3 inPos;
out vec3 vertPos;
uniform mat4 u_projectionMat44;
uniform mat4 u_modelViewMat44;
void main()
{
vec4 viewPos = u_modelViewMat44 * vec4( inPos, 1.0 );
vertPos = viewPos.xyz;
gl_Position = u_projectionMat44 * viewPos;
}
Геометрия шейдера
#version 400
layout( triangles ) in;
layout( triangle_strip, max_vertices = 3 ) out;
in vec3 vertPos[];
out vec3 geoPos;
out vec3 geoNV;
void main()
{
vec3 leg1 = vertPos[1] - vertPos[0];
vec3 leg2 = vertPos[2] - vertPos[0];
geoNV = normalize( cross( leg1, leg2 ) );
geoPos = vertPos[0];
EmitVertex();
geoPos = vertPos[1];
EmitVertex();
geoPos = vertPos[2];
EmitVertex();
EndPrimitive();
}
Фрагмент шейдера
#version 400
in vec3 geoPos;
in vec3 geoNV;
void main()
{
// ...
}
Конечно, вы можете вычислять вектор нормалей также в шейдерах тесселяции (начиная с OpenGL ES 3.2).
Но это имеет смысл, только если вы уже потребовали тесселяционный шейдер по другим причинам и дополнительно рассчитали
нормальный вектор лица:
Вершинный шейдер
Вершинный шейдер такой же, как и выше.
Шейдер управления тесселяцией
#version 400
layout( vertices=3 ) out;
in vec3 vertPos[];
out vec3 tctrlPos[];
void main()
{
tctrlPos[gl_InvocationID] = vertPos[gl_InvocationID];
if ( gl_InvocationID == 0 )
{
gl_TessLevelOuter[0] = ;
gl_TessLevelOuter[1] = ;
gl_TessLevelOuter[2] = ;
gl_TessLevelInner[0] = ;
}
}
Шейдер оценки тесселяции
#version 400
layout(triangles, ccw) in;
in vec3 tctrlPos[];
out vec3 tevalPos;
out vec3 tevalNV;
void main()
{
vec3 leg1 = tctrlPos[1] - tctrlPos[0];
vec3 leg2 = tctrlPos[2] - tctrlPos[0];
tevalNV = normalize( cross( leg1, leg2 ) );
tevalPos = tctrlPos[0] * gl_TessCoord.x + tctrlPos[1] * gl_TessCoord.y + tctrlPos[2] * gl_TessCoord.z;
}
Fragmant shader
#version 400
in vec3 tevalPos;
in vec3 tevalNV;
void main()
{
// ...
}