прост процедурен скайбокс

Като част от опит да генерирам много просто изглеждащо небе, създадох небесна кутия (по същество куб, преминаващ от (-1, -1, -1) до (1, 1, 1), който е начертан след всички моята геометрия и принуден да се върне назад чрез следния прост вертекс шейдър:

#version 330
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 normal;

out Data
{
    vec4 eyespace_position;
    vec4 eyespace_normal;
    vec4 worldspace_position;
    vec4 raw_position;
} vtx_data;

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

void main()
{
    mat4 view_without_translation = view;
    view_without_translation[3][0] = 0.0f;
    view_without_translation[3][1] = 0.0f;
    view_without_translation[3][2] = 0.0f;

    vtx_data.raw_position = position;
    vtx_data.worldspace_position = model * position;
    vtx_data.eyespace_position =  view_without_translation * vtx_data.worldspace_position;

    gl_Position = (projection * vtx_data.eyespace_position).xyww;
}

От тук се опитвам да покажа моето небе като много прост градиент от наситено синьо в горната част до по-светло синьо на хоризонта.

Очевидно простото смесване на моите два цвята въз основа на Y координатата на всеки фрагмент ще изглежда много зле: фактът, че гледате кутия, а не купол, е веднага ясен, както се вижда тук:

грешен скайбокс

Обърнете внимание на доста видимите "ъгли" в горния ляв и горния десен ъгъл на кутията.

Инстинктивно си мислех, че очевидното решение ще бъде да се нормализира позицията на всеки фрагмент, да се получи позиция върху единична сфера, след което да се вземе Y координатата на това. Мислех, че това ще доведе до стойност, която ще бъде постоянна за дадена "надморска височина", ако това има смисъл. Като този :

#version 330
in Data
{
    vec4 eyespace_position;
    vec4 eyespace_normal;
    vec4 worldspace_position;
    vec4 raw_position;
} vtx_data;

out vec4 outputColor;

const vec4 skytop = vec4(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 skyhorizon = vec4(0.3294f, 0.92157f, 1.0f, 1.0f);

void main()
{  
    vec4 pointOnSphere = normalize(vtx_data.worldspace_position);
    float a = pointOnSphere.y;
    outputColor = mix(skyhorizon, skytop, a);
}

Резултатът обаче е почти същият като първата екранна снимка (мога да я публикувам, ако е необходимо, но тъй като визуално е подобна на първата, пропускам я, за да съкратя този въпрос в момента).

След известно произволно бъркане (програмиране на cargo cult, знам :/), разбрах, че това работи:

void main()
{  
    vec3 pointOnSphere = normalize(vtx_data.worldspace_position.xyz);
    float a = pointOnSphere.y;
    outputColor = mix(skyhorizon, skytop, a);
}

Единствената разлика е, че нормализирам позицията без нейния W компонент.

И ето работния резултат: (разликата е фина в екранните снимки, но доста забележима в движение) правилен skybox

И така, накрая, моят въпрос: защо това работи, когато предишната версия е неуспешна? Сигурно не съм разбрал нещо изключително основно за хомогенните координати, но мозъкът ми просто не щрака в момента!


person Nicolas Lefebvre    schedule 25.03.2012    source източник


Отговори (1)


GLSL normalize не обработва хомогенни координати сам по себе си. Той интерпретира координатата като принадлежаща на R^4. Като цяло това не е това, което искате. Въпреки това, ако vtx_data.worldspace_position.w == 0, тогава нормализирането трябва да доведе до същия резултат.

Не знам какво означава vec3 pointOnSphere = normalize(vtx_data.worldspace_position);, защото лявата страна също трябва да има тип vec4.

person Stefan Hanke    schedule 26.03.2012
comment
vec3 pointOnSphere = normalize(vtx_data.worldspace_position) е правописна грешка от моя страна, докато си играех с кода, за да генерирам екранните снимки за този въпрос :o Съжалявам! И благодаря за отговора! - person Nicolas Lefebvre; 26.03.2012