Нечетный эффект с нормалями GLSL

#opengl #glsl #normals

#opengl #glsl #нормали

Вопрос:

В качестве некоторого сходства с проблемой, с которой я сталкивался ранее и о которой писал ранее, я пытаюсь заставить нормали правильно отображаться в моем приложении GLSL. Для целей моего объяснения я использую модель ninjaHead.obj, поставляемую с RenderMonkey для целей тестирования (вы можете ознакомиться с ней здесь). Теперь в окне предварительного просмотра в RenderMonkey все выглядит великолепно: RenderMonkey и код вершины и фрагмента, сгенерированный соответственно, равен:

Вершина:

 uniform vec4 view_position;

varying vec3 vNormal;
varying vec3 vViewVec;

void main(void)
{
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

  // World-space lighting
  vNormal = gl_Normal;
  vViewVec = view_position.xyz - gl_Vertex.xyz;
}
  

Фрагмент:

 uniform vec4 color;

varying vec3 vNormal;
varying vec3 vViewVec;

void main(void)
{
   float v = 0.5 * (1.0   dot(normalize(vViewVec), vNormal));
   gl_FragColor =  v* color;
}
  

Я основал свой GLSL-код на этом, но я не совсем получаю ожидаемые результаты…

Мой код вершинного шейдера:

 uniform mat4 P;
uniform mat4 modelRotationMatrix;
uniform mat4 modelScaleMatrix;
uniform mat4 modelTranslationMatrix;
uniform vec3 cameraPosition;

varying vec4 vNormal;
varying vec4 vViewVec;

void main()
{
vec4 pos = gl_ProjectionMatrix * P * modelTranslationMatrix * modelRotationMatrix * modelScaleMatrix * gl_Vertex;

gl_Position = pos;

gl_TexCoord[0] = gl_MultiTexCoord0;

gl_FrontColor = gl_Color;   

vec4 normal4 = vec4(gl_Normal.x,gl_Normal.y,gl_Normal.z,0);     

// World-space lighting
   vNormal = normal4*modelRotationMatrix;
   vec4 tempCameraPos = vec4(cameraPosition.x,cameraPosition.y,cameraPosition.z,0);     
   //vViewVec = cameraPosition.xyz - pos.xyz;
   vViewVec = tempCameraPos - pos;
}
  

Мой фрагмент кода шейдера:

 varying vec4 vNormal;
varying vec4 vViewVec;

void main()
{
  //gl_FragColor = gl_Color;
  float v = 0.5 * (1.0   dot(normalize(vViewVec), vNormal));
gl_FragColor =  v * gl_Color;
}
  

Однако мой рендеринг приводит к этому…
Рендеринг в OpenGL

Кто-нибудь знает, что может быть причиной этого и / или как заставить это работать?

РЕДАКТИРОВАТЬ В ответ на комментарии кварка, вот модель, отрисованная без каких-либо нормальных расчетов / расчетов освещения, чтобы показать, что все треугольники визуализируются. Визуализация плоского затенения

И вот модель, затеняющая нормали, используемые для цветов. Я считаю, что проблема найдена! Теперь причина в том, почему это так отображается и как это решить? Предложения приветствуются! Обычное затенение

РЕШЕНИЕ Ну что ж, все, проблема решена! Спасибо Кварку за всю его полезную информацию, которая определенно помогла моей практике программирования, но, боюсь, ответ исходит от того, что я БОЛЬШОЙ профан… У меня возникла ошибка в функции display () моего кода, которая установила смещение glNormalPointer на случайное значение. Раньше это было так:

 gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getNormalsBufferObject());
gl.glNormalPointer(GL.GL_FLOAT, 0, getNormalsBufferObject());
  

Но должно было быть так:

 gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getNormalsBufferObject());
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
  

Итак, я думаю, это урок. НИКОГДА не используйте бездумно Ctrl C и Ctrl V для экономии времени в пятницу днем и… Когда вы уверены, что часть кода, на которую вы смотрите, верна, проблема, вероятно, в чем-то другом!

Комментарии:

