Вывод фрагментного шейдера не зависит от вращения

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

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

#version 120

varying vec3 normal, position;
uniform vec3 scale, translation;
uniform mat4 rotation;

void main()
{
    vec4 vertex= (gl_Vertex * rotation) * vec4(scale,1.0) + vec4(translation,0.0);
    gl_Position= gl_ProjectionMatrix * gl_ModelViewMatrix *vertex ;
    normal= normalize(gl_NormalMatrix * gl_Normal);
    position= vec3(gl_ModelViewMatrix * gl_Vertex);
}

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

#version 120

#pragma optionNV( unroll none)

uniform int model;
// 1: phong, 2: blinn-phong
varying vec3 normal, position;
// 1: directional, 2:point, 3:spot
uniform int lightType;
uniform float spotExponent;


vec4 phong(vec3 L, vec3 N, vec3 V, int light) {
    float LdotN= max(dot(L,N),0.0);
    vec3 R= 2.0 * LdotN * N - L;
    vec4 color= gl_FrontMaterial.ambient * gl_LightSource[light].ambient;
    color+= gl_FrontMaterial.diffuse * gl_LightSource[light].diffuse * LdotN;
    color+= gl_FrontMaterial.specular * gl_LightSource[light].specular * pow(max(dot(R,V),0.0),gl_FrontMaterial.shininess);
    return color;
}

vec4 blinnPhong(vec3 L, vec3 N, vec3 V, int light) {
    float LdotN= max(dot(L,N),0.0);
    vec3 H= normalize(L+V);
    vec4 color= gl_FrontMaterial.ambient * gl_LightSource[light].ambient;
    color+= gl_FrontMaterial.diffuse * gl_LightSource[light].diffuse * LdotN;
    color+= gl_FrontMaterial.specular * gl_LightSource[light].specular * pow(max(dot(H,N),0.0),gl_FrontMaterial.shininess);
    return color;
}

float norm(vec3 v) {
    return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

float pointLightAttenuation(float range, int light) {
    float distance= norm(gl_LightSource[light].position.xyz - position);
    if(distance > range)
        return 0.0;
    else
        return max(1.0,1.0/(gl_LightSource[light].quadraticAttenuation*distance*distance+gl_LightSource[light].linearAttenuation*distance+gl_LightSource[light].constantAttenuation));
}

float spotLightAttenuation(vec3 L, int light)  
{
    //return pow(max(dot(normalize(gl_LightSource[light].spotDirection),-L),0.0),spotExponent);
    return max(dot(normalize(gl_LightSource[light].spotDirection),-L),0.0);
}


void main()
{
    vec3 N= normalize(normal);
    vec3 L;
    float attenuation= 1.0;
    if(lightType== 1) { 
        // If light is directional
        L= normalize(- position);
    }
    else {
        L= normalize(gl_LightSource[0].position.xyz - position);
        attenuation= pointLightAttenuation(90.0,0);
        if(lightType== 3)
            attenuation*= spotLightAttenuation(L,0);
    }
    vec3 V= -normalize(position);
    if(model==1)
        gl_FragColor= attenuation * phong(L,N,V,0);
    else
        gl_FragColor= attenuation * blinnPhong(L,N,V,0);
}

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

Это скриншот чайника, нарисованный до поворота:

введите здесь описание изображения

А этот после вращения вокруг оси Y:

введите здесь описание изображения

Как вы видите, это похоже на то, как если бы свет двигался вместе с чайником, но свет находился бы в фиксированном положении: всегда (0,0,-15)!


person Ramy Al Zuhouri    schedule 19.11.2013    source источник
comment
Эй, position, который вы передаете от вершинного шейдера к фрагментному шейдеру, вычисляется gl_ModelViewMatrix * gl_Vertex, в котором вы не использовали вектор преобразования и поворота.   -  person starrify    schedule 19.11.2013


Ответы (1)


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

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

Так что просто измени

normal= normalize(gl_NormalMatrix * gl_Normal);
position= vec3(gl_ModelViewMatrix * gl_Vertex);

to

normal = normalize(gl_NormalMatrix * ((gl_Normal * mat3(rotation)) / scale));
position = vec3(gl_ModelViewMatrix * vertex);

Но, честно говоря, вам будет намного лучше, если вы просто вычислите правильную матрицу из ваших трех параметров и умножите ее на матрицу представления модели на ЦП, прежде чем что-либо рисовать, поскольку вы все равно используете gl_ModelViewMatrix для вычисления положения вершины (будь то это необходимо, учитывая, что вы, похоже, не устанавливаете для него никаких значений, это другой вопрос). И я также пока проигнорирую тот факт, что ваше умножение с матрицей rotation на самом деле противоположно порядку, который обычно использует OpenGL (и который вы используете сами с другими матрицами), предполагая, что вы знаете об этом и знаете, что делаете. .

person Christian Rau    schedule 19.11.2013