HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- OpenGL (https://hlfx.ru/forum/forumdisplay.php?forumid=7)
-- Эффект преломления как в Half-Life2 (https://hlfx.ru/forum/showthread.php?threadid=19)
Отправлено XaeroX 22-10-2005 в 08:37:
Эффект преломления как в Half-Life2
Эффект преломления
Преломление во многих разработках реализуется в вершинной программе на основе вектора нормали к данной вершине. Однако для этого требуется высокополигональная модель, иначе вершин (а значит, и нормалей) будет недостаточно для правильного преломления.
Сейчас повсеместно используются карты нормалей для детализации поверхностей. Недавно я приводил пример, как сделать высокодетализированный эффект хромированной поверхности с их помощью. Теперь рассмотрим эффект преломления.
Вспомните ребристые дверные стекла в Half-Life 2. Искажение мира за ними выглядит очень правдоподобно. Мы сделаем что-то очень похожее.
Для начала нам нужно захватить изображение мира в текстуру. Для этого я сделал один проход, выводящий изображение в квадрат 256 х 256. В принципе, можно было этого не делать и использовать расширение GL_NV_texture_rectangle, при этом нужно было бы изменить матрицу текстуры и заменить функции обращения к текстуре в шейдере.
Итак, мы отрендерили объекты мира в текстуру. Теперь нужно настроить матрицу текстуры для проецирования обычным способом и подключить вершинную и фрагментную программу.
Сама идея преломления заключается в следующем. Мы передаем нормаль к поверхности во фрагментную программу. Далее в ней мы получаем попиксельно нормали из карты нормалей и вычисляем их скалярное произведение. После этого мы просто умножаем координаты проекции на полученное значение. В случае, если обе нормали совпали (dot=1), сцена не искажается, иначе – искажается тем сильнее, чем больше отличие нормалей.
Вершинная программа делает вот что:
1. Вычисляет проекцию вершины.
2. Вычисляет координаты проекции для вершины.
3. Передает нормаль как координаты текстуры юнита 2.
4. Передает координаты текстуры юнита 0 и текущий цвет.
Cg Vertex Shader:
4 | float3 normal : NORMAL; |
5 | float2 tex0 : TEXCOORD0; |
11 | float4 pos : POSITION; |
12 | float2 tex0 : TEXCOORD0; |
13 | float4 tex1 : TEXCOORD1; |
14 | float3 norm : TEXCOORD2; |
18 | VertOut main(VertIn vin) |
21 | vout.pos = mul(glstate.matrix.mvp, vin.pos); |
23 | vout.tex1 = mul(glstate.matrix.texture[1], vin.pos); |
24 | vout.norm = vin.normal; |
25 | vout.color = vin.color; |
Переходим к фрагментной программе. Для начала получаем нормаль и приводим ее к диапазону [-1,1], после чего вычисляем координаты преломления, как описано выше. W-компонент не трогаем, он используется для проецирования. Получаем цвета текстуры и проекции и усредняем их, домножив на текущий цвет.
Cg Pixel Shader:
4 | float2 tex0 : TEXCOORD0; |
5 | float4 tex1 : TEXCOORD1; |
6 | float3 norm : TEXCOORD2; |
15 | FragOut main(VertOut fin, |
16 | uniform sampler2D diffuseMap :TEXUNIT0, |
17 | uniform sampler2D screenMap :TEXUNIT1, |
18 | uniform sampler2D normalMap :TEXUNIT2) |
22 | //get diffuse texture color |
23 | float4 diffuse = tex2D(diffuseMap, fin.tex0); |
25 | //get normal and expand it from [0,1] to [-1,1] |
26 | float3 normal = tex2D(normalMap, fin.tex0).xyz; |
27 | normal = normal*2 - 1; |
29 | //compute refraction texcoords using difference btw normals, and lookup screen texture |
31 | refraction.xyz = fin.tex1.xyz * dot(normal, fin.norm); |
32 | refraction.w = fin.tex1.w; |
33 | float4 screen = tex2Dproj(screenMap, refraction); |
35 | fout.col = fin.color * (diffuse + screen) * 0.5; |
В примере слева вы увидите проекцию без преломления (это именно проекция, а не полупрозрачная поверхность!). Справа – шейдерное преломление. Кликайте мышью для смены карты нормалей. Жду ваших отзывов 
P.S.: заранее отвечаю на вопрос, почему нет полных исходников. Я выложил код шейдеров, а это и есть само ноу-хау. Остается только их присоединить к проекту – это дело техники. Кроме того, этот эффект используется в Half-Life FX, а потому полные исходники не могут быть открыты до его релиза.
Для работы примера требуется поддержка вершинных и пиксельных шейдеров 2.0__________________
Отправлено XaeroX 23-10-2005 в 09:12:
Что-то народ неактивно комментирует 
Для тех, у кого вторые шейдеры не поддерживаются, вот пара скринов. Конечно, видеть это надо в динамике, но все же...


__________________
Отправлено Дядя Миша 24-10-2005 в 06:43:
XaeroX
Насколько я понимаю - рассчет преломления делается на основе самой текстурки и напрямую от нее зависит.
В хл2 рифленые стекла были вертикальными 
Хорошая штука 
Отправлено XaeroX 24-10-2005 в 07:04:
g-cont нарисуй "вертикальную" нормалмапу, в смысле полосы на ней вертикальные, и будет тебе щастье ;Р
__________________
Отправлено Security 24-10-2005 в 14:39:
Xaerox
Это для первой халвы???!!!
Отправлено Дядя Миша 24-10-2005 в 16:10:
XaeroX да я понимаю
Security да для первой.
Отправлено BUzer 25-10-2005 в 02:09:
Классно, только текстурку-бы получше подобрать..
Тутор, наверное, писался для таких дубов в этом деле, как я?
Можно уж было тогда по-понятнее.. Вот, например, фраза
"1. Вычисляет проекцию вершины.
2. Вычисляет координаты проекции для вершины."
очень жуткая
Я так понял, под первым подразумевается умножение на видовую матрицу, а под вторым - нахождение спроецированных текстурных координат для "экранной" текстуры?
Также не помешали-бы комментарии к переменным при описании структур - типа что в них будет храниться... По-русски 
И наконец, вечно мучивший меня вопрос - а что, из пиксельного шейдера нельзя узнать цвет пикселя в цветовом буфере экрана? Если да, то это просто кидалово какое-то. А как тогда делать прозрачный полигон? (я так понимаю, в твоем примере он не прозрачен)
Отправлено XaeroX 25-10-2005 в 07:10:
BUzer
1. да, ты понял совершенно верно 
2. гм... не люблю русские комментарии.
3. нельзя. зато прозначность можно задать умножением на fin.color.w - это и есть альфа фрагмента.
__________________
Отправлено BUzer 25-10-2005 в 10:16:
XaeroX
3. Не понял, умножением чего?.. Вот я хочу вывести полупрозрачный полигон с пикс шейдером. Мне надо будет испольховать glEnable(GL_BLEND), glBlendFunc..? И куда шейдер будет выдавать альфу?
Отправлено XaeroX 25-10-2005 в 10:20:
Цитата:
Автор XaeroX
fin.color.w - это и есть альфа фрагмента
Это в терминологии Cg. То есть это альфа входная (задаваемая через glColor4f). Альфу текстуры нужно учитывать самому. А обычная прозрачность будет выводиться так:
Cg Pixel Shader:
С учетом альфа-канала текстуры:
Cg Pixel Shader:
float4 diffuse = tex2D(diffuseMap, fin.tex0); |
fout.col.w = fin.col.w * diffuse.w; |
__________________
Отправлено BUzer 25-10-2005 в 11:17:
А контролировать процесс смешивания расчитанного в шейдере цвета с цветом на экране можно только через glBlendFunc, получается?..
Отправлено XaeroX 25-10-2005 в 17:02:
glBlendFunc, как я понимаю, учитывается частично - для цвета имеющегося фрагмента. А цвет фрагмента наложения рассчитывается целиком в шейдере.
__________________
Отправлено FiEctro 25-08-2007 в 13:51:
Знаю что тема устарела ... Но у меня ламерский вопрос : Реализовывать в хл это все точно также как и func_mirror ? И как сделать что бы вместо нормалей читалась обычная WAD текстура с началом к примеру ( * ) ?
__________________
У котёнка мокрый нос и гладенькая шерсть, у него забавный хвост и быстрых лапок шесть. Две задних, две средних и две передних лапы, такая многоножка получилася у папы.
Он ученый — папа мой — зверушек изучает, гуляет по помойкам, ловит крыс и чаек. Две крысы белокрылые и чайки две унылые покрытые пупырчатою кожей лягушат без пёрышек тоскуют и ускакать спешат.
А ещё есть муравей большой размером с гуся он пугает всех зверей, и я его боюся, когда он ковыляет на лапках на своих.
И в двери ударяет, и начинает стих: Я — муравей, воды налей! Не меньше ведра, напиться мне пора!
Отправлено XaeroX 25-08-2007 в 17:05:
-CJ- как найти текстуру в ваднике по имени - есть в коде ксаша (там просматривается texinfo). Ну и потом биндишь ее, что-то типа glBindTexture(GL_TEXTURE_2D, msurface->texinfo->texture->gl_index);
__________________
Отправлено FiEctro 26-08-2007 в 11:49:
XaeroX спасибо ! Но в ксаше ( water.cpp ) я неособо разобрался какой код отвечает за наложение эфекта преломления и отражения на текстуру ( % )
И еще я не особо понял смысл закоментированого в ( water.cpp ) кода и коментов