Мои Уведомления
Привет, !
Мой Аккаунт Мои Финансы Мои Подписки Мои Настройки Выход
Руководство API скрипты

Примеры поверхностных шейдеров

Шейдеры поверхностиУпрощенный способ написания шейдеров для встроенных -в конвейере рендеринга. Подробнее
См. примеры Словарь
на этой странице, показывающие, как использовать встроенный модели освещения. Примеры реализации пользовательских моделей освещения см. в разделе Примеры освещения шейдеров поверхности.

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

Совместимость конвейера рендеринга

Название функции Встроенный конвейер рендеринга Универсальный конвейер рендеринга (URP) Конвейер рендеринга высокого разрешения (HDRP) Пользовательский SRP
Surface Shaders Да No

Чтобы узнать об упрощенном способе создания объектов Shader в URP, см. раздел Shader Graph.

No

Чтобы узнать об упрощенном способе создания объектов Shader в HDRP, см. раздел Shader Graph.

Нет

Пример простого шейдера

Мы начнем с очень простого шейдера и будем его развивать. Вот шейдер, который устанавливает цвет поверхности на «белый». Он использует встроенную модель освещения Ламберта (рассеянного).

Shader "Example/Diffuse Simple" { SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = 1; } ENDCG } Fallback "Diffuse" }

Вот как это выглядит на модели с двумя лампами:

Texture

Полностью белый объект довольно скучен, поэтому давайте добавим текстуру. Мы добавим блок Properties в шейдер, чтобы получить селектор текстуры в нашем материале.

Shader "Example/Diffuse Texture" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

Нормальное сопоставление

Давайте добавим сопоставление нормалей:

Shader "Example/Diffuse Bump" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }

Контурное освещение

Теперь попробуйте добавить краевое освещение, чтобы выделить края GameObjectфундаментального объекта в сценах Unity, который может представлять персонажей, реквизит, декорации, камеры, путевые точки и многое другое. Функциональность GameObject определяется прикрепленными к нему компонентами. Подробнее
См. в Словарь
. Мы добавим немного испускаемого света в зависимости от угла между нормалью к поверхности и направлением взгляда. Для этого мы будем использовать встроенную переменную viewDir Surface Shader.

Shader "Example/Rim" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0) _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0 } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 viewDir; }; sampler2D _MainTex; sampler2D _BumpMap; float4 _RimColor; float _RimPower; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); o.Emission = _RimColor.rgb * pow (rim, _RimPower); } ENDCG } Fallback "Diffuse" }

Текстура детализации

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

Shader "Example/Detail" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _Detail ("Detail", 2D) = "gray" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float2 uv_Detail; }; sampler2D _MainTex; sampler2D _BumpMap; sampler2D _Detail; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }

Использование средства проверки текстуры не всегда имеет практический смысл, но в этом примере оно используется для иллюстрации того, что происходит:

Подробная текстура в пространстве экрана

Текстура деталей в экранном пространстве не имеет практического смысла для модели головы солдата, но здесь она используется для иллюстрации того, как можно использовать встроенный ввод screenPos:

Shader "Example/ScreenPos" { Properties { _MainTex ("Texture", 2D) = "white" {} _Detail ("Detail", 2D) = "gray" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float4 screenPos; }; sampler2D _MainTex; sampler2D _Detail; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; float2 screenUV = IN.screenPos.xy / IN.screenPos.w; screenUV *= float2(8,6); o.Albedo *= tex2D (_Detail, screenUV).rgb * 2; } ENDCG } Fallback "Diffuse" }

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

Отражение кубической карты

Вот шейдер, выполняющий кубическое отражение с использованием встроенного ввода worldRefl. Он очень похож на встроенный шейдер Reflective/Diffuse:

Shader "Example/WorldRefl" { Properties { _MainTex ("Texture", 2D) = "white" {} _Cube ("Cubemap", CUBE) = "" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float3 worldRefl; }; sampler2D _MainTex; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Emission = texCUBE (_Cube, IN.worldRefl).rgb; } ENDCG } Fallback "Diffuse" }

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

