This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Контроль версий
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Расширение функционала редактора
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Player-ноды
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Плагины
Форматы файлов
Материалы и шейдеры
Rebuilding the Engine Tools
Интерфейс пользователя (GUI)
Двойная точность координат
API
Animations-Related Classes
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
VR-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы

Абстрактные материалы

Чтобы упростить программирование шейдеров и скрыть сложность реализации различных фич, были добавлены Абстрактные материалы. Подобно абстрактным классам в объектно-ориентированном программировании, они служат шаблоном определения определенных параметров и функций.

Сам абстрактный материал не может быть назначен объектам, он используется для наследования от него других абстрактных и базовых материалов, расширяя его функциональность, что дает вам большую гибкость.

Вы можете создать свой собственный абстрактный материал или использовать готовые Mesh / Mesh Transparent / Mesh Unlit / Decal с тесселяцией или без нее для создания собственного базового материала.

Примечание
Невозможно создать экземпляр абстрактного материала.

Наследование и переопределение#

Наследуя от абстрактного материала, вы можете переопределить любые значения и добавить некоторые другие компоненты к дочернему материалу:

ULON
AbstractMaterial A
{
		Slider t = 0.5
}

BaseMaterial B <parent = A>
{
		Slider t = 1.0
		Slider t2 = 2.0
}
Примечание
Абстрактные материалы не поддерживают множественное наследование.

Когда дочерний материал реализует одноименный шейдер, происходит слияние исходного кода родительского и дочернего шейдера (код родительского шейдера предшествует дочернему):

ULON
AbstractMaterial A
{
		Shader example = 
		#{
				// A_fragment
		}#
}

AbstractMaterial B <parent = A>
{
		Shader example = 
		#{
				// B_fragment
		}#
}

// the result example shader for the abstract material B will look like this: 
Shader example = 
#{
		// A_fragment
		// B_fragment
#}

Вы можете расширить и включить код другого шейдера, используя наследование и маркер #shader shader_name.

ULON
AbstractMaterial A
{
		// including the different shader code
		Shader a = 
		#{
				// shader code A
				#shader b
		}#

		Shader b = 
		#{
				// shader code B1
		}#
}

AbstractMaterial B <parent = A>
{
		// expanding the parent shader code
		Shader b = 
		#{
				// shader code B2
		}#
}

// the resulting example shader for the abstract material B will look like this: 
Shader a = 
#{
		// shader code A
		// shader code B1
		// shader code B2
#}

