Преломление во многих разработках реализуется в вершинной программе на основе вектора нормали к данной вершине. Однако для этого требуется высокополигональная модель, иначе вершин (а значит, и нормалей) будет недостаточно для правильного преломления.
Сейчас повсеместно используются карты нормалей для детализации поверхностей. Недавно я приводил пример, как сделать высокодетализированный эффект хромированной поверхности с их помощью. Теперь рассмотрим эффект преломления.
Вспомните ребристые дверные стекла в Half-Life 2. Искажение мира за ними выглядит очень правдоподобно. Мы сделаем что-то очень похожее.
Для начала нам нужно захватить изображение мира в текстуру. Для этого я сделал один проход, выводящий изображение в квадрат 256 х 256. В принципе, можно было этого не делать и использовать расширение GL_NV_texture_rectangle, при этом нужно было бы изменить матрицу текстуры и заменить функции обращения к текстуре в шейдере.
Итак, мы отрендерили объекты мира в текстуру. Теперь нужно настроить матрицу текстуры для проецирования обычным способом и подключить вершинную и фрагментную программу.
Сама идея преломления заключается в следующем. Мы передаем нормаль к поверхности во фрагментную программу. Далее в ней мы получаем попиксельно нормали из карты нормалей и вычисляем их скалярное произведение. После этого мы просто умножаем координаты проекции на полученное значение. В случае, если обе нормали совпали (dot=1), сцена не искажается, иначе – искажается тем сильнее, чем больше отличие нормалей.
Вершинная программа делает вот что:
1. Вычисляет проекцию вершины.
2. Вычисляет координаты проекции для вершины.
3. Передает нормаль как координаты текстуры юнита 2.
4. Передает координаты текстуры юнита 0 и текущий цвет.
Переходим к фрагментной программе. Для начала получаем нормаль и приводим ее к диапазону [-1,1], после чего вычисляем координаты преломления, как описано выше. W-компонент не трогаем, он используется для проецирования. Получаем цвета текстуры и проекции и усредняем их, домножив на текущий цвет.
Cg Pixel Shader:
1
struct VertOut
2
{
3
float4 pos : POSITION;
4
float2 tex0 : TEXCOORD0;
5
float4 tex1 : TEXCOORD1;
6
float3 norm : TEXCOORD2;
7
float4 color : COLOR;
8
};
9
10
struct FragOut
11
{
12
float4 col : COLOR;
13
};
14
15
FragOut main(VertOut fin,
16
uniformsampler2D diffuseMap :TEXUNIT0,
17
uniformsampler2D screenMap :TEXUNIT1,
18
uniformsampler2D normalMap :TEXUNIT2)
19
{
20
FragOut fout;
21
22
//get diffuse texture color
23
float4 diffuse = tex2D(diffuseMap, fin.tex0);
24
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;
28
29
//compute refraction texcoords using difference btw normals, and lookup screen texture
В примере слева вы увидите проекцию без преломления (это именно проекция, а не полупрозрачная поверхность!). Справа – шейдерное преломление. Кликайте мышью для смены карты нормалей. Жду ваших отзывов
P.S.: заранее отвечаю на вопрос, почему нет полных исходников. Я выложил код шейдеров, а это и есть само ноу-хау. Остается только их присоединить к проекту – это дело техники. Кроме того, этот эффект используется в Half-Life FX, а потому полные исходники не могут быть открыты до его релиза.
Для работы примера требуется поддержка вершинных и пиксельных шейдеров 2.0
Вложение: refract.zip (330.4 кб)
Этот файл был скачан 1313 раз. XaeroX проверил это вложение на вирусы 03-11-2005 в 18:29
Что-то народ неактивно комментирует
Для тех, у кого вторые шейдеры не поддерживаются, вот пара скринов. Конечно, видеть это надо в динамике, но все же...
XaeroX
Насколько я понимаю - рассчет преломления делается на основе самой текстурки и напрямую от нее зависит.
В хл2 рифленые стекла были вертикальными
Хорошая штука
Тутор, наверное, писался для таких дубов в этом деле, как я? Можно уж было тогда по-понятнее.. Вот, например, фраза
"1. Вычисляет проекцию вершины.
2. Вычисляет координаты проекции для вершины."
очень жуткая Я так понял, под первым подразумевается умножение на видовую матрицу, а под вторым - нахождение спроецированных текстурных координат для "экранной" текстуры?
Также не помешали-бы комментарии к переменным при описании структур - типа что в них будет храниться... По-русски
И наконец, вечно мучивший меня вопрос - а что, из пиксельного шейдера нельзя узнать цвет пикселя в цветовом буфере экрана? Если да, то это просто кидалово какое-то. А как тогда делать прозрачный полигон? (я так понимаю, в твоем примере он не прозрачен)
BUzer
1. да, ты понял совершенно верно
2. гм... не люблю русские комментарии.
3. нельзя. зато прозначность можно задать умножением на fin.color.w - это и есть альфа фрагмента.
XaeroX
3. Не понял, умножением чего?.. Вот я хочу вывести полупрозрачный полигон с пикс шейдером. Мне надо будет испольховать glEnable(GL_BLEND), glBlendFunc..? И куда шейдер будет выдавать альфу?
Автор XaeroX fin.color.w - это и есть альфа фрагмента
Это в терминологии Cg. То есть это альфа входная (задаваемая через glColor4f). Альфу текстуры нужно учитывать самому. А обычная прозрачность будет выводиться так:
Знаю что тема устарела ... Но у меня ламерский вопрос : Реализовывать в хл это все точно также как и func_mirror ? И как сделать что бы вместо нормалей читалась обычная WAD текстура с началом к примеру ( * ) ?
__________________
У котёнка мокрый нос и гладенькая шерсть, у него забавный хвост и быстрых лапок шесть. Две задних, две средних и две передних лапы, такая многоножка получилася у папы.
Он ученый — папа мой — зверушек изучает, гуляет по помойкам, ловит крыс и чаек. Две крысы белокрылые и чайки две унылые покрытые пупырчатою кожей лягушат без пёрышек тоскуют и ускакать спешат.
А ещё есть муравей большой размером с гуся он пугает всех зверей, и я его боюся, когда он ковыляет на лапках на своих.
И в двери ударяет, и начинает стих: Я — муравей, воды налей! Не меньше ведра, напиться мне пора!
-CJ- как найти текстуру в ваднике по имени - есть в коде ксаша (там просматривается texinfo). Ну и потом биндишь ее, что-то типа glBindTexture(GL_TEXTURE_2D, msurface->texinfo->texture->gl_index);
XaeroX спасибо ! Но в ксаше ( water.cpp ) я неособо разобрался какой код отвечает за наложение эфекта преломления и отражения на текстуру ( % )
И еще я не особо понял смысл закоментированого в ( water.cpp ) кода и коментов
code:
/*
// This code should do the same on Gf4Ti (nv_texture_shader, nv_texture_shader2, nv_texture_shader3)
// BUT IT DOESN'T!!!!
//
// NB: if you wanna test it, do the following in gl_mirrors.cpp
// - operate the texture matrix for texture1
// - swap texcoords for units 0 and 1
void CreateWaterShader_ARB(void)
{
logPrint("Generating noise texture\n");
CreateNoiseTextureDsDt(1);
logPrint("Completed\n");
}
....
__________________
У котёнка мокрый нос и гладенькая шерсть, у него забавный хвост и быстрых лапок шесть. Две задних, две средних и две передних лапы, такая многоножка получилася у папы.
Он ученый — папа мой — зверушек изучает, гуляет по помойкам, ловит крыс и чаек. Две крысы белокрылые и чайки две унылые покрытые пупырчатою кожей лягушат без пёрышек тоскуют и ускакать спешат.
А ещё есть муравей большой размером с гуся он пугает всех зверей, и я его боюся, когда он ковыляет на лапках на своих.
И в двери ударяет, и начинает стих: Я — муравей, воды налей! Не меньше ведра, напиться мне пора!