アウトラインオブジェクト効果


26

リーグオブレジェンドやディアブロIIIに見られるようなアウトライン効果を実現するにはどうすればよいですか?

リーグオブレジェンドの概要 リーグオブレジェンドの概要 ディアブロIIIの概要

シェーダーを使用して行われますか?どうやって?
私は特定のエンジンに縛られていない答えを好みますが、私が取り組んでいるどんなエンジンにも適応できる答えです。

回答:


19

あなたはしますいくつかの点で二回オブジェクトをレンダリングする必要があります。カメラに面しいる面とカメラに面していない面を1回だけレンダリングしても問題ありませんが、トレードオフがあります。

最も単純な一般的な解決策は、同じパスでオブジェクトを2回レンダリングすることによって行われます。

  • 頂点シェーダーを使用してオブジェクトの法線を反転し、アウトラインのサイズで「爆破」し、フラグメントシェーダーを使用してアウトラインカラーでレンダリングします。
  • そのアウトラインレンダリング上で、オブジェクトを通常どおりにレンダリングします。フィギュア自体はカメラに面する面で構成されているのに対し、オブジェクトの「背面」にある面によってアウトラインが作成されるため、zオーダーは通常多少自動的に正しくなります。

これはビルドと実装が簡単で、テクスチャからテクスチャへのトリックを回避できますが、いくつかの顕著な欠点があります。

  • カメラからの距離で拡大縮小しない場合、アウトラインサイズは異なります。遠くにあるオブジェクトは、近くにあるオブジェクトよりも輪郭が小さくなります。もちろん、これはあなたが実際に欲しいものかもしれませ
  • 「ブローアップ」頂点シェーダーは、例のスケルトンのような複雑なオブジェクトに対してはうまく機能せず、レンダーにZファイティングアーティファクトを簡単に導入します。修正するには、オブジェクトを2つのパスでレンダリングする必要がありますが、法線を逆にする必要はありません。
  • 他のオブジェクトが同じスペースを占有している場合、アウトラインとオブジェクトはあまりうまく機能しない可能性があり、一般に反射シェーダーと屈折シェーダーを組み合わせた場合に正しくなるのは苦痛です。

このようなシェーダーの基本的な考え方は次のようになります(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"
}

他の一般的な方法では、オブジェクトも2回レンダリングされますが、頂点シェーダーは完全に回避されます。一方、1回のパスで簡単に行うことはできず、レンダリングからテクスチャへの変換が必要です。オブジェクトを「フラットな」アウトラインカラーのフラグメントシェーダーで一度レンダリングし、このレンダリングで(加重)ブラーを使用します画面スペース、その後、その上に通常どおりオブジェクトをレンダリングします。

3番目の、おそらく最も簡単な実装方法もありますが、GPUにもう少し負担がかかり、アーティストが簡単に生成できない限り、あなたのアーティストがあなたの睡眠中にあなたを殺したくなるでしょう:常にメッシュを作成し、完全に透明にするか、必要なときまで見えない場所(地下深くなど)に移動します


ここでステンシルバッファは一般的に使用されるアプローチではありませんか?
edA-qa mort-ora-y

1
@ edA-qamort-ora-y:これも同様に機能する可能性がありますが、私はそのアプローチを試したことがないため、コメントできません。:)実用的なアルゴリズムを念頭に置いている場合は、このメソッドを別の回答として追加してください。
マーティンソイカ

私はそれについてはあまり知りませんが、ステンシルバッファに関してアウトラインが頻繁に言及されるだけです。:)それはあなたの最初のアプローチのハードウェアベースのバージョン(2つのパス、最初の1つが大きい)に過ぎないようです。
edA-qa mort-ora-y

ステンシルバッファアプローチでは、ステンシルバッファの更新とクリアに使用する帯域幅が増え、複数のパスが必要になります。Martinリストのアプローチは、いくつかの限られた場合(最大で2つ)で1つのパスで実行でき、最小限の帯域幅オーバーヘッドが必要です。
ショーンミドルディッチ

このアプローチ(法線に沿ってサイズを拡大する)は、キューブのようなオブジェクト(特にオルソカメラ)では機能しません。それに対する解決策はありますか?
NPS

4

Martin Sojkasの回答に加えて、静的オブジェクト(またはスプライト)の場合は、もっと単純なもので済ますことができます。

同じスプライトを保存できますが、アウトラインをテクスチャアトラスまたは別のテクスチャに完全に保存できるため、切り替えが簡単になります。これにより、視覚的に魅力的なカスタムアウトラインを作成したり、見た目を変えたりすることもできます。

もう1つの方法は、スプライトをわずかに大きい1色の形状として保存し、スプライト自体がレンダリングされる直前にレンダリングすることです。これにより、選択範囲の色を簡単に変更できるようになります。また、方法#1のアウトラインスプライトほど必要な色の形状は必要ありません。

どちらもメモリフットプリントを増やします。


4

Martin Sojkaの答えへのコメントで指摘されているように、FlipCodeのMax McGuireで詳述されているように、ステンシルまたはデプスバッファーを利用することでも同様の効果を達成できます。

http://www.flipcode.com/archives/Object_Outlining.shtml

基本的に、ステンシルバッファーを一定の値に設定しながら、線幅を増やしてアウトライン化するモデルのワイヤーフレームバージョンを描画します(または、D3Dのように、たとえば、ラインにカメラ向きの四角形を使用してこれが不可能な場合)。

このアプローチは、今日のOpenGLを使用して少し時代遅れになっている可能性があり、オブジェクトアウトラインアップルをぼやけさせるには、テクスチャへのレンダリングが必要です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.