Шейдер Cook-Torrance с более чем одной точечной подсветкой

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

Я определяю материалы следующим образом:

struct material {
    vec3 ambient; /* ambient color */
    vec3 diffuse; /* diffuse color */
    vec3 specular; /* speculr color */
    float metallic;
    float roughness;
};

... благодаря чему мои огни имеют только одно свойство цвета / интенсивности,

struct light {
    vec3 position;
    vec3 color;
    bool enabled;
};

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

vec3 lighting() {
    vec3 color = vec3(0.0,0.0,0.0);
    float r0 = pow(material.metallic - 1.0,2.0)/pow(material.metallic + 1.0,2.0);
    vec3 V = normalize(-v_viewpos);
    vec3 N = normalize(v_normal);
    for (int i = 0; i < 4; i++) {
        if (light[i].enabled) {
            vec3 L = normalize(light[i].position - v_viewpos);
            // Half-way vector 
            vec3 halfVector = normalize(L + V);
            float NdotL = max(dot(N, L),0.0);
            float NdotV = max(dot(N, V),0.0);
            if (NdotL > 0.001 && NdotV > 0.001) {
                float NdotH = max(0.0, dot(N, halfVector));
                float HdotV = max(0.0, dot(halfVector, V));
                // Beckmann 
                float tanAlpha = sqrt(1.0-NdotH*NdotH)/NdotH;
                float D = exp(-pow(tanAlpha/material.roughness,2.0))/(4.0*pow(material.roughness,2.0)*pow(NdotH,4.0));
                // Shadowing-masking term 
                float G1 = (2.0 * NdotH * NdotV) / HdotV;
                float G2 = (2.0 * NdotH * NdotL) / HdotV;
                float G = min(1.0, min(G1, G2));
                // Fresnel reflection, Schlick approximation 
                float F = r0 + (1.0 - r0) * pow(1.0 - NdotL, 5.0);
                float R = (F*G*D) / (3.14159 * NdotL * NdotV);
                color += light[i].color * R * NdotL;
            }
            color += material.diffuse * light[i].color;
        }
    }
    return color;
}

Я считаю, что ключевой момент здесь - мои неправильные вычисления в цикле освещения:

color += light[i].color * R * NdotL;

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

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

Я читаю здесь о гамма-коррекции, но не могу понять, относится ли это к моему вопросу или нет.

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

2 ответа

Большинство ваших расчетов выглядят хорошо (в том числе направления V а также L и термин Френеля). Единственное, что вы могли бы перепутать, как комбинировать отдельные компоненты освещения. Для зеркального и рассеянного у вас есть

color += light[i].color * R * NdotL;

R соответствует зеркальной части и NdotL к диффузной части. Оба являются аддитивными, однако. Следовательно, уравнение должно быть (плюс с учетом материальных параметров):

color += light[i].color * (material.specular * R + material.diffuse * NdotL);

На окружающий срок у вас есть

color += material.diffuse * light[i].color;

замещать material.diffuse с material.ambient и это должно быть правильно.

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

  • vec3 V должен быть нормализованным вектором, начиная с позиции фрагмента до положения камеры.
  • vec3 L должен быть нормализованным вектором, начиная с позиции фрагмента до позиции света.

Один из этих векторов неверен в вашем шейдере, в зависимости от фактического значения v_viewpos,


Френель должен быть основан на HoV, а не на NoL:pow(1.0 - HoV, 5.0)


Для рассеянной части вы рассматриваете свой свет как рассеянный, а не точечный.

color += material.diffuse * light[i].color;

должно быть (для простого Lambertian)

color += material.diffuse * light[i].color * NoL;

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