如何获得类似于《英雄联盟》或《暗黑破坏神III》中的轮廓效果?
使用着色器完成吗?怎么样?
我希望答案不限于任何特定的引擎,而是可以适应我正在使用的任何引擎的答案。
如何获得类似于《英雄联盟》或《暗黑破坏神III》中的轮廓效果?
使用着色器完成吗?怎么样?
我希望答案不限于任何特定的引擎,而是可以适应我正在使用的任何引擎的答案。
Answers:
您将不得不在某个时刻两次渲染对象。您可以只渲染一次面对相机的面和一次面对远离相机的面,但是这是有其取舍的。
最简单的常见解决方案是通过在同一遍中两次渲染对象来完成的:
这很容易构建和实现,并且避免了任何“渲染到纹理”的技巧,但是有一些明显的缺点:
这样的着色器的基本想法如下所示(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"
}
另一种常见方法也将对象渲染两次,但完全避免了顶点着色器。另一方面,它不容易通过单步完成,并且需要渲染到纹理:使用“平面”轮廓色片段着色器渲染对象一次,并在此渲染中使用(加权)模糊屏幕空间,然后照常在其顶部渲染对象。
还有第三种方法,而且可能是最容易实现的方法,尽管它会使GPU负担更多,并使您的艺术家想在睡眠中谋杀您,除非您使它们易于生成:将对象作为单独的轮廓一直保持网格状,只是完全透明,或者一直移动到看不见的地方(例如地下深处),直到您需要它为止
正如对Martin Sojka的回答的评论所指出的那样,也可以通过使用模板或深度缓冲区来实现类似的效果,如Max McGuire在FlipCode上详细介绍的那样:
http://www.flipcode.com/archives/Object_Outlining.shtml
它基本上是在绘制模版的线框版本,并增加线宽(或者在不可能的情况下,例如在D3D中,使用面向相机的四边形作为线),同时将模板缓冲区设置为恒定值。
使用当今的OpenGL,此方法可能有些过时,并且为了使对象轮廓Applear模糊,仍然需要将其渲染为纹理。