Зеркальное освещение появляется как на передней, так и на задней сторонах объекта.

Я пишу небольшой тест затенения Фонга и бьюсь головой о кирпичную стену, пытаясь заставить работать зеркальный компонент. Похоже, он работает правильно, за исключением того, что зеркальный свет применяется как к передней, так и к задней части объекта. (Сам объект прозрачен, лица отсортированы на стороне процессора вручную.)

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

Насколько я понимаю, зеркальный компонент пропорционален cos угла между вектором глаза и вектором отраженного света.

Чтобы все было максимально просто, мой тестовый код пытается сделать это в мировом пространстве. Положение камеры и вектор света жестко закодированы (извините) как (0,0,4) и (-0.707, 0, -0.707) соответственно. Таким образом, вектор глаза равен (0,0,4) - fragPosition.

Поскольку матрица модели выполняет только вращение, вершинный шейдер просто преобразует нормаль вершины по матрице модели. (Примечание: это не очень хорошая практика, поскольку многие типы преобразования не сохраняют нормальную ортогональность. Как правило, для нормальной матрицы вы должны использовать обратное транспонирование верхнего левого 3x3 матрицы модели.) Чтобы сохранить Все просто: фрагментный шейдер работает с одним плавающим цветовым каналом и пропускает показатель степени отражения (/ использует показатель степени, равный 1).

Вершинный шейдер:

#version 140

uniform mat4 mvpMtx;
uniform mat4 modelMtx;

in vec3 inVPos;
in vec3 inVNormal;

out vec3 normal_world;
out vec3 fragpos_world;

void main(void) {
  gl_Position = mvpMtx * vec4(inVPos, 1.0);
  normal_world = vec3(modelMtx * vec4(inVNormal, 0.0));
  fragpos_world = vec3(modelMtx * vec4(inVPos, 1.0)); 
}

Фрагментный шейдер:

#version 140

uniform float
    uLightS,
    uLightD,
    uLightA;

uniform float uObjOpacity;

in vec3 normal_world, fragpos_world;
out vec4 fragOutColour;

void main(void) {

    vec3 vN = normalize(normal_world);
    vec3 vL = vec3(-0.707, 0, -0.707);

    // Diffuse:
    // refl in all directions is proportional to cos(angle between -light vector & normal)
    // i.e. proportional to -l.n
    float df = max(dot(-vL, vN), 0.0);

    // Specular:
    // refl toward eye is proportional to cos(angle between eye & reflected light vectors)
    // i.e. proportional to r.e
    vec3 vE = normalize(vec3(0, 0, 4) - fragpos_world);
    vec3 vR = reflect(vL, vN);
    float sf = max(dot(vR, vE), 0.0);

    float col = uLightA + df*uLightD + sf*uLightS;

    fragOutColour = vec4(col, col, col, uObjOpacity);

}

Я не могу найти здесь ошибку; Кто-нибудь может объяснить, почему зеркальный компонент появляется как на задней, так и на светлой стороне объекта?

Большое спасибо.


person ASpence    schedule 15.11.2013    source источник
comment
Нужен скриншот эффекта, какую геометрию вы используете?   -  person Alec Teal    schedule 15.11.2013
comment
Кроме того, это ужасно, вы должны ставить направления как юниформы и иметь отдельную матрицу для преобразования нормалей, а единые масштабы требуются, это только если юниформный масштаб является изометрией, что не всегда верно.   -  person Alec Teal    schedule 15.11.2013
comment
Привет, Алек, извини за жестко закодированные направления, попробуй представить, что это униформа. Я сделал вещи таким образом, чтобы они были ультрапростыми, и все же я не могу найти здесь несоответствия. Скриншот: http:/ben.am/temp/specular-problem.png   -  person ASpence    schedule 15.11.2013
comment
Вы не хотите использовать матрицу представления модели для преобразования векторов нормалей. Они могут не оставаться перпендикулярными, если вы сделаете это. Вместо этого вы хотите использовать транспонирование инверсии верхней левой подматрицы 3x3 NormalMatrix = T (ModelView^-1). Это то, чем был gl_NormalMatrix в старой школе GLSL, но вы можете так же легко вычислить это в своем вершинном шейдере или в другой юниформ-матрице.   -  person Andon M. Coleman    schedule 18.11.2013
comment
Спасибо, Андон. Я постарался устранить проблему с нормалями, прежде чем задавать этот вопрос, но, поскольку я не хочу поощрять использование матрицы модели для преобразования нормалей, я добавил примечание в текст вопроса.   -  person ASpence    schedule 19.11.2013


Ответы (2)


Ваш зеркальный вклад на самом деле будет одинаковым для передней и задней граней, потому что GLSL reflect нечувствителен к знаку нормали. Из этой ссылки:

reflect(I, N) = I - 2.0 * dot(N, I) * N

поэтому переворачивание знака N вводит два знака минус, которые отменяют. Другими словами, reflect меняет знак компонента I, который находится вдоль той же оси, что и N. Это не зависит от того, в какую сторону смотрит N.

Если вы хотите удалить зеркальный компонент с ваших задних граней, я думаю, вам нужно явно проверить свой dot(vE,vN).

person Mike Dinsdale    schedule 15.11.2013
comment
Или умножьте НА член Ламберта, то есть sf*=df - person bjorke; 17.11.2013
comment
Большое спасибо, Майк, это была проблема. Простой способ исправить задний зеркальный блик, который я сейчас использую, будет выглядеть так: float sf = 0.0; if (df > 0.0) { /* calculate sf */ } - person ASpence; 19.11.2013

Попробуйте изменить

fragpos_world = vec3(modelMtx * vec4(inVPos, 0.0)); 

to

fragpos_world = vec3(modelMtx * vec4(inVPos, 1.0)); 

потому что http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#Homogeneous_coordinates

person jparimaa    schedule 15.11.2013
comment
Также стоит отметить, что существует хорошая связь между 1.0 как координатой w, означающей положение, и 0.0 как направлением. - person Alec Teal; 15.11.2013
comment
Спасибо @jpaari, вы абсолютно правы, трансформацию fragpos_world нужно делать с w = 1.0. С другой стороны, нормальное преобразование должно быть выполнено с w = 0,0. Однако проблема остается, к сожалению. - person ASpence; 15.11.2013