Ви будете мати , щоб зробити об'єкт двічі в якій - то момент. Ви можете піти з рендерингу лише обличчя, що стоїть перед камерою один раз, і обличчя, що відводиться від камери один раз, але це має свої компроміси.
Найпростіше загальне рішення виконується шляхом надання об'єкта двічі за один і той же прохід:
- Ви використовуєте вершинний шейдер, щоб перевернути нормалі об'єкта і "підірвати його" за розміром контуру та фрагментом шейдера, щоб передати його в колір контуру
- За цим контурним візуалізацією об'єкт відображається нормально. Порядок z, як правило, автоматично правильний, більш-менш, оскільки контур складається з граней, які знаходяться на «задній частині» об'єкта, а сама фігура складається з облич, звернених до камери.
Це досить просто для створення та впровадження та дозволяє уникнути будь-яких хитрощів щодо текстури, але має кілька помітних недоліків:
- Розмір контуру, якщо його не масштабувати на відстані від камери, буде змінюватися. Об'єкти, що знаходяться далі, матимуть менший контур, ніж поблизу. Звичайно, це може бути те, що ви насправді хочете .
- Вершина шейдера "підірвати" не дуже добре справляється зі складними об'єктами, такими як скелет у вашому прикладі, легко вводячи артефакти z-боротьби у візуалізацію. Для її виправлення потрібно винести об’єкт за два проходи, але врятує вас від зміни норми.
- Обриси та об'єкт можуть не дуже добре працювати, коли інші об'єкти займають той самий простір, і взагалі боляче виправитись у поєднанні з шейдерами відбиття та заломлення.
Основна ідея такого шейдера виглядає приблизно так (Cg, для Unity - код є дещо зміненим тоновим шейдером, який я десь знайшов і не відмітив джерело, тому це більш погано написане підтвердження концепції, ніж готовий- використовувати шейдер):
Shader "Basic Outline" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor ("Outline Color", Color) = (1,0.5,0,1)
_Outline ("Outline width", Range (0.0, 0.1)) = .05
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
CGPROGRAM
#pragma exclude_renderers gles
#pragma exclude_renderers xbox360
#pragma vertex vert
struct appdata {
float4 vertex;
float3 normal;
};
struct v2f
{
float4 pos : POSITION;
float4 color : COLOR;
float fog : FOGC;
};
float _Outline;
float4 _OutlineColor;
v2f vert(appdata v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul ((float3x3)UNITY_MATRIX_MV, v.normal);
norm.x *= UNITY_MATRIX_P[0][0];
norm.y *= UNITY_MATRIX_P[1][1];
o.pos.xy += norm.xy * _Outline;
o.fog = o.pos.z;
o.color = _OutlineColor;
return o;
}
ENDCG
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
SetTexture [_MainTex] { combine primary }
}
Pass {
Name "BASE"
Tags {"LightMode" = "Always"}
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewDir : TEXCOORD1;
float3 normal : TEXCOORD2;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.normal = v.normal;
o.uv = TRANSFORM_UV(0);
o.viewDir = ObjSpaceViewDir( v.vertex );
return o;
}
uniform float4 _Color;
uniform sampler2D _MainTex;
float4 frag (v2f i) : COLOR
{
half4 texcol = tex2D( _MainTex, i.uv );
half3 ambient = texcol.rgb * (UNITY_LIGHTMODEL_AMBIENT.rgb);
return float4( ambient, texcol.a * _Color.a );
}
ENDCG
}
}
FallBack "Diffuse"
}
Інший поширений метод також робить об'єкт вдвічі, але повністю уникає вершинного шейдера. З іншого боку, це не може бути легко виконано за один прохід, і він потребує текстури рендерінгу: Візуалізуйте об’єкт один раз за допомогою "плоского" фрагмента шейдера з кольоровим контуром і використовуйте (зважене) розмиття для цього візуалізації в екранного простору , а потім виведіть об'єкт, як зазвичай, поверх нього.
Існує також третій і, можливо, найпростіший у застосуванні метод, хоча він трохи оподатковує GPU і змусить ваших артистів захотіти вбити вас уві сні, якщо ви не спростите їх для створення: Нехай об'єкти мають контур як окремий сітка весь час, просто повністю прозора або переміщена кудись, де її не видно (як глибоко під землею), поки не знадобиться