Вы будете иметь , чтобы сделать объект дважды в какой - то момент. Вы можете уйти с оказанием только лица , стоящие перед камерой , как только и лица , стоящие вдали от камеры один раз, но у него есть свои компромиссы.
Самое простое распространенное решение достигается путем рендеринга объекта дважды за один проход:
- Вы используете вершинный шейдер, чтобы инвертировать нормали объекта и «взорвать его» по размеру контура, и фрагментный шейдер, чтобы отобразить его в цвете контура.
- На этом контуре рендеринга вы делаете объект как обычно. Z-порядок обычно автоматически более или менее правильный, поскольку контур создается гранями, которые находятся на «тыльной стороне» объекта, а сама фигура состоит из граней, обращенных к камере.
Это достаточно просто для создания и реализации и позволяет избежать любых уловок рендеринга в текстуру, но имеет несколько заметных недостатков:
- Размер контура, если вы не масштабируете его по расстоянию от камеры, будет меняться. Объекты, расположенные дальше, будут иметь меньший контур, чем те, что находятся поблизости. Конечно, это может быть то, что вы на самом деле хотите .
- Вершинный шейдер «взорвать» не очень хорошо работает для сложных объектов, таких как скелет в вашем примере, легко вводя артефакты z-борьбы в рендер. Исправление требует от вас рендеринга объекта в два прохода, но спасает вас от изменения нормалей.
- Контур и объект могут работать не очень хорошо, когда другие объекты занимают одно и то же пространство, и, как правило, это трудно сделать правильно в сочетании с шейдерами отражения и преломления.
Основная идея для такого шейдера выглядит следующим образом (Cg, для Unity - код является слегка измененным toon-шейдером, который я нашел где-то и не заметил источника, так что это скорее плохо написанное доказательство концепции, чем готовый использовать шейдер):
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 и заставит ваших художников захотеть убить вас во сне, если вы не упростите их генерацию: у объектов есть контур как отдельный сетка все время, просто полностью прозрачная или перемещена куда-то, где ее не видно (например, глубоко под землей), пока она вам не понадобится