GLSL / GL2.1 Освещение: трансформация в глазное пространство - PullRequest
1 голос
/ 13 ноября 2011

Итак, это помогает преобразовать все в пространство глаз, прежде чем делать расчеты освещения? У меня проблемы с трансформирующейся частью. Я правильно преобразовал нормали, но когда я применяю переводы (когда объект не находится в центре мировой системы координат), освещение остается точно таким же.

Я подтвердил, что нет проблем с кодом C ++. Я буду вставлять мои шейдеры ...

ВОПРОС: Я хотел бы знать, что я не преобразую правильно, и как я должен преобразовать это.

вершинный шейдер ...

const int MAXLIGHTS = 4;

uniform int lightcount;
uniform vec4 lPositions[MAXLIGHTS];

//V = transformed vertex
//N = transformed normal
//E = eye vector
//L = vector from vertex to light
varying vec3 V, N, E, L[MAXLIGHTS];

void main()
{
    int lcount = lightcount > MAXLIGHTS ? MAXLIGHTS : lightcount;

    V = vec3(gl_ModelViewMatrix * gl_Vertex);

    N = gl_NormalMatrix * gl_Normal;

    E = normalize(-V);

    for(int i = 0; i < lcount; i++)
    {
        L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex));
    }

    gl_FrontColor = gl_Color;
    gl_Position = ftransform();
}

фрагментный шейдер ...

const int MAXLIGHTS = 4;

uniform int lightcount;
uniform vec4 lDiffuses[MAXLIGHTS];
uniform vec4 lAmbients[MAXLIGHTS];

varying vec3 V, N, E, L[MAXLIGHTS];
uniform bool justcolor;

void main()
{
    if(justcolor)
    {
        gl_FragColor = gl_Color;
        return;
    }
    int lcount = lightcount > MAXLIGHTS ? MAXLIGHTS : lightcount;

    vec4 ambient;
    vec4 diffuse;
    vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);
    vec4 color = vec4(0.0, 0.0, 0.0, 1.0);

    vec3 H;
    float NL;
    float NH;

    for(int i = 0; i < lcount; i++)
    {
        specular = vec4(0.0, 0.0, 0.0, 0.0);

        ambient = lAmbients[i];

        NL = dot(N, L[i]);
        diffuse = lDiffuses[i] * max(NL, 0.0);

        if(NL > 0.0)
        {
            H = normalize(E + L[i]);
            NH = max(0.0, dot(N, H));
            specular = pow(NH, 40.0) * vec4(0.3, 0.3, 0.3, 1.0);
        }

        color += gl_Color * (diffuse + ambient) + specular;
    }

    gl_FragColor = color;
}

Ответы [ 2 ]

3 голосов
/ 13 ноября 2011

Глазное пространство - это пространство, в которое ваша сцена преобразуется прямо перед тем, как пройти через матрицу проекции. Это то, что ftransform() удобно переносит (под этим я подразумевал полный путь от пространства модели через пространство глаза (преобразование вида модели) до пространства отсечения (преобразование проекции)).

Матрица вида модели содержит полное преобразование из локального объекта в пространство глаза. Однако ваши огни будут находиться не в (каждом) локальном пространстве объекта, а в мировом пространстве. Итак, мы имеем дело с двумя различными преобразованиями:

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

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

Вместо этого вы должны предварительно трансформировать свои световые позиции в пространство глаз, прежде чем передавать их шейдеру (униформе). Так как это сделать. Во-первых, я настоятельно рекомендую вам избавиться от старых функций манипулирования матрицей OpenGL (glRotate, glTranslate, glScale, ... и помощников GLU, таких как gluPerspective, ...). Вещи становятся проще без них, плюс они были удалены из более поздних версий OpenGL.

Так как же это сделать тогда? Скажем, у вас есть матричная библиотека, такая как GLM, но любая другая тоже будет работать. Для рендеринга вашей сцены вы должны следовать этой схеме (псевдокод, подобный Python)

render_scene:
    projection_matrix = Matrix.Perspective(…)
    view_matrix = Matrix.LookAt(…)

    light_positions = []
    for i, light in enumerate(scene.lights):
        if i > MAX_LIGHTS:
             break
        light_positions.append( view_matrix * light.position )

    glUniform3fv(glGetUniformLocation("lPositions"), light_positions)

    for object in scene.objects:
        obj_modelview = view_matrix * object.transform
        # This is using deprecated functionality
        glMatrixMode(GL_MODELVIEW); glLoadMatrix(obj_modelview)
        object.draw()

Как вы можете видеть, источники света "вручную" преобразуются в пространство глаза с помощью view_matrix, тогда как вершины объекта не затрагиваются графическим процессором, но параметры для шейдера установлены для чертежа.

2 голосов
/ 13 ноября 2011
L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex));

Этот код имеет смысл, только если lPositions находится в модели пробел. И это крайне маловероятно.

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

Кроме того, L и E совершенно лишние. Вы получите более точные результаты, вычислив их в фрагментном шейдере. Вычисления достаточно просты и дешевы, и, поскольку вам все равно нужно перенормировать их (чего вы не делаете) в фрагментном шейдере, вы ничего не получите.

L - это положение света в пространстве глаза - положение поверхности глаза. E - это просто направление от глаза к положению, которое является нормализованным отрицанием положения поверхности глаза в пространстве.

...