простой процедурный скайбокс - PullRequest
5 голосов
/ 26 марта 2012

Как часть попытки создать очень простое небо, я создал скайбокс (в основном куб, идущий от (-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 каждого фрагмента будет выглядеть очень плохо: тот факт, что вы смотрите на коробку, а не на купол, сразу становится ясным, как показано здесь:

wrong skybox

Обратите внимание на хорошо видимые «углы» в верхнем левом и правом верхнем углу окна.

Инстинктивно я думал, что очевидным решением было бы нормализовать положение каждого фрагмента, чтобы получить положение на единичной сфере, а затем взять координату 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);
}

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

После некоторой случайной игры (программирование культа грузов, я знаю: /) я понял, что это работает:

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

Разница лишь в том, что я нормализую положение без его W-компонента.

И вот рабочий результат: (на скриншотах разница невелика, но в движении довольно заметна) correct skybox

Итак, наконец, мой вопрос: почему это работает, когда предыдущая версия дает сбой? Должно быть, я неправильно понимаю что-то чрезвычайно важное в однородных координатах, но мой мозг просто не щелкает прямо сейчас!

1 Ответ

3 голосов
/ 26 марта 2012

GLSL normalize не обрабатывает однородные координаты как таковые.Он интерпретирует координату как принадлежащую R^4.Это вообще не то, что вы хотите.Однако, если vtx_data.worldspace_position.w == 0, то нормализация должна дать тот же результат.

Я не знаю, что означает vec3 pointOnSphere = normalize(vtx_data.worldspace_position);, потому что левая сторона также должна иметь тип vec4.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...