Bạn sẽ phải kết xuất đối tượng hai lần tại một số điểm. Bạn có thể thoát khỏi việc chỉ hiển thị các mặt đối diện với máy ảnh một lần và các mặt quay ra khỏi máy ảnh một lần, nhưng nó có sự đánh đổi.
Giải pháp phổ biến đơn giản nhất được thực hiện bằng cách hiển thị đối tượng hai lần trong cùng một lượt:
- Bạn sử dụng một shader đỉnh để đảo ngược các quy tắc của đối tượng và "thổi nó lên" theo kích thước của phác thảo và một shader mảnh để hiển thị nó trong màu phác thảo
- Qua bản phác thảo đó, bạn kết xuất đối tượng bình thường. Thứ tự z thường tự động đúng, ít nhiều, vì đường viền được tạo bởi các mặt nằm ở "mặt sau" của đối tượng trong khi hình vẽ được tạo thành từ các mặt đối diện với máy ảnh.
Điều này đủ đơn giản để xây dựng và thực hiện và tránh mọi thủ thuật kết xuất đồ họa, nhưng có một vài nhược điểm đáng chú ý:
- Kích thước phác thảo, nếu bạn không chia tỷ lệ theo khoảng cách từ máy ảnh, sẽ thay đổi. Các đối tượng ở xa hơn sẽ có một phác thảo nhỏ hơn so với những đối tượng gần đó. Tất nhiên, đây có thể là những gì bạn thực sự muốn .
- Trình tạo bóng đỉnh "nổ tung" không hoạt động tốt cho các đối tượng phức tạp như bộ xương trong ví dụ của bạn, dễ dàng đưa các vật phẩm chiến đấu z vào kết xuất. Việc sửa nó đòi hỏi bạn phải hiển thị đối tượng trong hai lần, nhưng giúp bạn không bị đảo ngược quy tắc.
- Phác thảo và đối tượng có thể không hoạt động tốt khi các đối tượng khác chiếm cùng một không gian và nói chung là một nỗi đau để có được ngay khi kết hợp với các bóng đổ phản xạ và khúc xạ.
Ý tưởng cơ bản cho một shader như thế này (Cg, đối với Unity - mã là một shader toon được sửa đổi một chút mà tôi tìm thấy ở đâu đó và không lưu ý đến nguồn, vì vậy nó là một khái niệm bằng văn bản tồi tệ hơn là sẵn sàng sử dụng shader):
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"
}
Phương pháp phổ biến khác cũng làm cho đối tượng hai lần, nhưng tránh hoàn toàn shader đỉnh. Mặt khác, nó không thể được thực hiện dễ dàng trong một lần duy nhất và cần kết xuất đồ họa: Kết xuất đối tượng một lần với trình tạo bóng mảnh màu phác thảo "phẳng" và sử dụng hiệu ứng làm mờ (có trọng số) trên kết xuất này trong không gian màn hình , sau đó kết xuất đối tượng như bình thường trên đầu nó.
Ngoài ra còn có một phương pháp thứ ba và có thể dễ thực hiện nhất, mặc dù nó đánh thuế GPU nhiều hơn một chút và sẽ khiến các nghệ sĩ của bạn muốn giết bạn trong giấc ngủ trừ khi bạn tạo ra chúng dễ dàng: Để các đối tượng có đường viền riêng biệt chia lưới mọi lúc, hoàn toàn trong suốt hoặc di chuyển đến nơi không nhìn thấy (như sâu dưới lòng đất) cho đến khi bạn cần nó