Основы постпроцессов.

Введение.

Мы использовали шейдеры, которые мы изучали в этом учебнике, чтобы отображать модели на экране. Другой способ шейдеров обычно используется для манипуляции изображениями. Это включает изображение, которое мы рисуем на экране, когда мы делаем нашу игру. Манипуляции по обработке вывода рендеринга после того, как мы отобразили наши объекты на экран, называются постобработкой.
Постобработка по-прежнему использует тот же язык и структуру шейдера, что и шейдеры, которые визуализируют поверхности, поэтому я бы рекомендовал вам сначала знать, как визуализировать поверхности. Если вы прочитали / поняли мой урок по рендерингу текстур, вы поймете и этот урок.

Шейдер постобработки.

Как простое введение в постобработку, я покажу вам, как сделать шейдер, который инвертирует цвета изображения.
Поскольку большая часть структуры такая же, как и у других шейдеров, мы собираемся использовать шейдер текстур в качестве базы для нового.
У этого простого шейдера есть некоторые вещи, которые нам не нужны, если мы не будем рисовать поверхности. Мы собираемся их удалить. Я удаляю:
  • цвет оттенка (мы можем сохранить его, если хотим отенить изображение),
  • теги (Unity может читать, когда и как визуализировать объекты, но, как я уже упоминал, мы не рендерим объекты шейдером),
  • преобразование текстуры (maintex будет изображением до того, как мы применим шейдер к нему, т.к. мы всегда хотим обрабатывать всю сцену),
  • макрос transform tex (поскольку он используется для преобразование текстуры, и мы больше не используем его, но мы все еще хотим записать координаты uv в структуру v2f)
  • и ту часть, в которой используется цвет оттенка.
