Потихоньку (ага, скромник какой нашелся) осваиваю Unity3d. Решил делиться знаниями.
Начиная с версии 3.0 был введен deferred render. Что это такое некоторые знают, некоторые слышали, а кто-то видит эти слова вместе в первый раз. Если кратко, то deferred render - это техника, позволяющая использовать много пиксельных источников света без существенного падения ФПС. Это достигается за счет того, что освещение просчитывается не во время рендера сцены, а после него - то есть только для тех пикселей, что попали на экран. Это довольно удобная техника, которая, к тому же, в unity хорошо дружит с припаянным Beast Lightmapper и обеспечивает красивое мягкое перетекание динамических теней в статические.
Недостатком deferred render-а является то, что он несовместим с хардварным АнтиАлиасингом (АА). Но беда не велика - это достаточно просто исправить, что я вам сейчас и покажу.
Для начала - принцип. Хардварный АА работает примерно так: для устранения "лесенок" для рассчета цвета пиксела используется значение не одного пиксела, а нескольких. То есть видеокарта просчитывает для одного пиксела рендер несколько раз со смещением, затем смешивает полученные цвета и выдает конечный результат. Получается весьма красиво и гладко.
Мы будем делать то же самое, но другими путями. Как? Очень просто:
- Наложим на камеру эффект размытия граней (есть в стардартной поставке имедж эффектов)
- Отрендерим мир в разрешении, превышающем экранное в некоторое число раз;
- Отрисуем отрендеренное уже в экранное разрешение (т.е. с уменьшением) и наложим GUI и HUD элементы.
Что нам для этого понадобится в сцене:
- Слой "GUI", в который мы поместим всё, что должно быть отрисовано после рендера мира;
- Главная камера с наложенным на неё эффектом "Edge blur" и скриптом "захвата" текстуры;
- Дополнительная камера с ортогональной проекцией, с Culling Mask, содержащей в себе только слой GUI, компонентом GUILayer, скриптом финального вывода картинки и параметром "Depth", большим чем у Главной камеры.
Скрипт, который надо вешать на Главную камеру (JavaScript) :
var BigRenderScale : float = 1.5;
static var big_render : RenderTexture;
function Start() {
// Создадим рендер-текстуру
big_render = RenderTexture(Screen.width*BigRenderScale, Screen.height*BigRenderScale, 24);
big_render.Create();
// И установим её
gameObject.camera.targetTexture = big_render;
}
Скрипт для Дополнительной камеры (JavaScript) :
private var mat : Material;
function Start() {
// Создадим материал с простейшим шейдером
mat = new Material("Shader \"Texture Simple\" {" +
" Properties { " +
" _MainTex (\"Base (RGB)\", 2D) = \"white\" " +
" } " +
" SubShader { " +
" Pass { " +
" SetTexture [_MainTex] " +
"}" +
"}" +
"}"
);
}
function OnPostRender() {
// Установим материалу текстуру из рендера Главной камеры
mat.SetTexture("_MainTex", AA_emul_grab.big_render);
// Прямоугольник с текстурными координатами
GL.PushMatrix();
mat.SetPass(0);
GL.Color(Color(1,1,1,1));
GL.LoadOrtho();
GL.Begin(GL.QUADS);
GL.TexCoord(Vector3(0,0,0));
GL.Vertex3(0,0,0);
GL.TexCoord(Vector3(0,1,0));
GL.Vertex3(0,1,0);
GL.TexCoord(Vector3(1,1,0));
GL.Vertex3(1,1,0);
GL.TexCoord(Vector3(1,0,0));
GL.Vertex3(1,0,0);
GL.End();
GL.PopMatrix();
}
Особенности:
Для наглядности рекомендую отключить на время компонент "Camera" дополнительной камеры и настроить Edge Blur визуально. Параметр "BigRenderScale" больше 2х делать ОЧЕНЬ не рекомендую. Помните, нагрузка на систему возрастает в квадратичной зависимости от этого параметра. Больше рендер - больше пикселей - меньше ФПС. Скорее всего оптимальным будет настроить "BigRenderScale"=1.3, и дальше "плясать" с Edge Blur параметрами.
Результат усилий - в аттаче.
P.S.
Кусочки скрипта, ответственные за GL-часть, нагло позаимствованны с соседнего ресурса. Но особой мыслительной ценности в них нет, так что копирайта они не достойны
P.P.S.
По этому же принципу возможны другие реализации, и скорее всего моя - не самая быстрая. Но она работает.