Вы будете иметь , чтобы сделать объект дважды в какой - то момент. Вы можете уйти с оказанием только лица , стоящие перед камерой , как только и лица , стоящие вдали от камеры один раз, но у него есть свои компромиссы.
Самое простое распространенное решение достигается путем рендеринга объекта дважды за один проход:
- Вы используете вершинный шейдер, чтобы инвертировать нормали объекта и «взорвать его» по размеру контура, и фрагментный шейдер, чтобы отобразить его в цвете контура.
- На этом контуре рендеринга вы делаете объект как обычно. 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 и заставит ваших художников захотеть убить вас во сне, если вы не упростите их генерацию: у объектов есть контур как отдельный сетка все время, просто полностью прозрачная или перемещена куда-то, где ее не видно (например, глубоко под землей), пока она вам не понадобится
В дополнение к ответу Мартина Сойкаса, для статических объектов (или спрайтов) вы можете сделать что-то более простое.
Вы можете сохранить тот же спрайт, но с контуром, в свой текстурный атлас или другую текстуру целиком, что облегчает переключение. Это также позволит вам создавать пользовательские контуры, которые более привлекательны или выглядят иначе.
Другой метод - сохранить спрайт как одноцветную фигуру, которая немного больше, и визуализировать ее непосредственно перед рендерингом самого спрайта. Это даст вам возможность легко менять цвет выделения, и вам может не потребоваться столько разных цветовых форм, сколько вам понадобятся контурные спрайты с помощью метода # 1.
И то и другое увеличит вашу память.
источник
Как указано в комментариях к ответу Мартина Сойки, аналогичного эффекта можно добиться также с помощью буфера трафарета или глубины, как это подробно описано Максом МакГуайром на FlipCode:
http://www.flipcode.com/archives/Object_Outlining.shtml
Это в основном рисование каркасной версии модели, которую вы хотите обрисовать в общих чертах с увеличенной шириной линии (или в случае, если это невозможно, как, например, в D3D, с использованием квадратов для линий, обращенных к камере), при установке буфера трафарета на постоянное значение.
Этот подход может быть немного устаревшим с использованием сегодняшнего OpenGL, и для того, чтобы размыть контур объекта, рендеринг в текстуру все равно будет необходим.
источник