Как исправить проблему с освещением (проблему с фрагментным шейдером?) При использовании нескольких источников света в OpenGL - PullRequest
1 голос
/ 20 апреля 2020

Я работаю над проектом, который требует двух источников света в opengl. Это должны быть направленные огни. Я на 99% уверен, что выбрал маршрут, который сложнее, чем должен был быть, но я не очень хорошо понимаю проект, поэтому все, что я мог найти, это маршрут, который я выбрал. У меня есть объект пирамиды с текстурой на нем. У меня также есть два отдельных огня, заполняющий свет и ключевой свет. Заполняющий свет больше для окружающего освещения, а ключевой свет - это основная «лампочка» как таковая. Для всех 3 объектов у меня есть вершинный шейдер и фрагментный шейдер (хотя я не знаю, действительно ли мне нужно было / нужно было это сделать). У меня есть список вершин, которые рисуют фигуры, содержат нормали и координаты текстуры. Основной объект пирамиды использует все 3 из этих атрибутов, но источники света используют только вершины, поэтому они имеют одинаковую форму.

Моя главная проблема заключается в том, что источники света работают неправильно. Пирамида освещается только через окружающее освещение ключевого света. Он не включает в себя компоненты рассеянного или зеркального освещения. Заполняющий свет также, по-видимому, в значительной степени игнорируется, но, похоже, потому что ключевой свет перекрывает все другие данные освещения.

Вот как мои шейдеры выглядят для основной пирамиды:

