Почему мой шейдер OpenGL Phong ведет себя как плоский шейдер?
Последние пару недель я изучал OpenGL и столкнулся с некоторыми проблемами при внедрении шейдера Phong. Похоже, что нет никакой интерполяции между вершинами, несмотря на мое использование smooth
Классификатор. Я что-то здесь упускаю? Чтобы отдать должное должному, код вершинного и фрагментного шейдеров сильно зависит от OpenGL SuperBible 5th Edition. Я очень рекомендую эту книгу!
Вершинный шейдер:
#version 330
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix; // mvp = ModelViewProjection
uniform mat4 mvMatrix; // mv = ModelView
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main(void) {
vVaryingNormal = normalMatrix * vNormal;
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
vVaryingLightDir = normalize(vLightPosition - vPosition3);
gl_Position = mvpMatrix * vVertex;
}
Фрагмент шейдера:
#version 330
out vec4 vFragColor;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main(void) {
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
vFragColor = diff * diffuseColor;
vFragColor += ambientColor;
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir),normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if(diff != 0) {
float fSpec = pow(spec, 32.0);
vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
}
}
Это (общественное достояние) изображение из Википедии точно показывает, какое изображение я получаю и к чему стремлюсь - я получаю "плоское" изображение, но хочу изображение "Фонг".
Любая помощь будет принята с благодарностью. Спасибо!
редактировать: если это имеет значение, я использую PyOpenGL 3.0.1 и Python 2.6.
edit2:
Решение
Оказывается, проблема была с моей геометрией; Кос был прав. Для всех, у кого есть эта проблема с моделями Blender, Кос указал, что Edit->Faces->Set Smooth
делает трюк. Я обнаружил, что Wings 3D работает "из коробки".
2 ответа
Хм... ты интерполируешь нормальное как varying
переменная, поэтому фрагментный шейдер должен получить правильную для каждого пикселя нормаль.
Единственное объяснение (я могу думать) того факта, что у вас есть результат, как на вашем левом изображении, состоит в том, что каждый фрагмент на данном лице в конечном итоге получает одинаковую норму. Вы можете подтвердить это фрагментным шейдером, например:
void main() {
vFragColor = normalize(vVaryingNormal);
}
Если это так, то остается вопрос: почему? Вершинный шейдер выглядит нормально.
Так может быть что-то не так в вашей геометрии? Какие данные вы отправляете в шейдер? Вы уверены, что правильно рассчитали нормали для каждой вершины, а не только для нормали для каждой грани?
Оранжевые линии являются нормалями диагональной грани, красные линии - нормалями горизонтальной грани.
Если ваши данные выглядят как на изображении выше, то даже с правильным шейдером вы получите плоскую штриховку. Убедитесь, что у вас есть правильные нормали для каждой вершины, как на нижнем изображении. (Их действительно просто рассчитать для сферы.)
В дополнение к этому ответу, вот простой геометрический шейдер, который позволит вам визуализировать ваши нормали. Измените сопутствующий вершинный шейдер по мере необходимости в зависимости от местоположения вашего атрибута и способа отправки ваших матриц.
Но сначала, фотография гигантской головы кролика от нашего друга, кролика из Стэнфорда, как пример результата!
Главное предупреждение: обратите внимание, что мне не нужно преобразовывать нормали с помощью матрицы вида модели вместо правильной матрицы нормалей. Это не будет работать правильно, если ваше представление модели содержит неравномерное масштабирование. Кроме того, длина ваших нормалей не будет правильной, но это мало что значит, если вы просто хотите проверить их направление.
Вершинный шейдер:
#version 330
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 normal;
layout(location = 2) in mat4 mv;
out Data
{
vec4 position;
vec4 normal;
vec4 color;
mat4 mvp;
} vdata;
uniform mat4 projection;
void main()
{
vdata.mvp = projection * mv;
vdata.position = position;
vdata.normal = normal;
}
Геометрия шейдера:
#version 330
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;
in Data
{
vec4 position;
vec4 normal;
vec4 color;
mat4 mvp;
} vdata[3];
out Data
{
vec4 color;
} gdata;
void main()
{
const vec4 green = vec4(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 blue = vec4(0.0f, 0.0f, 1.0f, 1.0f);
for (int i = 0; i < 3; i++)
{
gl_Position = vdata[i].mvp * vdata[i].position;
gdata.color = green;
EmitVertex();
gl_Position = vdata[i].mvp * (vdata[i].position + vdata[i].normal);
gdata.color = blue;
EmitVertex();
EndPrimitive();
}
}
Фрагмент шейдера:
#version 330
in Data
{
vec4 color;
} gdata;
out vec4 outputColor;
void main()
{
outputColor = gdata.color;
}