Anda akan harus membuat objek dua kali di beberapa titik. Anda dapat pergi dengan rendering hanya wajah yang menghadap kamera sekali dan wajah yang menghadap jauh dari kamera sekali, tetapi memiliki trade-off.
Solusi umum yang paling sederhana dilakukan dengan merender objek dua kali dalam pass yang sama:
- Anda menggunakan vertex shader untuk membalikkan normals dari objek dan "meledakkannya" dengan ukuran outline dan shader fragmen untuk membuatnya dalam warna outline
- Lebih dari garis itu membuat, Anda membuat objek secara normal. Z-order biasanya secara otomatis benar, lebih atau kurang, karena garis dibuat oleh wajah-wajah yang ada di "belakang" objek sementara gambar itu sendiri terdiri dari wajah-wajah yang menghadap kamera.
Ini cukup sederhana untuk membangun dan mengimplementasikan dan menghindari trik render-to-tekstur, tetapi memiliki beberapa kelemahan nyata:
- Ukuran garis besar, jika Anda tidak mengukurnya berdasarkan jarak dari kamera, akan bervariasi. Objek yang lebih jauh akan memiliki garis besar yang lebih kecil daripada yang terdekat. Tentu saja, ini mungkin yang sebenarnya Anda inginkan .
- Vertex shader "blow up" tidak bekerja dengan baik untuk objek kompleks seperti kerangka dalam contoh Anda, dengan mudah memperkenalkan artefak z-fight ke render. Memperbaikinya mengharuskan Anda untuk membuat objek dalam dua lintasan, tetapi menyelamatkan Anda dari membalikkan normals.
- Garis besar dan objek mungkin tidak bekerja dengan baik ketika objek lain menempati ruang yang sama dan secara umum sulit untuk diperbaiki ketika dikombinasikan dengan bayangan dan pembiasan pembiasan.
Gagasan dasar untuk shader seperti ini terlihat seperti ini (Cg, untuk Unity - kodenya adalah sedikit dimodifikasi toon shader yang saya temukan di suatu tempat dan tidak mencatat sumbernya, jadi ini adalah bukti konsep yang lebih buruk daripada konsep siap-pakai). untuk menggunakan 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"
}
Metode umum lainnya membuat objek dua kali juga, tetapi menghindari vertex shader sepenuhnya. Di sisi lain, itu tidak dapat dengan mudah dilakukan dalam satu lintasan, dan membutuhkan render-to-tekstur: Membuat objek sekali dengan "datar" garis-garis shader fragmen berwarna dan menggunakan blur (tertimbang) pada render ini di layar ruang , lalu render objek seperti biasa di atasnya.
Ada juga metode ketiga dan mungkin paling mudah untuk diterapkan, meskipun pajak GPU sedikit lebih dan akan membuat artis Anda ingin membunuh Anda dalam tidur Anda kecuali Anda membuatnya mudah bagi mereka untuk menghasilkan: Buat benda memiliki garis besar sebagai terpisah jala sepanjang waktu, hanya sepenuhnya transparan atau pindah ke suatu tempat di mana itu tidak terlihat (seperti jauh di bawah tanah) sampai Anda membutuhkannya
Selain jawaban Martin Sojkas, untuk objek statis (atau sprite), Anda dapat menggunakan sesuatu yang lebih sederhana.
Anda dapat menyimpan sprite yang sama tetapi dengan garis besar ke dalam atlas tekstur Anda atau seluruhnya tekstur lain, yang membuat beralih mudah. Ini juga akan memungkinkan Anda untuk melakukan garis besar khusus yang lebih menarik secara visual atau hanya terlihat berbeda.
Metode lainnya adalah menyimpan sprite sebagai bentuk satu warna yang sedikit lebih besar dan merender sebelum sprite itu sendiri dibuat. Ini akan memberi Anda kemampuan untuk dengan mudah mengubah warna seleksi, dan Anda mungkin tidak membutuhkan bentuk warna yang jauh berbeda karena Anda akan perlu menguraikan sprite dengan metode # 1.
Keduanya akan meningkatkan jejak memori Anda.
sumber
Seperti yang ditunjukkan dalam komentar untuk jawaban Martin Sojka, efek serupa juga dapat dicapai dengan menggunakan stensil atau penyangga kedalaman, seperti yang dirinci oleh Max McGuire pada FlipCode:
http://www.flipcode.com/archives/Object_Outlining.shtml
Ini pada dasarnya menggambar versi wireframe dari model yang Anda inginkan diuraikan dengan peningkatan lebar garis (atau dalam kasus ini tidak mungkin, seperti dalam D3D misalnya, menggunakan kamera menghadap paha depan untuk garis) sambil mengatur buffer stensil ke nilai konstan.
Aproach ini mungkin sedikit tanggal menggunakan OpenGL hari ini dan agar objek garis apel kabur, rendering ke tekstur masih akan diperlukan.
sumber