/* Pyramid Vertex Shader Source Code */
const GLchar * pyramidVertexShaderSource = GLSL(330,
    layout (location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
    layout (location = 1) in vec3 normal; // VAP position 1 for normals
    layout (location = 2) in vec2 textureCoordinate; // Color data from Vertex Attrib Pointer 1

    out vec3 Normal; // For outgoing normals to fragment shader
    out vec3 FragmentPos; // For outgoing color / pixels to fragment shader
    out vec2 mobileTextureCoordinate; // variable to transfer Texture data to the fragment shader


    // Global variables for the transform matrices
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

void main(){
        gl_Position = projection * view * model * vec4(position, 1.0f); // transforms vertices to clip coordinates
        FragmentPos = vec3(model * vec4(position, 1.0f)); // Gets fragment / pixel position in world space only (exclude view and projection)
        mobileTextureCoordinate = vec2(textureCoordinate.x, 1.0f - textureCoordinate.y); // flips the texture horizontal
        Normal = mat3(transpose(inverse(model))) * normal; // get normal vectors in world space only and exclude normal translation properties
    }
);


/* Pyramid Fragment Shader Source Code */
const GLchar * pyramidFragmentShaderSource = GLSL(330,

        in vec2 mobileTextureCoordinate; // Variable to hold incoming color data from vertex shader
        in vec3 Normal; // For incoming normals
        in vec3 FragmentPos; // For incoming fragment position

        out vec4 pyramidColor; // For outgoing pyramid color


        // Uniform variables for object color, light color, light position, and camera/view position
        uniform vec3 fillLightColor;
        uniform vec3 fillLightPos;
        uniform vec3 keyLightColor;
        uniform vec3 keyLightPos;
        uniform vec3 viewPosition;
        uniform sampler2D uTexture; // Useful when working with multiple textures

        void main() {
            /* Phong lighting model calculations to generate ambient, diffuse, and specular components */

            // Calculate Ambient lighting
            float ambientStrength = 0.3f; // Set ambient or global lighting strength
            vec3 ambient = ambientStrength * fillLightColor; // Generate ambient light color

            // Calculate Diffuse lighting for fill lamp
            vec3 norm = normalize(Normal); // Normalize vectors to 1 unit
            vec3 lightDirection = normalize(fillLightPos - FragmentPos); // Calculate distance (light direction) between fill light source and fragments/pixels
            float impact = max(dot(norm, lightDirection), 0.0); // Calculate diffuse impact by generating dot product of normal and light
            vec3 diffuse = impact * fillLightColor; // Generate diffuse light color

            // Calculate Specular lighting for fill lamp
            float specularIntensity = 0.8f; // Set specular light strength
            float highlightSize = 16.0f; // Set specular highlight size
            vec3 viewDir = normalize(viewPosition - FragmentPos); // Calculate view direction
            vec3 reflectDir = reflect (-lightDirection, norm); // Calculate reflection vector
            //Calculate specular component
            float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
            vec3 specular = specularIntensity * specularComponent * fillLightColor;

            // Calculate Ambient lighting for key lamp
            float keyAmbientStrength = 1.0f;
            vec3 keyAmbient = keyAmbientStrength * keyLightColor;

            // Calculate Diffuse lighting for key lamp
            vec3 keyLightDirection = normalize(keyLightPos - FragmentPos); // Calculate distance between key light source and fragments
            float keyImpact = max(dot(norm, keyLightDirection), 0.0);
            vec3 keyDiffuse = keyImpact * keyLightColor;

            // Calculate Specular lighting for key lamp
            float keySpecularIntensity = 0.8f; // Set specular light strength
            float keyHighlightSize = 32.0f; // Set specular highlight size
            vec3 keyReflectDir = reflect (-keyLightDirection, norm); // Calculate reflection vector
            //Calculate specular component
            float keySpecularComponent = pow(max(dot(viewDir, keyReflectDir), 0.0), keyHighlightSize);
            vec3 keySpecular = keySpecularIntensity * keySpecularComponent * keyLightColor;

            // Calculate phong result
            vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
            vec3 fillResult = (ambient + diffuse + specular);
            vec3 keyResult = (keyAmbient + keyDiffuse + keySpecular);
            vec3 lightingResult = fillResult + keyResult;
            vec3 phong = (lightingResult) * objectColor;
            pyramidColor = vec4(phong, 1.0f); // Send lighting results to GPU

        }
    ); 

Насколько я могу судить, моя проблема в моей математике для расчета освещения, но независимо от того, как я Попробуй изменить это ... Я просто недостаточно умен, чтобы знать, что именно происходит. По общему признанию мои навыки линейной алгебры - мусор, таким образом, мне действительно трудно. Я уверен, что в моем коде много ошибок и то, как я подошел к проблеме, но, вероятно, после 15-20 часов попыток это лучшее, что мне удалось загрузить и выглядело даже удаленно правильно.

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

Ответы [ 2 ]

1 голос
/ 20 апреля 2020

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

Vertex Shader :

Итак, для вашего Vertex Shader вы берете 3 указателя attrib: положение, нормаль и координата текстуры - это стандартно, и я ожидаю, что вы ' Вы правильно буферизируете / передаете данные.

layout (location = 0) in vec3 position; 
layout (location = 1) in vec3 normal; 
layout (location = 2) in vec2 textureCoordinate;

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

out vec3 Normal;
out vec3 FragmentPos; 
out vec2 mobileTextureCoordinate; 

Вы также передаете нормали вершин, положение фрагмента и координату текстуры фрагментному шейдеру (кстати, почему вы называете это «Мобильной текстурной координатой»? Я недостаточно разбираюсь в догадках); здесь все выглядит хорошо: никаких проблем.

Тогда у вас есть униформа: объявления модели, вида и матрицы проекции - конечно, нет проблем.

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

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

void main(){
        gl_Position = projection * view * model * vec4(position, 1.0f); 

выглядит хорошо.

    FragmentPos = vec3(model * vec4(position, 1.0f)); 

выглядит хорошо, вы игнорируете матрицу проекции и, очевидно, матрица вида.

    mobileTextureCoordinate = vec2(textureCoordinate.x, 1.0f - textureCoordinate.y);

Вы переворачиваете координату текстуры по горизонтали (пока не знаю, почему, но неважно)

        Normal = mat3(transpose(inverse(model))) * normal; 
    }
);

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

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

    in vec2 mobileTextureCoordinate; 
    in vec3 Normal; 
    in vec3 FragmentPos; 
    out vec4 pyramidColor; // For outgoing pyramid color

Пока проблем нет

    uniform vec3 fillLightColor;
    uniform vec3 fillLightPos;
    uniform vec3 keyLightColor;
    uniform vec3 keyLightPos;
    uniform vec3 viewPosition;
    uniform sampler2D uTexture; 

