Играю тут в НС 2010, там есть прозрачный аномальный бизон, которым вооружаются враги в невидимых экзоскелетах. Симпатичная штуковина. Однако если эту пушку взять в руки, то вместо прозрачного бизон станет просто блестящим, с текстурой воды.
Дело в том что если просто назначить прозрачные шейдеры худовой модели оружия, то модель сильно уедет вперёд.
Это связано с тем, что все худовые модели в сталкере рисуются с уменьшенным, в два раза примерно, углом обзора, для того чтобы нивелировать эффект рыбьего газа, который возникает если рисовать худ с полным углом обзора, что можно лицезреть в таких играх как крайзис и сурвариум, и это одна из причин почему я считаю их отстоем. Однако с прозрачными шейдерами уменьшение угла обзора почему-то не срабатывает, и получается то что можно видеть на скрине выше.
Чтобы это пофиксить нужно создать специальный костыльный шейдер прозрачности для худа, который рисует всё с уменьшенным углом обзора. В модели аномального бизона для прозрачности используется шейдер models\pautina, на его основе и будем делать. Поэтому лезем в gamedata\shaders\r1 и находим там файл models_pautina.s. Если его открыть можно увидеть такой код:
function normal (shader, t_base, t_second, t_detail)
shader:begin ("model_distort4ghost","particle") -- particle_alphaonly
: sorting (3, true)
: blend (true,blend.srccolor,blend.invsrcalpha)
: aref (true,0)
: zb (true,false)
: fog (false)
: distort (false)
shader:sampler ("s_base") :texture (t_base)
end
function l_special (shader, t_base, t_second, t_detail)
shader:begin ("model_distort4ghost","particle_distort")
: sorting (3, true)
: blend (true,blend.srcalpha,blend.invsrcalpha)
: zb (true,false)
: fog (false)
: distort (true)
shader:sampler ("s_base") :texture (t_base)
shader:sampler ("s_distort") :texture ("pfx\\pfx_dist_glass") //:texture (t_base) -- ("pfx\\pfx_dist_glass2"
end
function normal (shader, t_base, t_second, t_detail)
shader:begin ("model_def_lplanes","base_lplanes")
: fog (false)
: zb (true,false)
: blend (true,blend.srccolor,blend.one)
: aref (true,0)
: sorting (2, true)
shader:sampler ("s_base") :texture (t_base)
end
Во первых, тут у нас две функции normal, что конечно является ошибкой разработчиков в самом деле используется только одна, вторая, поэтому первую лучше удалить чтобы не путаться. НО это нам с прозрачностью на худе не поможет. Дальше нужно поменять имя вершинного шейдера, которое идёт первым параметром функции shader:begin, ведь именно там применяются все транформации, в том числе и угол обзора. Я заменил model_distort4ghost и model_def_lplanes на model_distort4ghost_hud, в итоге получился такой файл:
function l_special (shader, t_base, t_second, t_detail)
shader:begin ("model_distort4ghost_hud","particle_distort")
: sorting (3, true)
: blend (true,blend.srcalpha,blend.invsrcalpha)
: zb (true,false)
: fog (false)
: distort (true)
shader:sampler ("s_base") :texture (t_base)
shader:sampler ("s_distort") :texture ("pfx\\pfx_dist_glass") //:texture (t_base) -- ("pfx\\pfx_dist_glass2"
end
function normal (shader, t_base, t_second, t_detail)
shader:begin ("model_distort4ghost_hud","base_lplanes")
: fog (false)
: zb (true,false)
: blend (true,blend.srccolor,blend.one)
: aref (true,0)
: sorting (2, true)
shader:sampler ("s_base") :texture (t_base)
end
Сохраняю его под именем gamedata\shaders\r1\models_pauhuda.s, имя нового шейдера прозрачности для худа будет models\pauhuda. Обратите внимание что в оригинале использовалось два разных вершинных шейдера (model_def_lplanes и model_distort4ghost), но я буду использовать только один, т.к. они всё-ровно не сильно отличаются.
Дальше самое интересное, нужно создать вершинный шейдер. Делать его будем на основе model_distort4ghost, поэтому копируем model_distort4ghost.vs и переименовываем его как model_distort4ghost_hud.vs, и смотрим что у него внутри:
#include "common.h"
#include "skin.h"
struct vf
{
float4 hpos : POSITION;
float2 tc0 : TEXCOORD0; // base
float4 c0 : COLOR0; // color
};
vf _main (v_model v)
{
vf o;
o.hpos = mul (m_WVP, v.pos); // xform, input in world coords
o.tc0 = v.tc.xy; // copy tc
// calculate fade
float3 dir_v = normalize (mul(m_WV,v.pos));
float3 norm_v = normalize (mul(m_WV,v.norm));
float fade = 0.9*abs (dot(dir_v,norm_v));
o.c0 = fade;
return o;
}
/////////////////////////////////////////////////////////////////////////
#ifdef SKIN_NONE
vf main(v_model v) { return _main(v); }
#endif
#ifdef SKIN_0
vf main(v_model_skinned_0 v) { return _main(skinning_0(v)); }
#endif
#ifdef SKIN_1
vf main(v_model_skinned_1 v) { return _main(skinning_1(v)); }
#endif
#ifdef SKIN_2
vf main(v_model_skinned_2 v) { return _main(skinning_2(v)); }
#endif
И где же здесь применяется угол обзора? А вот в этой строчке:
o.hpos = mul (m_WVP, v.pos); // xform, input in world coords
Тут нужно немного пояснить что к чему. Угол обзора заключен в матрице m_WVP, которая на самом деле является комбинацией трёх основных матриц m_W, m_V, и m_P. Так же в шейдерах доступны и другие комбинации этих матриц, m_WV к примеру. Буквы W, V, P это первые буквы от слов World, View, Projection. Не буду сильно вдаваться в подробности того как это работает и для чего нужно, скажу только что угол обзора находится в m_P. Если взглянуть на функцию создания такой матрицы, то можно узнать что:
a) для конструирования матрицы на нужен угол обзора, соотношение сторон екрана, а так же дистанция ближней и дальней отсекающих плоскостей
б) угол обзора и соотношение сторон можно получить обратно из готовой матрицы путём нехитрым математических вычислений ( отношение сторон как m_P[1][1] / m_P[0][0], а угол как atan( 1.0 / m_P[1][1] ) )
С этими знаниями уже можно выцарапать угол и соотношение сторон из готовой матрицы m_P, поделить угол на два и сконструировать новую матрицу m_P_fix с исправленным углом обзора, а плоскости отсечения можно не трогать. Чтож, меньше слов, больше дела. Модифицируем наш вершинный шейдер model_distort4ghost_hud таким образом:
#include "common.h"
#include "skin.h"
struct vf
{
float4 hpos : POSITION;
float2 tc0 : TEXCOORD0; // base
float4 c0 : COLOR0; // color
};
vf _main (v_model v)
{
vf o;
float hud_fov = 0.45;
float aspect_ratio = m_P[1][1] / m_P[0][0];
float half_fovy = atan( 1.0 / m_P[1][1] );
float4x4 m_P_fix = m_P;
m_P_fix[1][1] = 1.0 / tan(half_fovy * hud_fov);
m_P_fix[0][0] = m_P_fix[1][1] / aspect_ratio;
float3 view_pos = mul(m_WV, v.pos);
o.hpos = mul (m_P_fix, float4(view_pos, 1)); // xform, input in world coords
o.tc0 = v.tc.xy; // copy tc
// calculate fade
float3 dir_v = normalize (mul(m_WV,v.pos));
float3 norm_v = normalize (mul(m_WV,v.norm));
float fade = 0.9*abs (dot(dir_v,norm_v));
o.c0 = fade;
return o;
}
/////////////////////////////////////////////////////////////////////////
#ifdef SKIN_NONE
vf main(v_model v) { return _main(v); }
#endif
#ifdef SKIN_0
vf main(v_model_skinned_0 v) { return _main(skinning_0(v)); }
#endif
#ifdef SKIN_1
vf main(v_model_skinned_1 v) { return _main(skinning_1(v)); }
#endif
#ifdef SKIN_2
vf main(v_model_skinned_2 v) { return _main(skinning_2(v)); }
#endif
Тут конструируется матрица m_P_fix с уменьшенным до 45% углом обзора, потом точка из модели (v.pos) трансформируется сначала стандартной матрицей m_WV (комбинацией m_W и m_V), а уже потом исправленной матрицей m_P_fix. Строго говоря значение переменной hud_fov (0.45) должно быть равным значению одноимённой консольной команды hud_fov. В финальной версии игры эта команда заблокирована, а значение константно и никогда не меняется. Впрочем, всякие движковые моды типа FOV switcher могут его менять.
Ну всё, шейдер готов, осталось только указать его в модели. Лезем в геймдату, открываем gamedata\meshes\weapons\bizo1\wpn_bizon_hud1.ogf через СДК, или по старинке через хекс-редактор, меняем все шейдеры models\pautina на models\pauhuda, а потом смотрим на результат:
А вот так эта модель выглядит с родными текстурами оружия, вместо текстуры воды:
Короче вот готовый пример прозрачного шейдера для худа + модель аномального бизона которая его использует (для Народной Солянки 2010): https://drive.google.com/file/d/1W4R28ON2x8z1u_xlw2ACOdfi-9YEYGra/view?usp=sharing
А задачу создания такого шейдера для R2 рендера я оставляю вам в качестве домашнего задания, гегеге.