Войти
ПрограммированиеФорумГрафика

[GLSL] Помогите доделать перевод координат в мировые во фрагментном шейдере

#0
(Правка: 11:50) 11:20, 26 июня 2021

Есть задача - ограничить отображение квада с помощью кастомного фрустума...т.е. выполнить его клипирование с помощью 4х плоскостей (ну или хотя бы 2х)

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

За основу взял метод unproject Vector3 из своего фреймворка:
Проверить пока решил на одной плоскости.

vec4 getWorldCoords() {
    vec2 windowSize = vec2(640, 480); // temporaly
    vec3 screenCoords;

    screenCoords.x = (2.0 * gl_FragCoord.x) / windowSize.x - 1.0;
    screenCoords.y = (2.0 * gl_FragCoord.y) / windowSize.y - 1.0;
    screenCoords.z = 2.0 * gl_FragCoord.z - 1.0;
    vec4 worldCoords = u_invProjectionView * vec4(screenCoords.xyz, 1.0);
    worldCoords.xyz /= worldCoords.w;
    worldCoords.xyz *= vec3(512.0, 512.0, 8192.0); // BuildEngine coords scale
    return worldCoords;
}

void main() {  
    if(dot(plane, getWorldCoords()) < 0.0)
        discard;
...
}

Решил сделать проверку, передав в шейдер плоскость камеры - ожидав увидеть неклипированную картинку, но в итоге увидел такое:

scr-portren1.map-0001 | [GLSL] Помогите доделать перевод координат в мировые во фрагментном шейдере

Порыскав в интернете, заметил, что screenCoords.z кто-то пишет 0, кто-то пишет 1, кто-то пишет формулу с учетом глубины пикселя, но ни один из предложенных способов неправильный.

Во-первых непонятно, почему метод, который работает на CPU (в отношении screenCoords.z) не работает на шейдере?

Ну и лучшее преобразование у меня получилось при screenCoords.z = 1, тогда картинка приобретает вот так вид:

scr-portren1.map-0000 | [GLSL] Помогите доделать перевод координат в мировые во фрагментном шейдере

и она обрезается то больше, то меньше в зависимости от поворота камеры, часто даже работает как надо, но иногда плоскость клипирования залазит на само изображение, что конечно же неправильно.

И соотвесттвенно если применять такой глючный метод к фрустумам порталов, тогда получается что-то типа этого:

scr-portren1.map-0002 | [GLSL] Помогите доделать перевод координат в мировые во фрагментном шейдере


Может у меня где-то ошибка?
Или может быть эту задачу можно решить другим способом? Но если что использую GL20, методы для GL30 предпочитаю не рассматривать


#1
(Правка: 11:30) 11:30, 26 июня 2021

Может это оно ?

vec2 windowSize = vec2(640, 480); 

и
worldCoords.xyz *= vec3(512.0, 512.0, 8192.0);

#2
(Правка: 11:45) 11:45, 26 июня 2021

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

По поводу последней строки - я знал, что к ней начнут придераться (не в первый раз уже), поэтому оставил комментарий - в общем нет, это строка далеко не лишняя

#3
(Правка: 11:56) 11:53, 26 июня 2021

Хорошо.
Тогда что это plane ?

if(dot(plane, getWorldCoords()) < 0.0)
И ты в курсе, что в твоем случае, dot лучше делать с vec3 , а не с vec4  ?
А твоя getWorldCoords возвращает vec4.

#4
(Правка: 12:00) 11:59, 26 июня 2021

Fedorovich
Я пришел к выводу что неважно какой dot брать
dot это x * plane.normal.x + y * plane.normal.y + z * plane.normal.z + w * plane.d

Т.е. чувствуешь? Неважно что я напишу, dot(vec3 , vec3) + plane.d или просто напишу dot(vec4, vec4) потому что w == 1 и результат будет один и тот же. Или есть другая причина?

Plane это плоскость, в данном случае левая плоскость отсечения фрустума камеры, в шейдер передаю туда normal.xyz и dist именно в такой последовательности.

texshader.setUniformMatrix("u_invProjectionView", cam.invProjectionView);
Plane p = cam.frustum.planes[2];
texshader.setUniformf("plane", p.normal.x, p.normal.y, p.normal.z, p.d);

Ошибки в плоскости быть не может, т.к. эта же плоскость работает в OcclusionCulling'е до отрисовки, ну и соответственно не вижу причины не работать этой же плоскости ниже по коду

#5
(Правка: 12:04) 12:03, 26 июня 2021
  потому что w == 1 и результат будет один и тот же. 

По этому поводу(w всегда == 1), есть другое мнение.
#6
(Правка: 12:08) 12:07, 26 июня 2021

Вообще да, твоя наводка помогла, благодарю :)

Вставил в код

vec4 worldCoords = u_invProjectionView * screenCoords;
    worldCoords.xyz /= worldCoords.w;
    worldCoords.xyz *= vec3(512.0, 512.0, 8192.0); // BuildEngine coords scale
    worldCoords.w = 1.0;
    return worldCoords;

"Плавать" плоскость перестала, надо немного погонять тесты, при первом взгляде все хорошо (ну разве что 1 пиксель там все-таки проскакивает, но это уже хотя бы не на пол-экрана)

По этому поводу(w всегда == 1), есть другое мнение.

Это какое?)) Теория говорит, что W == 0 это направление, W == 1 - положение. Другого не дано
#7
(Правка: 13:00) 12:46, 26 июня 2021

если ты координаты worldCoords.xyz /= worldCoords.w; делишь на w то и координаты плейна тоже должны быть делёные на w вообще чтобы небыло деления на 0 там подругому пишут да w = z так и z+=1.0f;

#8
13:51, 26 июня 2021

Ну в любом случае, проблема решена - ошибка найдена, все работает теперь так как и должно

ПрограммированиеФорумГрафика