Предполагается, что эти формы заданы правильно, и вы вызываете glUseProgram () / "shader" .useProgram () / связывание эквивалента шейдера перед установкой униформ с помощью вызовов glUniformX

    void main() {

        // Calculate Ambient lighting
        float ambientStrength = 0.3f; 
        vec3 ambient = ambientStrength * fillLightColor; 

        // Calculate Diffuse lighting for fill lamp
        vec3 norm = normalize(Normal); // Normalize vectors to 1 unit
        vec3 lightDirection = normalize(fillLightPos - FragmentPos); // Calculate distance (light direction) between fill light source and fragments/pixels
        float impact = max(dot(norm, lightDirection), 0.0); // Calculate diffuse impact by generating dot product of normal and light
        vec3 diffuse = impact * fillLightColor; // Generate diffuse light color

        // Calculate Specular lighting for fill lamp
        float specularIntensity = 0.8f; // Set specular light strength
        float highlightSize = 16.0f; // Set specular highlight size
        vec3 viewDir = normalize(viewPosition - FragmentPos); // Calculate view direction
        vec3 reflectDir = reflect (-lightDirection, norm); // Calculate reflection vector
        //Calculate specular component
        float specularComponent = pow(max(dot(viewDir, reflectDir), 0.0), highlightSize);
        vec3 specular = specularIntensity * specularComponent * fillLightColor;

        // Calculate Ambient lighting for key lamp
        float keyAmbientStrength = 1.0f;
        vec3 keyAmbient = keyAmbientStrength * keyLightColor;

        // Calculate Diffuse lighting for key lamp
        vec3 keyLightDirection = normalize(keyLightPos - FragmentPos); // Calculate distance between key light source and fragments
        float keyImpact = max(dot(norm, keyLightDirection), 0.0);
        vec3 keyDiffuse = keyImpact * keyLightColor;

        // Calculate Specular lighting for key lamp
        float keySpecularIntensity = 0.8f; // Set specular light strength
        float keyHighlightSize = 32.0f; // Set specular highlight size
        vec3 keyReflectDir = reflect (-keyLightDirection, norm); // Calculate reflection vector
        //Calculate specular component
        float keySpecularComponent = pow(max(dot(viewDir, keyReflectDir), 0.0), keyHighlightSize);
        vec3 keySpecular = keySpecularIntensity * keySpecularComponent * keyLightColor;

        // Calculate phong result
        vec3 objectColor = texture(uTexture, mobileTextureCoordinate).xyz;
        vec3 fillResult = (ambient + diffuse + specular);
        vec3 keyResult = (keyAmbient + keyDiffuse + keySpecular);
        vec3 lightingResult = fillResult + keyResult;
        vec3 phong = (lightingResult) * objectColor;
        pyramidColor = vec4(phong, 1.0f); // Send lighting results to GPU

    }
); 

Вы сошли с ума? Вы уверены, что pyramidColor действительно устанавливает цвет фрагмента?

Все это выглядит готово ie Резак с этого момента, но я не проверял себя!

1 голос
/ 20 апреля 2020

На первый взгляд ваш код выглядит правильно. Конечно, цвет фрагмента зависит от цвета света и цвета объекта (текстуры). Обычно умножают (модулируют) цвет объекта и цвет света. Другая возможность - суммировать цвет объекта и цвет света. Во всяком случае, это не проблема в вашем коде шейдера.

Но есть много возможных проблем. Убедитесь, что все формы правильно установлены. Убедитесь, что источник света не находится «позади» объекта, а цвета света не «нулевые».

Часто встречающаяся проблема заключается в том, что точечный источник света расположен в me sh. В этом случае внутренняя часть объекта подсвечивается (что вы не можете видеть), но внешняя сторона находится на теневой стороне.

Общая проблема состоит в том, что вектор нормали поверхности указывает на внутреннюю часть я sh. Это вообще не вызовет рассеянного и зеркального света, поскольку произведение Dot вычисляет значение, которое пропорционально косинусу угла между двумя векторами. Если угол между двумя векторами больше 90 °, косинус становится отрицательным, и, следовательно, произведение точек также отрицательно.

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

vec3 norm    = normalize(Normal);
vec3 viewDir = normalize(viewPosition - FragmentPos);

if (dot(norm, viewDir) < 0.0)
    norm = -norm;

В любом случае, это ничего не поможет, если атрибут вектора vector задан неправильно, а все векторы нормалей (0, 0, 0) .
Чтобы оценить нормальные векторы, вы можете выполнить простой тест отладки. Просто добавьте вектор нормали к pyramidColor в последней строке фрагмента шейдера:

void main() {
    // [...]

    pyramidColor.rgb += abs(norm.xyz);
}
...