1. Пожалуйста, пожалуйста, всегда указывайте вашу версию OpenGL. Также хорошей практикой является использование #version директивы в вашем GLSL.

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

Ответ №1:

  1. Какова ваша P матрица? (Я полагаю, это преобразование «мир-> вид с камеры»).

  2. vNormal = normal4*modelRotationMatrix; Почему вы изменили порядок аргументов? Делая это, вы умножаете нормаль на обратный поворот, чего вы на самом деле не хотите. Вместо этого используйте стандартный порядок (modelRotationMatrix * normal4)

  3. vViewVec = tempCameraPos - pos . Это совершенно неверно. pos ваша вершина находится в однородном пространстве клипов, в то время как tempCameraPos она находится в мировом пространстве (я полагаю). Вам нужно получить результат в том же пространстве, что и ваше обычное значение (мировое пространство), поэтому используйте положение вершины в мировом пространстве ( modelTranslationMatrix * modelRotationMatrix * modelScaleMatrix * gl_Vertex ) для этого уравнения.

Комментарии:

1. Спасибо за ответ. Да, P действительно является мировой матрицей камеры. Я внес изменения, которые вы предложили в (2) и (3), но видимых изменений нет. Я не могу понять, что происходит!

2. @Крис Робинсон. На вашем скриншоте видно, что некоторые треугольники вообще не нарисованы. Не могли бы вы залить модель постоянным цветом, чтобы проверить это?

3. @kvark Когда я выполняю рендеринг без нормалей (т. Е. плоского затенения), модель действительно заполняется и отображаются все треугольники.

4. @Крис Робинсон. Ладно, хорошо. Теперь я предлагаю поместить промежуточные результаты функции в цвет, чтобы увидеть, какие аргументы отличаются между рабочим и нерабочим кодом. Попробуйте: gl_FragColor = normalize(vViewVec); и затем =vNormal .

5. @kvark Спасибо за всю вашу помощь до сих пор. Я отрисовал модель, используя нормали для цветов, и получил странные результаты при редактировании. Теперь, очевидно, это неправильно, я бы подумал, что в этом случае нормали должны медленно меняться между соседними треугольниками. Даже если бы некоторые треугольники были нарисованы задом наперед, я бы не ожидал увидеть изменение цвета, которое я вижу. Есть какие-нибудь подсказки относительно того, что это может быть?

Ответ №2:

Вы, кажется, немного смешиваете версии GL? Вы передаете матрицы вручную с помощью uniforms, но используете фиксированную функцию для передачи атрибутов вершин. Хм. В любом случае…


Мне искренне не нравится, что вы делаете со своими нормалями. Взгляните:

 vec4 normal4 = vec4(gl_Normal.x,gl_Normal.y,gl_Normal.z,0);     

vNormal = normal4*modelRotationMatrix;
  

Обычный хранит только данные о направлении, зачем использовать vec4 для этого? Я считаю, что более элегантно просто использовать just vec3 . Кроме того, посмотрите, что происходит дальше — вы умножаете нормаль на матрицу поворота модели 4×4… И, кроме того, четвертая кордината вашей нормали равна 0, так что это неправильный вектор в однородных координатах. Я не уверен, что это главная проблема здесь, но я не удивлюсь, если это умножение даст вам мусор.

Стандартный способ преобразования нормалей — это умножение vec3 на подматрицу 3×3 матрицы представления модели (поскольку вас интересует только ориентация, а не перевод). Ну, точно, «самый правильный» подход заключается в использовании обратного переноса этой подматрицы 3×3 (это становится важным, когда у вас есть масштабирование). В старых версиях OpenGL он предварительно вычислялся как gl_NormalMatrix .

Итак, вместо вышеупомянутого вам следует использовать что-то вроде

 // (...)
varying vec3 vNormal;
// (...)

mat3 normalMatrix = transpose(inverse(mat3(modelRotationMatrix)));
// or if you don't need scaling, this one should work too-
mat3 normalMatrix = mat3(modelRotationMatrix);

vNormal = gl_Normal*normalMatrix;
  

Это, безусловно, одна вещь, которую нужно исправить в вашем коде — я надеюсь, это решит вашу проблему.