iPad GLSL. Из фрагмента шейдера как получить поверхность - не вершина - нормально - PullRequest
4 голосов
/ 26 марта 2010

Можно ли получить доступ к нормали поверхности - нормали, связанной с плоскостью фрагмента - из шейдерного фрагмента? Или, может быть, это можно сделать в вершинном шейдере?

Потеряны ли все знания о соответствующей геометрии при переходе по конвейеру шейдера, или есть какой-то разумный способ восстановить эту информацию в вершине фрагментного шейдера?

Заранее спасибо.

Приветствия
Дуг
twitter: @ dugla

Ответы [ 3 ]

3 голосов
/ 27 апреля 2010

Вы можете получить для каждого пикселя нормали, интерполированные из нормалей вершин, просто используя переменную (в более новом OpenGL это просто вход / выход). Но не забудьте нормализовать это нормально! Интерполированные нормали больше не должны иметь длину 1. Эти нормали также дают плохие результаты на острых гранях.

Если вы хотите использовать пользовательские нормали с более высоким разрешением, то чаще всего используются карты нормалей. Вы создаете текстуру с запеченными нормалями для вашего объекта. Затем вы можете получить доступ к нормали в текстуре фрагмента, используя текстурный поиск.

3 голосов
/ 02 июля 2017

Вектор нормали к поверхности можно рассчитать приблизительно по частной производной от положения пространства вида в шейдере 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()
{
    // ...
}
0 голосов
/ 09 апреля 2010

Если вы передадите нормаль вершины к фрагментному шейдеру в «варьировании», вы получите интерполированный фрагмент нормали.

РЕДАКТИРОВАТЬ: вам придется вычислять нормали в вашем приложении и передавать их в ваш шейдер в качестве атрибута для каждой вершины вашего треугольника.

Обычный способ вычисления нормали для треугольника - это перекрестное произведение.

  1. Назовите три точки, составляющие треугольник P1, P2 и P3.
  2. Рассчитать V1, вектор из P1 в P2.
  3. Рассчитайте V2, вектор из P1 в P3.
  4. Рассчитать перекрестное произведение V1 и V2.

Это даст вам нормаль к плоскости треугольника. V2 должен быть «слева от» V1, или ваша нормальная будет указывать «в» вместо «вне». Подробнее см. Статью Википедии о кросс-продуктах .

ДОПОЛНИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ: Хорошо, теперь я понимаю вашу проблему. Да, это правда, что с общими вершинами вы не можете иметь более одной нормали на вершину.

Единственное, о чем я могу подумать, это то, что, возможно, геометрический шейдер мог бы помочь, потому что он проходит все три вершины для треугольника. У меня нет с ними никакого опыта.

...