Если вы хотите создать отражения, на которые влияют карты нормалейТип текстуры Bump Map, который позволяет добавлять поверхность такие детали, как неровности, бороздки и царапины, моделируются так, как если бы они были представлены реальной геометрией. Подробнее
См. в Словарь
, он должен быть более сложным: INTERNAL_DATA необходимо добавить в структуру Input, а функцию WorldReflectionVector использовать для вычисления вектор отражения пикселей после того, как вы записали нормальный вывод.

Shader "Example/WorldRefl Normalmap" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _Cube ("Cubemap", CUBE) = "" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 worldRefl; INTERNAL_DATA }; sampler2D _MainTex; sampler2D _BumpMap; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); o.Emission = texCUBE (_Cube, WorldReflectionVector (IN, o.Normal)).rgb; } ENDCG } Fallback "Diffuse" }

Вот блестящий солдат с картой нормалей:

Срезы через мировую космическую позицию

Вот шейдер, который «нарезает» GameObject, отбрасывая пикселинаименьшие единицы в компьютерном изображении. Размер пикселя зависит от разрешения вашего экрана. Пиксельное освещение рассчитывается для каждого пикселя экрана. Подробнее
См. в Словарь
в почти горизонтальных кольцах. Это делается с помощью функции clip() Cg/HLSL, основанной на мировой позиции пикселя. Мы будем использовать встроенную переменную worldPos Surface Shader.

Shader "Example/Slices" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } Cull Off CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 worldPos; }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5); o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }

Обычное выдавливание с модификатором вершин

Можно использовать функцию модификатора вершин, которая будет изменять входящие данные вершин в вершинном шейдерепрограмме. который запускается в каждой вершине 3D-модели во время рендеринга модели. Подробнее
См. в Словарь
. Это можно использовать для таких вещей, как процедурная анимация и экструзия по нормалям. Для этого используется директива компиляции Surface Shader vertex:functionName с функцией, которая принимает параметр inout appdata_full.

Вот шейдер, который перемещает вершины вдоль их нормалей на величину, указанную в Материале:

Shader "Example/Normal Extrusion" { Properties { _MainTex ("Texture", 2D) = "white" {} _Amount ("Extrusion Amount", Range(-1,1)) = 0.5 } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; }; float _Amount; void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

Перемещение вершин по нормалям делает солдат толстым:

Пользовательские данные, вычисляемые для каждой вершины

С помощью функции модификатора вершин также можно вычислять пользовательские данные в вершинном шейдере, которые затем будут передаваться в функцию поверхностного шейдера для каждого пикселя. Используется та же директива компиляции vertex:functionName, но функция должна принимать два параметра: inout appdata_full и выходной ввод. Вы можете заполнить любой входной элемент, который не является встроенным значением.

Примечание. Элементы пользовательского ввода, используемые таким образом, не должны иметь имена, начинающиеся с «uv», иначе они не будут работать должным образом.

В приведенном ниже примере определяется пользовательский элемент float3 customColor, который вычисляется в функции вершины:

Shader "Example/Custom Vertex Data" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; float3 customColor; }; void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); o.customColor = abs(v.normal); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Albedo *= IN.customColor; } ENDCG } Fallback "Diffuse" }

В этом примере customColor устанавливается равным абсолютному значению нормали:

Более практичным применением может быть вычисление любых данных для каждой вершины, которые не предоставляются встроенными входными переменными; или оптимизация вычислений шейдеров. Например, можно вычислить краевое освещение в вершинах GameObject вместо того, чтобы делать это в шейдере поверхности для каждого пикселя.

Окончательный модификатор цвета

Можно использовать функцию «модификатор окончательного цвета», которая изменит окончательный цвет, вычисленный шейдером. Для этого используется директива компиляции поверхностного шейдера finalcolor:functionName. , с функцией, которая принимает параметры Input IN, SurfaceOutput o, inout fixed4 color.