Те же правила применяются к Скрипт код (используйте #script script_name в качестве маркера).

Элементы в родительских и дочерних материалах Группы с тем же именем могут быть объединены. Обе группы должны иметь аргумент merge_group, установленный на true. Первая запись группы определяет место в пользовательском интерфейсе редактора, где расположены все элементы этой группы (родительский и дочерний):

ULON
AbstractMaterial A
{
		Group A1
		{
				Slider T1

				Group example <merge_group=true>
				{
					   Slider T2
				}
		}
}

BaseMaterial B <parent = A>
{
		Group B1
		{
				Slider T3

				Group example <merge_group=true>
				{
					   Slider T4
				}
		}
}

// the result grouping for the BaseMaterial B will look like this ("example" in the BaseMaterial B is merged with the parent example group): 
Group A1
{
		Slider T1

		Group example
		{
			   Slider T2
			   Slider T4
		}
}

Group B1
{
		Slider T3
}
Примечание
Группы в одном материале могут иметь одинаковые имена. Если для всех этих групп аргумент merge_group не установлен на true, они будут отображаться в пользовательском интерфейсе редактора как отдельные группы.

Сравнение с традиционной логикой базового материала#

Абстрактные материалы обобщают внутреннюю логику и обеспечивают упрощенный интерфейс ввода и вывода для производных базовых материалов. Традиционный подход включает в себя много повторяющейся работы по созданию материалов с разными оттенками для одних и тех же типов объектов. Поэтому рекомендуется использовать смесь абстрактного и базового материала. См. следующие примеры, которые помогают добиться такого же модифицированного эффекта SSAO.

Сочетание абстрактного и базового материала#

Этот пример демонстрирует, как объединяются абстрактный и базовый материалы.

my_abstract.abstmat

ULON
AbstractMaterial <preview_hidden=true var_prefix=var texture_prefix=tex>
{
	Texture2D color <source=procedural>
	
	Shader common=
	#{
	#}

	Shader base_shader=
	#{
		#include <core/materials/shaders/render/common.h>
		
		STRUCT_FRAG_BEGIN
			INIT_COLOR(float4)
		STRUCT_FRAG_END
		
		#shader common
		
		MAIN_FRAG_BEGIN(FRAGMENT_IN)
			#shader fragment
		MAIN_FRAG_END
	#}
	
	Pass post
	{
		Fragment=base_shader
	}
	
	Expression RENDER_CALLBACK_END_POST_MATERIALS=
	#{
		Texture source = engine.render.getTemporaryTexture(engine.render_state.getScreenColorTexture());
		
		source.copy(engine.render_state.getScreenColorTexture());
		setTexture("color", source);
		renderPassToTexture("post", engine.render_state.getScreenColorTexture());
		
		engine.render.releaseTemporaryTexture(source);
	#}
}

my_base.basemat

ULON
BaseMaterial <parent=my_abstract>
{
	Texture2D normal <source=gbuffer_normal>
	Texture2D scene_depth <source=current_depth>
	Texture2D modulation = "core/textures/common/checker_d.dds"
	Texture2D <source=ssao>

	Slider global_scale = 2

	Shader common=
	#{
		float3 triplanar_sample(float2 uv, float depth_in)
		{
			float3 normal_ws = mul3(s_imodelview, unpackGBufferNormal(TEXTURE(tex_normal, uv).rgb));
			float3 position_ws = s_camera_position + mul3(s_imodelview, nativeDepthToPositionVS(depth_in, uv));
			
			float4 texcoord = position_ws.xyyz * var_global_scale;
			float3 weight = triplanarWeightFast(normal_ws, 0.5f);
			
			return TEXTURE_TRIPLANAR(tex_modulation, texcoord, weight).rgb;
		}

		float4 final(float2 uv, float depth_in)
		{
			float4 base = TEXTURE(tex_color, uv);
			float ssao = TEXTURE(tex_ssao, uv).r;

			float3 effect = triplanar_sample(uv, depth_in);
			return float4(lerp(base.rgb * effect, base.rgb, ssao), base.a);
		}
	#}

	Shader fragment=
	#{
		float depth = TEXTURE(tex_scene_depth, IN_UV).r;
		if (depth == 0.0f || depth == 1.0f)
			discard;
		
		OUT_COLOR = final(IN_UV, depth);
	#}
}

Только основной материал#

Этот пример демонстрирует тот же вариант использования, что и над но с использованием только базового материала.

my_single_base.basemat

ULON
BaseMaterial <preview_hidden=true var_prefix=var texture_prefix=tex>
{
	Texture2D color <source=procedural>
	Texture2D normal <source=gbuffer_normal>
	Texture2D scene_depth <source=current_depth>
	Texture2D modulation = "core/textures/common/checker_d.dds"
	Texture2D <source=ssao>

	Slider global_scale = 2
	
	Shader base_shader=
	#{
		#include <core/materials/shaders/render/common.h>
		
		STRUCT_FRAG_BEGIN
			INIT_COLOR(float4)
		STRUCT_FRAG_END
		
		float3 triplanar_sample(float2 uv, float depth_in)
		{
			float3 normal_ws = mul3(s_imodelview, unpackGBufferNormal(TEXTURE(tex_normal, uv).rgb));
			float3 position_ws = s_camera_position + mul3(s_imodelview, nativeDepthToPositionVS(depth_in, uv));
			
			float4 texcoord = position_ws.xyyz * var_global_scale;
			float3 weight = triplanarWeightFast(normal_ws, 0.5f);
			
			return TEXTURE_TRIPLANAR(tex_modulation, texcoord, weight).rgb;
		}

		float4 final(float2 uv, float depth_in)
		{
			float4 base = TEXTURE(tex_color, uv);
			float ssao = TEXTURE(tex_ssao, uv).r;

			float3 effect = triplanar_sample(uv, depth_in);
			return float4(lerp(effect, base.rgb, ssao), base.a);
		}
		
		MAIN_FRAG_BEGIN(FRAGMENT_IN)
			float depth = TEXTURE(tex_scene_depth, IN_UV).r;
			if (depth == 0.0f || depth == 1.0f)
				discard;
			
			OUT_COLOR = final(IN_UV, depth);
		MAIN_FRAG_END
	#}
	
	Pass post
	{
		Fragment=base_shader
	}
	
	Expression RENDER_CALLBACK_END_POST_MATERIALS=
	#{
		Texture source = engine.render.getTemporaryTexture(engine.render_state.getScreenColorTexture());
		
		source.copy(engine.render_state.getScreenColorTexture());
		setTexture("color", source);
		renderPassToTexture("post", engine.render_state.getScreenColorTexture());
		
		engine.render.releaseTemporaryTexture(source);
	#}
}

Создание и использование собственного материала#

Давайте создадим материал с нуля. В этом примере мы возьмем базовый абстрактный материал для эффектов постобработки.

1. Создание родительского абстрактного материала#

Создайте новый текстовый файл с расширением *.abstmat. В качестве имени материала используется имя файла, назовем его my_abstract.

Абстрактный материал содержит все основные параметры, которые наследуются всеми дочерними материалами, а также базовый код шейдера. Эти основные вещи объявляются следующим образом:

ULON
AbstractMaterial <preview_hidden=true var_prefix=var texture_prefix=tex>
{

	// Let's add a screen texture, we will use it later in BaseMaterial
	Texture2D color <source=procedural>

	Shader common=
	#{
	#}

	// Here comes the core of our fragment shader:
	Shader base_shader= // Declare the shader node for further use
	#{
		#include <core/materials/shaders/render/common.h> //Base UUSL include
		
		// We render to a single texture with 4 channels
		STRUCT_FRAG_BEGIN
			INIT_COLOR(float4)
		STRUCT_FRAG_END
		
		// Here we'll add code from inherited materials
		#shader common
		
		// Declare the main fragment function
		MAIN_FRAG_BEGIN(FRAGMENT_IN)
			// Here we'll add code from inherited materials
			#shader fragment
		MAIN_FRAG_END
	#}
}

Теперь нам нужно добавить проход рендеринга, потому что один шейдер сам по себе ничего не рендерит. Поскольку мы реализуем материал пост-эффекта, мы должны добавить проход post и указать шейдеры, которые будут использоваться для него (мы будем использовать наш base_shader как фрагментный шейдер).

Примечание
Если вершинный шейдер не указан, будет использоваться шейдер по умолчанию из core/materials/shaders/default/empty.vert.
ULON
Pass post
{
	Fragment=base_shader
}

UNIGINE поддерживает Скриптовые материалы функциональность, позволяющая выполнять выражения (фрагменты кода) на определенных этапах последовательность рендеринга , и мы собираемся использовать его.

ULON
// Let's subscribe our USC expression to render callback after all Engine's post-effects
Expression RENDER_CALLBACK_END_POST_MATERIALS=
#{
	// Make a copy of the screen texture
	Texture source = engine.render.getTemporaryTexture(engine.render_state.getScreenColorTexture());
	source.copy(engine.render_state.getScreenColorTexture());

	// Set it as the source texture for the post-effect
	setTexture("color", source);

	// Render the pass "post" directly to the screen
	renderPassToTexture("post", engine.render_state.getScreenColorTexture());
	
	// Since we won't use a newly requested texture, we can tell the engine that anyone else can use it.
	engine.render.releaseTemporaryTexture(source);
#}

Собрав все вместе, мы получаем полный код нашего абстрактного материала, который вы можете скопировать и вставить в свой текстовый редактор и сохранить как файл my_abstract.abstmat в папке data вашего проекта:

my_abstract_base1.basemat

ULON
AbstractMaterial <preview_hidden=true var_prefix=var texture_prefix=tex>
{

	// Let's add a screen texture, we will use it later in BaseMaterial
	Texture2D color <source=procedural>

	Shader common=
	#{
	#}

	// Here comes the core of our fragment shader:
	Shader base_shader= // Declare the shader node for further use
	#{
		#include <core/materials/shaders/render/common.h> //Base UUSL include
		
		// We render to a single texture with 4 channels
		STRUCT_FRAG_BEGIN
			INIT_COLOR(float4)
		STRUCT_FRAG_END
		
		// Here we'll add code from inherited materials
		#shader common
		
		// Declare the main fragment function
		MAIN_FRAG_BEGIN(FRAGMENT_IN)
			// Here we'll add code from inherited materials
			#shader fragment
		MAIN_FRAG_END
	#}

	// Adding a post pass to render our effect
	Pass post
	{
		Fragment=base_shader
	}

	// Let's subscribe our USC expression to render callback after all Engine's post-effects
	Expression RENDER_CALLBACK_END_POST_MATERIALS=
	#{
		// Make a copy of the screen texture
		Texture source = engine.render.getTemporaryTexture(engine.render_state.getScreenColorTexture());
		source.copy(engine.render_state.getScreenColorTexture());

		// Set it as the source texture for the post-effect
		setTexture("color", source);

		// Render the pass "post" directly to the screen
		renderPassToTexture("post", engine.render_state.getScreenColorTexture());
		
		// Since we won't use a newly requested texture, we can tell the engine that anyone else can use it.
		engine.render.releaseTemporaryTexture(source);
	#}
}

2. Наследование базового материала от абстрактного#

Теперь мы собираемся наследовать базовый материал от созданного абстрактного (my_abstract.abstmat), чтобы расширить его функциональность.

Для начала мы реализуем простой пост-эффект цветового фильтра.

Создайте новый файл с расширением *.basemat. Опять же, имя файла используется в качестве имени основного материала.

Опишите базовый материал, указав, что он имеет абстрактный материал my_abstract в качестве родителя:

ULON
BaseMaterial <parent=my_abstract>
{

	// Add the color parameter that will define the optical filter color
	Color my_color = [0.5 0.5 0.5 1]

	// All shader variables that are defined in the material should have a prefix to be referred to in shaders
	// For sliders, colors, and other basic parameters, it is defined with var_prefix currently set as 'var'
	// For textures, it is texture_prefix currently set as 'tex'
	Shader fragment=
	#{
		// Sample screen texture
		float4 sample = TEXTURE(tex_color, IN_UV);

		// Multiply the screen texture by specified color
		OUT_COLOR = sample * var_my_color;
	#}
}
Примечание

Все переменные шейдера, определенные в материале, должны иметь префикс, чтобы на них можно было ссылаться в шейдерах:

  • Префикс для slider, color и других основных параметров задаются с помощью аргумента var_prefix.
  • Префикс для текстур задается с помощью аргумента texture_prefix.

Например:

ULON
// Setting prefixes
AbstractMaterial <preview_hidden=true var_prefix=var texture_prefix=tex>

/*...*/

// Adding the parameter
Color my_color = [0.5 0.5 0.5 1]

/*...*/

// Refering to a variable
OUT_COLOR = sample * var_my_color;

Сохраните базовый материал как my_abstract_base1.basemat в папку data вашего проекта.

Вот и все. То, что мы написали, достаточно для создания простого эффекта постобработки.

3. Добавление другого производного материала#

Теперь мы собираемся вывести другой, более сложный материал из нашего абстрактного.

Этот материал должен использовать трехплоскостное отображение для применения указанной текстуры modulation с указанным intensity в закрытых областях (согласно карте SSAO).

Давайте создадим новый файл базового материала и установим тот же абстрактный материал (my_abstract.abstmat) в качестве его родителя.

Вот полный код нашего второго базового материала (сохраните его как my_abstract_base2.basemat в папке data вашего проекта):

my_abstract_base2.basemat

ULON
BaseMaterial <parent=my_abstract>
{
	// For this example more scene textures are required
	Texture2D normal <source=gbuffer_normal>
	Texture2D scene_depth <source=current_depth>
	Texture2D <source=ssao>

	// Modulation texture, UV scale, and effect intensity
	Texture2D modulation = "core/textures/common/checker_d.dds"
	Slider global_scale = 2
	Slider power = 10 <min=0 max=3>
	
	Shader common=
	#{
		float3 triplanar_sample(float2 uv, float depth_in)
		{
			float3 normal_ws = mul3(s_imodelview, unpackGBufferNormal(TEXTURE(tex_normal, uv).rgb));
			float3 position_ws = s_camera_position + mul3(s_imodelview, nativeDepthToPositionVS(depth_in, uv));
			
			float4 texcoord = position_ws.xyyz * var_global_scale;
			float3 weight = triplanarWeightFast(normal_ws, 0.5f);
			
			return TEXTURE_TRIPLANAR(tex_modulation, texcoord, weight).rgb;
		}

		float4 final(float2 uv, float depth_in)
		{
			float4 base = TEXTURE(tex_color, uv);
			float ssao = TEXTURE(tex_ssao, uv).r;

			float3 effect = triplanar_sample(uv, depth_in);
			return float4(lerp(base.rgb * effect * var_power, base.rgb, ssao), base.a);
		}
	#}

	Shader fragment=
	#{
		// Sample depth texture
		float depth = TEXTURE(tex_scene_depth, IN_UV).r;
		if (depth == 0.0f || depth == 1.0f)
		   discard;
			
		OUT_COLOR = final(IN_UV, depth);
	#}
}

4. Наследование пользовательских материалов от базовых и их использование#

Значения параметров, заявленные в базовых материалах, могут быть изменены в режиме реального времени только в унаследованных пользовательских материалах. Следовательно, вам необходимо наследовать пользовательские материалы от базовых материалов для ваших проектов и корректировать их по мере необходимости (назначать и изменять значения параметров и т. д.).

Чтобы протестировать наш первый пост-эффект, сделайте следующее:

  • Найдите my_abstract_base1.basemat вMaterialsщелкните его правой кнопкой мыши, выберите Create Child и переименуйте новый материал в my_color_filter.

  • Поскольку это пост-эффект, для глобального применения материала мы должны открыть файл Settings -> Render -> Scriptable Materials.
  • Нажмите Add New Scriptable Material и укажите материал my_color_filter. После этого вы можете изменить цвет фильтра.

Вот финальное изображение с нашим первым пост-материалом.

Повторите описанные выше шаги для базового материала my_abstract_base2.basemat, чтобы протестировать второй пост-эффект. Вот как это может выглядеть:

Последнее обновление: 03.05.2024
Build: ()