Затем мы добавим несколько деталей, которые помогут шейдеру лучше работать в качестве шейдера постобработки. Это скрытие тега инспектора для основного свойства текстуры, поскольку оно будет установлено из кода и маркеров, которые сообщают Unity не выполнять отбраковку или запись / чтение в буфер глубины.
После этих изменений шейдер должен выглядеть примерно так.
Shader "Tutorial/016_Postprocessing"{
    //показываем значения для редактирования в инспекторе
    Properties{
        [HideInInspector]_MainTex ("Texture", 2D) = "white" {}
    }

    SubShader{
        // Маркер, который указывает, что нам не нужен клиппинг 
        // и чтение / запись буфера глубины
        Cull Off
        ZWrite Off 
        ZTest Always

        Pass{
            CGPROGRAM
            //включаем полезные функции
            #include "UnityCG.cginc"

            //определяем вершинный и фрагментный шейдеры
            #pragma vertex vert
            #pragma fragment frag

            //текстура отрендеренного экрана
            sampler2D _MainTex;

            //объект, который приходит в вершинный шейдер
            struct appdata{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            //данные передаваемые в фрагментный шейдер
            struct v2f{
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            //вершинный шейдер
            v2f vert(appdata v){
                v2f o;
                //конвертируем позицию вершины из пространства объекта в пространство экрана
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            //фрагментный шейдер
            fixed4 frag(v2f i) : SV_TARGET{
                //цвет пикселя из текстуры
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }

            ENDCG
        }
    }
}

C# скрипт постобработки.

Теперь, когда у нас есть основа нашего шейдера постобработки, мы можем написать скрипт C#, который будет использовать камера.
Нам понадобится нормальный monobehaviour, только с одним методом OnRenderImage. Метод будет автоматически вызван Unity. Он получает два аргумента, один rendertexture с визуализированным изображением и один rendertexture, который мы можем записать, который затем используется в качестве отображаемого изображения. Чтобы перемещать данные изображения из одного объекта rendertexture в другой, мы используем blit-метод.
using UnityEngine;

//Скрипт будет на том же объекте, на котором главная камера
public class Postprocessing : MonoBehaviour {

 //метод, который автоматически вызывает Unity после того как камера сделала рендеринг
 void OnRenderImage(RenderTexture source, RenderTexture destination){
  //копирует пиксели их текстуры источника в текстуру получатель
  Graphics.Blit(source, destination);
 }
}
Пока этот скрипт ничего не делает, потому что он вообще не меняет изображение. Для того чтобы он что-тоделал, мы можем передать blit-функции материал, который будет использован для рисования выходной текстуры, в качестве третьего параметра. Мы добавим материал как сериализованную переменную класса, а затем передадим ее функции blit для этого.
using UnityEngine;

// Скрипт будет на том же объекте, на котором главная камера
public class Postprocessing : MonoBehaviour {
    //Материал, используемый для постобработки
    [SerializeField]
    private Material postprocessMaterial;

    // метод, который автоматически вызывает Unity после того как камера сделала рендеринг
    void OnRenderImage(RenderTexture source, RenderTexture destination){
        // копирует пиксели из текстуры источника в текстуру получатель
        Graphics.Blit(source, destination, postprocessMaterial);
    }
}
С помощью этой переменной мы можем настроить нашу сцену. Сначала мы добавляем новый материал в наш проект и применяем к нему шейдер постобработки.

Затем мы берем игровой объект с нашей камерой и навешиваем на него скрипт C#, который мы написали. Затем мы добавляем наш новый материал к компоненту.

Эффект обратных цветов.

После этого наша установка будет завершена, и мы должны увидеть обычное изображение. Чтобы инвертировать цвета нашего изображения, мы возвращаемся в наш шейдер и редактируем фрагментную функцию. Вместо того, чтобы просто возвращать цвет входной текстуры, мы сначала инвертируем цвет, вычисляя 1 минус цвет, а затем возвращаем его.
// фрагментный шейдер
fixed4 frag(v2f i) : SV_TARGET{
    // цвет пикселя из текстуры
    fixed4 col = tex2D(_MainTex, i.uv);
    //инвертируем цвет
    col = 1 - col;
    return col;
}

Инвертирование цвета, очевидно, не то, что вы часто хотите делать, но это открывает множество возможностей для будущих эффектов, некоторые из которых я покажу в ближайшие недели.
Shader "Tutorial/016_Postprocessing"{
    // показываем значения для редактирования в инспекторе
    Properties{
        [HideInInspector]_MainTex ("Texture", 2D) = "white" {}
    }

    SubShader{
          // Маркер, который указывает, что нам не нужен клиппинг 
          // и чтение / запись буфера глубины
        Cull Off
        ZWrite Off 
        ZTest Always

        Pass{
            CGPROGRAM
            // включаем полезные функции
            #include "UnityCG.cginc"

            // определяем вершинный и фрагментный шейдеры
            #pragma vertex vert
            #pragma fragment frag

            // текстура отрендеренного экрана
            sampler2D _MainTex;

            // объект, который приходит в вершинный шейдер
            struct appdata{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            // данные передаваемые в фрагментный шейдер
            struct v2f{
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            // вершинный шейдер
            v2f vert(appdata v){
                v2f o;
                // конвертируем позицию вершины из пространства объекта в пространство экрана
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            // фрагментный шейдер
            fixed4 frag(v2f i) : SV_TARGET{
                // цвет пикселя из текстуры
                fixed4 col = tex2D(_MainTex, i.uv);
                // инвертируем цвет
                col = 1 - col;
                return col;
            }

            ENDCG
        }
    }
}
using UnityEngine;

// Скрипт будет на том же объекте, на котором главная камера
public class Postprocessing : MonoBehaviour {
 // Материал, используемый для постобработки
 [SerializeField]
 private Material postprocessMaterial;

 // метод, который автоматически вызывает Unity после того как камера сделала рендеринг
 void OnRenderImage(RenderTexture source, RenderTexture destination){
  // копирует пиксели из текстуры источника в текстуру получатель
  Graphics.Blit(source, destination, postprocessMaterial);
 }
}
Надеюсь, вы научились делать простые постобработки в Unity и готовы сами делать их.

Перевод Беляев В.А. ака seaman

Комментариев нет:

Отправить комментарий