Вот простой шейдер, который применяет оттенок к конечному цвету. Это отличается от простого применения оттенка к цвету альбедо поверхности: этот оттенок также повлияет на любой цвет, полученный из Lightmapsпредварительно визуализированная текстура, содержащая эффекты источников света на статических объектах сцены. Карты освещения накладываются поверх геометрии сцены для создания эффекта освещения. Подробнее
См. в Словарь
, Световые зондыСветовые зонды хранят информацию о том, как свет проходит через пространство в вашей сцене. Набор световых зондов, расположенных в заданном пространстве, может улучшить освещение движущихся объектов и статических пейзажей LOD в этом пространстве. Подробнее
См. в Словаре
и подобных дополнительных источниках.

Shader "Example/Tint Final Color" { Properties { _MainTex ("Texture", 2D) = "white" {} _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor struct Input { float2 uv_MainTex; }; fixed4 _ColorTint; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { color *= _ColorTint; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

Более практичным применением может быть вычисление любых данных для каждой вершины, которые не предоставляются встроенными входными переменными; или оптимизация вычислений шейдеров. Например, можно вычислить краевое освещение в вершинах GameObject вместо того, чтобы делать это в шейдере поверхности для каждого пикселя.

Окончательный модификатор цвета

Можно использовать функцию «модификатор окончательного цвета», которая изменит окончательный цвет, вычисленный шейдером. Для этого используется директива компиляции поверхностного шейдера finalcolor:functionName. , с функцией, которая принимает параметры Input IN, SurfaceOutput o, inout fixed4 color.

Вот простой шейдер, который применяет оттенок к конечному цвету. Это отличается от простого применения оттенка к цвету альбедо поверхности: этот оттенок также повлияет на любой цвет, полученный из Lightmapsпредварительно визуализированная текстура, содержащая эффекты источников света на статических объектах сцены. Карты освещения накладываются поверх геометрии сцены для создания эффекта освещения. Подробнее
См. в Словарь
, Световые зондыСветовые зонды хранят информацию о том, как свет проходит через пространство в вашей сцене. Набор световых зондов, расположенных в заданном пространстве, может улучшить освещение движущихся объектов и статических пейзажей LOD в этом пространстве. Подробнее
См. в Словаре
и подобных дополнительных источниках.

Shader "Example/Fog via Final Color" { Properties { _MainTex ("Texture", 2D) = "white" {} _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input,data); float4 hpos = UnityObjectToClipPos(v.vertex); hpos.xy/=hpos.w; data.fog = min (1, dot (hpos.xy, hpos.xy)*0.5); } fixed4 _FogColor; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = _FogColor.rgb; #ifdef UNITY_PASS_FORWARDADD fogColor = 0; #endif color.rgb = lerp (color.rgb, fogColor, IN.fog); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

Линейный туман

Shader "Example/Linear Fog" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert #pragma multi_compile_fog sampler2D _MainTex; uniform half4 unity_FogStart; uniform half4 unity_FogEnd; struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input,data); float pos = length(UnityObjectToViewPos(v.vertex).xyz); float diff = unity_FogEnd.x - unity_FogStart.x; float invDiff = 1.0f / diff; data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0); } void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { #ifdef UNITY_PASS_FORWARDADD UNITY_APPLY_FOG_COLOR(IN.fog, color, float4(0,0,0,0)); #else UNITY_APPLY_FOG_COLOR(IN.fog, color, unity_FogColor); #endif } void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }

Наклейки

Наклейки обычно используются для добавления деталей к материалам во время выполнения (например, попадания пуль). Они особенно полезны при отложенном рендеринге, потому что изменяют GBuffer до того, как он активируется, тем самым снижая производительность.

В типичном сценарии надписи должны отображаться после непрозрачных объектов и не должны отбрасывать тени, как показано в ShaderLabЯзык Unity для определения структуры объектов Shader. Подробнее
См. в разделе Словарь
«Теги» в приведенном ниже примере.

Shader "Example/Decal" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry+1" "ForceNoShadowCasting"="True" } LOD 200 Offset -1, -1 CGPROGRAM #pragma surface surf Lambert decal:blend sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } }
Вы можете отблагодарить автора, за перевод документации на русский язык. ₽ Спасибо
Руководство Unity 2021.3