Странные зеркальные результаты от GLSL phong shader

Я пытаюсь реализовать затенение фона в GLSL, но у меня есть некоторые проблемы с зеркальным компонентом.

Странный зеркальный результат

Зеленый свет - это зеркальный компонент. Свет (точечный свет) движется по кругу над плоскостью. Зеркальная подсветка всегда направлена ​​внутрь к оси Y, вокруг которой вращается свет и распространяется в направлении рассеянного отражения, как видно на изображении. Это, кажется, не зависит от расположения камеры, и я не уверен, где я иду не так.

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

#version 330 core

/*
* Phong Shading with with Point Light (Quadratic Attenutation)
*/

//Input vertex data
layout(location = 0) in vec3 vertexPosition_modelSpace;
layout(location = 1) in vec2 vertexUVs;
layout(location = 2) in vec3 vertexNormal_modelSpace;

//Output Data; will be interpolated for each fragment
out vec2 uvCoords;

out vec3 vertexPosition_cameraSpace;
out vec3 vertexNormal_cameraSpace;

//Uniforms
uniform mat4 mvMatrix;
uniform mat4 mvpMatrix;
uniform mat3 normalTransformMatrix;


void main()
{
    vec3 normal = normalize(vertexNormal_modelSpace);

    //Set vertices in clip space
    gl_Position = mvpMatrix * vec4(vertexPosition_modelSpace, 1);

    //Set output for UVs
    uvCoords = vertexUVs;

    //Convert vertex and normal into eye space
    vertexPosition_cameraSpace = mat3(mvMatrix) * vertexPosition_modelSpace;    
    vertexNormal_cameraSpace = normalize(normalTransformMatrix * normal);

}

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

#version 330 core

in vec2 uvCoords;

in vec3 vertexPosition_cameraSpace;
in vec3 vertexNormal_cameraSpace;

//out
out vec4 fragColor;

//uniforms
uniform sampler2D diffuseTex;
uniform vec3 lightPosition_cameraSpace;

void main()
{
    const float materialAmbient = 0.025;    //a touch of ambient

    const float materialDiffuse = 0.65;
    const float materialSpec = 0.35;  

    const float lightPower = 2.0;
    const float specExponent = 2;

    //--------------Set Colors and determine vectors needed for shading-----------------           

    //reflection colors- NOTE- diffuse and ambient reflections will use the texture color
    const vec3 colorSpec = vec3(0,1,0); //Green spec color
    vec3 diffuseColor = texture2D(diffuseTex, uvCoords).rgb;    //Get color from the texture at fragment

    const vec3 lightColor = vec3(1,1,1);        //White light

    //Re-normalize normal vectors : after interpolation they make not be unit length any longer
    vec3 normVertexNormal_cameraSpace = normalize(vertexNormal_cameraSpace);

    //Set camera vec
    vec3 viewVec_cameraSpace = normalize(-vertexPosition_cameraSpace);  //Since its view space, camera at origin

    //Set light vec
    vec3 lightVec_cameraSpace = normalize(lightPosition_cameraSpace - vertexPosition_cameraSpace);

    //Set reflect vect
    vec3 reflectVec_cameraSpace = normalize(reflect(-lightVec_cameraSpace, normVertexNormal_cameraSpace));      //reflect function requires incident vec; from light to vertex

    //----------------Find intensity of each component---------------------

    //Determine Light Intensity
    float distance = abs(length(lightPosition_cameraSpace - vertexPosition_cameraSpace));
    float lightAttenuation = 1.0/( (distance > 0) ? (distance * distance) : 1 );    //Quadratic

    vec3 lightIntensity = lightPower * lightAttenuation * lightColor;

    //Determine Ambient Component
    vec3 ambientComp = materialAmbient * diffuseColor * lightIntensity;

    //Determine Diffuse Component
    float lightDotNormal = max( dot(lightVec_cameraSpace, normVertexNormal_cameraSpace), 0.0 );
    vec3 diffuseComp = materialDiffuse *  diffuseColor * lightDotNormal * lightIntensity;

    vec3 specComp = vec3(0,0,0);
    //Determine Spec Component
    if(lightDotNormal > 0.0)
    {
        float reflectDotView = max( dot(reflectVec_cameraSpace, viewVec_cameraSpace), 0.0 );
        specComp = materialSpec *  colorSpec * pow(reflectDotView, specExponent) * lightIntensity;
    }

    //Add Ambient + Diffuse + Spec
    vec3 phongFragRGB = ambientComp + 
          diffuseComp + 
          specComp;  

    //----------------------Putting it together-----------------------

    //Out Frag color
    fragColor = vec4(  phongFragRGB, 1);
}

Просто отметив, что normalTransformMatrix, видимый в шейдере Vertex, является транспонированной в обратном направлении матрицы представления модели.

Я устанавливаю вектор из положения вершины на источник света, на камеру и вектор отражения, все в пространстве камеры. Для рассеянного вычисления я беру скалярное произведение вектора света и вектора нормали, а для зеркального компонента я беру скалярное произведение вектора отражения и вектора обзора. Возможно, есть какое-то фундаментальное недоразумение, которое я имею с алгоритмом?

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

Я знаю, что на сайте много вопросов о затенении фонг, но у всех, кажется, есть проблема, которая немного отличается. Если кто-то может увидеть, где я иду не так, пожалуйста, дайте мне знать. Любая помощь приветствуется.

РЕДАКТИРОВАТЬ: Хорошо, это работает сейчас! Как и предложил jozxyqk ниже, мне нужно было выполнить операцию mat4*vec4 для моей позиции вершины или потерять информацию о переводе. Когда я впервые внес изменение, я получал странные результаты, пока не понял, что совершаю ту же ошибку в своем коде OpenGL для lightPosition_cameraSpace, прежде чем передать его шейдеру (ошибка в том, что я приводил матрицу представления к mat3 для расчета вместо установки вектора положения света в качестве vec4). После того, как я отредактировал эти строки, кажется, шейдер работает правильно! Спасибо за помощь, jozxqk!

1 ответ

Решение

Я вижу две части, которые выглядят неправильно.

  1. "vertexPosition_cameraSpace = mat3 (mvMatrix) * vertexPosition_modelSpace" должен быть умножением mat4/vec4(x,y,z,1), в противном случае он игнорирует часть перевода матрицы представления модели.

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

Другие вопросы по тегам