Вперше проблеми з тіньовим картографуванням


9

Я вперше реалізував базове тіньове відображення в OpenGL за допомогою шейдерів, і я стикаюся з деякими проблемами. Нижче ви можете побачити приклад моєї відображеної сцени:

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

Наступний процес тіньового відображення полягає в тому, що я відтворюю сцену до кадру кадрів, використовуючи матрицю перегляду зі світлової точки зору, а також матриці проекції та моделі, які використовуються для нормального відображення.

У другому проході я надсилаю вищевказану матрицю MVP зі світлової точки зору до вершинного шейдера, який перетворює положення у світлий простір. Шейдер фрагмента робить розділення точки зору і змінює положення на координати текстури.

Ось мій вершинний шейдер,


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

І мій фрагмент шейдер,


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

Однією з проблем є самозатінення, як ви бачите на малюнку, ящик має власну тінь, накинуту на себе. Те, що я спробував, - це зміщення полігону (тобто glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)), але воно не змінилося сильно. Як ви бачите в шейдері фрагмента, я поставив статичне значення зміщення 0,001, але мені потрібно змінити значення залежно від відстані світла, щоб отримати більш бажані ефекти, що не дуже зручно. Я також спробував використати відсікання передньої частини обличчя, коли я передавався на фреймбуфер, який теж не змінився.

Інша проблема полягає в тому, що пікселі поза межами виду Світла фрустують затіненими. Єдиний об’єкт, який, як передбачається, здатний кидати тіні - це ящик. Я думаю, я повинен вибрати більш відповідні проекції та переглянути матриці, але я не впевнений, як це зробити. Які існують звичайні практики, я повинен вибрати ортографічну проекцію?

З трохи погуглившись, я розумію, що ці питання не такі дрібниці. Хтось має прості втілити рішення цих проблем. Не могли б ви дати мені кілька додаткових порад?

Запитайте, будь ласка, чи вам потрібна додаткова інформація щодо мого коду.

Ось порівняння з і без тіньового картографування крупним планом ящика. Самозатінення видніше.


Схоже, ви повинні детальніше описати свої проблеми. У вас є одне речення про свої проблеми, а наступне речення пропонує виправити це. Розкажіть , які саме проблеми є і що ви вже зробили, щоб спробувати вирішити.
MichaelHouse

Я відредагував свій пост, сподіваюся, що зараз краще.
Grieverheart

Відповіді:


6

Не слід затінювати багатокутники, повернуті назад від світла.

Я змінив ваш фрагмент шейдер на код нижче.

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

8

Щодо самозатінення, я не впевнений, про що ви маєте на увазі - я не бачу жодного «тіньового прища» на ящику на екрані екрану. Якщо ви маєте на увазі, що обличчя, звернені до світла, темні, ну, звичайно, є - вони повинні бути! :) Бічне обличчя виглядає дещо дивно, розділивши діагональ на половину освітленої та наполовину затіненої. Якщо ви використовуєте N крапки L для освітлення, і у вас хороші нормали (тобто жорсткі нормали на цьому кубі, а не згладжені нормали), цього не повинно відбуватися.

Використання зміщення багатокутника та / або малювання тильних сторін на тіньовій карті - це стандартні рішення (добре ... обхідні шляхи дійсно) для тіньових вугрів.

Що стосується проекційної матриці, то ви повинні думати про світло як камеру. Якщо це точкове світло, то це була б перспективна камера, і якщо це спрямоване світло, це було б як ортографічна камера. Побудуйте матрицю проекції так само, як і для камери, використовуючи поле зору світла, співвідношення сторін тощо.

Щоб обмежити піксельний шейдер лише малювати пікселі у фокусі точки зору світла, після обчислення тіней ультрафіолетової карти просто перевірте, чи вони між 0 і 1, а discardякщо ні (або встановіть колір світла на нуль). Інший спосіб - встановити текстуру тіньової карти в режимі "затискання до кордону" та встановити нульовий колір межі, що забезпечить повне затінення все, що знаходиться поза тіньовою картою.


Ось крупний план поля та порівняння із посиланням на тіньове відображення та без нього . Тут помітніше самотінення. Чи можете ви пояснити, чому б ви використовували перспективну камеру для точкових вогнів та ортографічну для спрямованих?
Grieverheart

Гаразд, на цьому знімку це схоже на тіньові прищі. Намалювання заднього обличчя на тіньовій карті (а не передніх граней) повинно це виправити. Я знаю, ти сказав, що ти спробував це, але, можливо, щось пішло не так? Це просто glCullFace(GL_FRONT). Що стосується точки проти спрямованості, то тут все стосується променів. Промені камери сходяться до точки в перспективній камері, а світлові промені сходяться до точки в точковому світлі; промені камери паралельні для ортографічної камери, а світлові промені паралельні для спрямованого світла.
Натан Рід

Я бачу, це має сенс. Ось ще одне порівняння з відсіканням обличчя та без нього. У нижній частині ввімкнено відмежування обличчя. Я спробую використати ортографічну проекцію для своєї матриці світлого перегляду, яка не повинна впливати на проблему самозатінення, правда? Можливо, діапазон кліпів занадто великий (тобто у мене від 0,1 до 100)?
Grieverheart

Вони виглядають точно так само. Ти використовуєш зворотне обличчя для решти сцени, правда? Ви робили glEnable(GL_CULL_FACE)?
Натан Рід

Вони не зовсім однакові, уважно придивіться до коробки. На малюнку вище тіньова лінія випукла, а лінія внизу увігнута.
Grieverheart

3

Пікселі поза зоною світла темні, тому що їх відбирають за межами текстури глибини світла.

Перетворюючи вершину за допомогою матриці проекції, ви отримуєте координати кліпу [lightspace_Position] фрагмента. Потім ви перетворюєте це в координатні текстури [UVCoords]. Однак координати простору кліпу фрагмента все ще можуть бути за межами діапазону від -1,0 до 1,0 (поза екраном і, отже, відрізаними), і тому перетворені координати текстури можуть бути поза діапазоном від 0,0 до 1,0 вашої текстури.

Спробуйте це:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Ви також можете помножити матрицю зміщення у [lightMVP] і уникати перетворення координат у вашому шейдері фрагмента.


Матриця зміщення зазвичай є дорогою, якою йдуть люди. Це набагато швидше, ніж розгалуження коду в фрагменті шейдера.
Ян Янг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.