И так. Можно и эдак.
![](https://habrastorage.org/getpro/habr/upload_files/8cd/191/940/8cd191940b068f93c7ace20e64bcc625.png)
Но лучше все по порядку.
В далёкий 1993 год Raven Software решили лицензировать у ID Software движок id Tech 1.
![](https://habrastorage.org/getpro/habr/upload_files/c95/c67/1f1/c95c671f1bc5795364bea331b36c9d77.gif)
Поскольку работали на время - часть спрайтов была отрендерена с дополнительной дорисовкой поверх отрендереных изображений. В большинстве спрайтов это не так заметно, но вот в некоторых - очень даже.
Решил попробовать свои силы в этом деле.
Для начала была скручена моделька
![](https://habrastorage.org/getpro/habr/upload_files/192/721/e17/192721e1770d77f11487029780076ac4.png)
![](https://habrastorage.org/getpro/habr/upload_files/be7/156/66d/be715666daa00ae44190c1ee35342daa.png)
Делалось это дело для замены старой, и довольно корявой версии пушки demon tech repeater для мода Complex Doom Invasion. В скриншотах одни из первых итерации, до того как были добавлены последние элементы.
Первые рендеры напрямую из Блендера вышли размытыми, понятное дело antialiasing делает своё дело.
Поскольку ранее я уже кодил рендеринг спрайтов на Unity - было принято решение работать дальше в нём.
Первые рендеры из Unity просто с размытием и даже с уменьшением через Lancoz фильтр все равно были довольно уродливы:
![](https://habrastorage.org/getpro/habr/upload_files/335/30a/a03/33530aa030a4a46e6fe97cfd00b572fd.png)
![](https://habrastorage.org/getpro/habr/upload_files/c1c/c17/bc0/c1cc17bc07c5dba975dbd1de08770ae8.png)
Пришлось прибегнуть вдобавок к использованию Питона (Пайтона, как вам угодно) и дописать скрипты для уменьшения разрешения (код тут).
![](https://habrastorage.org/getpro/habr/upload_files/6c8/eee/3b3/6c8eee3b3007e775716fd51c18be8237.png)
Далее вдобавок был отрендерен outline. Работает так - берём legacy image effects из assetstore'а, и подставляем edge detection с режимом triangle depth normals. Не забываем уменьшить. В данном случае для уменьшения как раз таки использовался lancoz.
![](https://habrastorage.org/getpro/habr/upload_files/3b8/187/cf8/3b8187cf850030dee46bf3ac4cdb44df.png)
Наложил одно на другое (все через скрипты):
![](https://habrastorage.org/getpro/habr/upload_files/489/133/c02/489133c028fde6d0b3107dc9c49590d8.png)
Чуть чуть дорисовал, добавил 50% постеризацию (не путать с пАстеризацией - на Русском термин называется Изогелия https://ru.wikipedia.org/wiki/Изогелия ) со смешиванием (как эффект камеры) а также SSAO для теней.
Код шейдера постеризации и код скрипта для его применения поверх камеры:
Shader "Hidden/LightPosterize"
{
//стандартная рыба CG шейдера, мы только добавляем
//дополнительные переменные для силы Изогелии и силы смешивания
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Precision ("Precision", Range(0.001, 0.1)) = 0.15
_Interp("Interp", Range(0,1)) = 1
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
//подхватываем карту глубин-нормалей
sampler2D _CameraDepthNormalsTexture;
float _Precision, _Interp;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float3 normalValues;
float depthValue;
DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), depthValue, normalValues);
//обрезаем эффект если слишком глубоко - позволяет избавиться от артефактов
if (depthValue > 0.99) return col;
//собственно рабочий код - постеризуем
float3 upscaled = col.rgb / _Precision + float3(0.5, 0.5, 0.5)*_Precision;
float3 final = round( upscaled )* _Precision;
col.rgb = lerp(col.rgb, final, _Interp);
return col;
}
ENDCG
}
}
}
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class ShaderApply : MonoBehaviour
{
public Material mat;
public DepthTextureMode mode;
Camera cam;
//как обычно для эффектов которые накладываются на стандарную камеру -
//подсасываемся через OnRenderImage
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
cam = GetComponent<Camera>();
//если материала на скрипте нет - прогоняем картинку дальше
if (mat == null)
{
Graphics.Blit(source, destination);
return;
}
//если есть - берем что у нас сейчас в source rendertexture'е и прогоняем через наш шейдер
cam.depthTextureMode = mode;
//mat is the material containing your shader
Graphics.Blit(source, destination, mat);
}
}
Получилось вот так:
![](https://habrastorage.org/getpro/habr/upload_files/0b5/128/bd5/0b5128bd502ab1be06bd508074b71d41.png)
Ну и вдобавок как все это выглядит когда вся анимация отрендеренна и прогнана через скрипты:
И теперь алгоритм по порядку:
![](https://habrastorage.org/getpro/habr/upload_files/3ec/e89/a67/3ece89a67a9c6503fc7ff9a433f659b1.png)
![](https://habrastorage.org/getpro/habr/upload_files/07b/781/bf9/07b781bf9fa82235fd3dace7efcd5c10.png)
Рендерим в разрешении 640x400 с повышенной резкостью, SSAO и постеризацией.
Рендерим эффект плазмы. В данном случае я рендерю на чёрном фоне без альфа канала, перекидываю в Питон, создаю альфа канал из чёрного цвета и нормализую цвет - таким образом избегая colorbanding
Рендерим outline в разрешении 1280x800 отдельно, перекидываю тоже в питон. Скрипт уменьшает спрайт аутлайна, множит основной спрайт на аутлайн (50% помножение), затем скрипт закидывает получившийся спрайт поверх эффекта плазмы, если тот присутствует в ряду кадров.
Готово. Надеюсь статья была полезной.