Наше комьюнити:   OpenGL Shader Lab  •  Half-Life FX  •  Форум  
OpenGL Shader Lab

Просмотр комментариев

Показаны комментарии 1-0 из 0

Возведение в степень в шейдерах 1.х 31-05-2009 09:11

    В этой небольшой статье я расскажу о некоторых способах возведения в высокую степень в шейдерных моделях, не поддерживающих эту функцию напрямую. Речь идет о шейдерах версии 1.х. Начиная с версии 2.0 (и тем более в GLSL) функция pow стала библиотечной, и никаких проблем наблюдаться не будет. Но как быть, если мы хотим, чтобы наш движок выдавал похожую картинку на разных шейдерных моделях? Если вас не заботит поддержка видеокарт ниже GeForce 6xxx и Radeon 9500, дальше можете не читать.
    А если вам все же хочется, чтобы хоть какой-то минимум спецэффектов работал на таких видеокартах, как Radeon 9200 и GeForce 3? И если вы хотите видеть удовлетворительную производительность на карточках GeForce FX, на которых шейдеры 2.0 хоть и поддерживаются, но работают несравненно медленнее, чем 1.х? Тогда стоит задуматься о правильном выборе модели бликового освещения. В частности, о степени, в которую будете возводить (N.H).
    Конечно, можно использовать высокую степень на шейдерной модели 2.0 и выше, и низкую – на 1.х. Однако минус такого подхода очевиден – картинка сильно различается, и тем сильнее, чем ниже значение степени. Посмотрим, в какую степень все же можно возвести в шейдерах 1.х без особых затрат.
Первое, что приходит на ум – последовательное возведение (т.н. successive squaring). Мы получаем значение (N.H), умножаем само на себя, потом еще раз само на себя, и т.д. Правило, которым мы руководствуемся, известно из школьного курса алгебры: «при перемножении степеней с одинаковыми основаниями показатели степеней складываются».
    Довольно легко реализовать возведение в 8 степень, используя Register Combiners.


Register Combiners:
!!RC1.0
//General combiner 1
//RGB: spare0 = N.H
{
    rgb {
        spare0 = expand(tex0) . expand(tex1);
    }
}
//General combiner 1
//RGB: spare0 = saturate(N.H)^2
{
    rgb {
        spare0 = unsigned(spare0) * unsigned(spare0);
    }
}
//Final combiner
//RGB: out = saturate(N.H)^8
final_product = spare0 * spare0;
out.rgb = final_product * final_product;
out.a = unsigned_invert(zero);



    Оставшиеся блоки можно использовать под другие нужды освещения (наложение текстуры, фонового освещения, диффузного освещения и т.д.). Однако обратите внимание – мы использовали финальный комбайнер для возведения в квадрат дважды. На практике – в нем нужно сделать как минимум еще одно умножение и, возможно, сложение. Тогда для возведения потребуется еще один или два блока General Combiner. А если мы хотим возвести в 16 степень – то всего потребуется не менее 5 блоков. Не очень удобно, а главное – не очень быстро.
На шейдерах 1.4 для Radeon (ATI_fragment_shader) возведение в 8 и 16 степень делается проще, т.к. в одном проходе шейдера разрешается делать 8 инструкций, и в принципе можно возводить даже в более высокие степени (например, 32). Более того, второй проход шейдера позволяет организовать обращение к текстуре, в которую закодирована функция любой степени (разумеется, точность 8-битная). А вот на GeForce3 Texture Shader такая возможность есть только на бумаге – на практике шейдер GL_DOT_PRODUCT_TEXTURE_2D_NV не позволяет ренормализовать вектор half-angle, а без этого результат может давать огромную ошибку. Да, шейдерная модель 1.х от NVidia кастрированная, это нужно признать, и больше всего проблем возникает именно с ней…
    К счастью, существуют формулы приближенного вычисления степени 8 и 16, которые хорошо ложатся на механизм Register Combiners. Вот эти формулы:

1) saturate(4*((N.H)-0.75))
2) saturate(4*((N.H)^2-0.75))
3) (saturate(2*((N.H)-0.5)))^4

Первая формула требует всего один General Combiner:


Register Combiners:
!!RC1.0
const0 = ( 0.75, 0.75, 0.75, 0.75 );
{
    rgb {
        discard = expand(tex0) . expand(tex1);
        discard = unsigned_invert(zero) * -const0;
        spare0 = sum();
        scale_by_four();
    }
}



Вторая – чуть сложнее, и потребует два General Combiner:


Register Combiners:
!!RC1.0
const0 = ( 0.75, 0.75, 0.75, 0.75 );
{
    rgb {
        spare0 = expand(tex0) . expand(tex1);
    }
}
{
    rgb {
        discard = unsigned(spare0) * unsigned(spare0);
        discard = unsigned_invert(zero) * -const0;
        spare0 = sum();
        scale_by_four();
    }
}



Последняя формула – самое лучшее приближенное вычисление 16 степени, однако потребуется возвести всего лишь в 4-ю методом successive squaring.


Register Combiners:
!!RC1.0
{
    rgb {
        spare0 = expand(tex0) . expand(tex1);
    }
}
{
    rgb {
        spare0 = unsigned(spare0) * unsigned(spare0);
        bias_by_negative_one_half_scale_by_two();
    }
}
{
    rgb {
        spare0 = unsigned(spare0) * unsigned(spare0);
    }
}
{
    rgb {
        spare0 = unsigned(spare0) * unsigned(spare0);
    }
}


    Последнее возведение можно сделать в Final Combiner (используя final_product), и тогда приближенное возведение в 16 степень будет стоить вам три General Combiner, где еще остается место для других вычислений.
    Какую формулу выбрать? Все зависит от того, какие ограничения накладывает на вас используемая модель освещения. На мой взгляд, оптимальна последняя формула. Она так же легко реализуется по механизму ATI_fragment_shader за один проход (всего 3 инструкции, главное не забывать про модификаторы 2X_BIT, BIAS_BIT и т.п.).
    А для наглядности – привожу график функций в сравнении с обычными степенными.



    Шейдеры 1.4 для Радеонов я не привожу, но писать их еще легче, чем Register Combiners, особенно если воспользоваться моей библиотекой AFSparse для чтения и обработки ассемблероподобного кода.
    Статью я написал потому, что не нашел в Интернете ничего подобного тогда, когда мне это понадобилось. Теперь такая статья есть.
    И напоследок: помните, что системные требования ваших движков должны соответствовать выдаваемой картинке. И если эту картинку можно нарисовать на шейдерах 1.х, то сделайте это.


Автор: XaeroX

Новости форума
Новые статьи
Вход в систему
Имя:
Пароль:
Помнить меня

[ вход ]
[ регистрация ]
[ забыли пароль? ]

Реклама
Текущие активные пользователи: 43
В данный момент на сайте 0 участников и 43 гостей.


Наш форум: В данный момент на форуме 122 посетителей.
Временная зона GMT. Текущее время 20:48.

На основе AcademCMS v1.3
Авторское право © 2001 - 2009, Chain Studios
Техническая площадка: HLFX.Ru