Personaje de dibujos animados de Guilty Gear Bad Flame_Estilo 2D_Representación de personajes de Unity
Efecto inicial del personaje:
Representación básicaSimpleBas
Shader "SimpleBase"
{
Properties
{
[Header(BaseColor)]
_MainTex ("BaseTex", 2D) = "white" {
}
[Space(20)]
[Header(ILM)]
_ILMTex("ILMTex",2D) = "white"{
}
[Space(20)]
[Header(SSS)]
_SssTex("SssTex",2D) = "white"{
}
[Space(20)]
[Header(Detail)]
_DetailTex("DetailTex",2D) = "white"{
}
}
SubShader
{
Tags {
"RenderType"="Opaque" }
LOD 100
Pass
{
Tags{
"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float4 tangent :TEXCOORD2;
half3 normal : NORMAL;
half4 color : COLOR;
};
struct v2f
{
float4 uv : TEXCOORD0;
float4 pos : SV_POSITION;
half4 vertexColor : TEXCOORD1;
half3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _ILMTex;
sampler2D _SssTex;
sampler2D _DetailTex;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv0, _MainTex);
o.uv.zw = v.uv1;
o.vertexColor = v.color;
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 ilm = tex2D(_ILMTex,i.uv.xy);
fixed4 baseColor = tex2D(_MainTex, i.uv.xy);
fixed4 sssColor = tex2D(_SssTex,i.uv.xy);
fixed3 detail = tex2D(_DetailTex,i.uv.zw);
half ao = saturate((i.vertexColor.r - 0.7) * 50);
half3 worldNormal = normalize(i.worldNormal);
half3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
half3 halfDir = normalize(worldLightDir + worldViewDir);
half NdotH = saturate(dot(halfDir,worldNormal));
half NdotL = dot(worldLightDir,worldNormal);
half sssFactor = saturate((NdotL * 0.5 + 0.5 - i.vertexColor.b * 0.5) * 50) * ao;
fixed4 finalColor = 1;
finalColor.rgb = lerp(sssColor,baseColor,sssFactor) * detail * ilm.a;
return finalColor;
}
ENDCG
}
}
}
Análisis de recursos
Modelo
Color de vértice:
Canal B gris del modelo
Recursos de textura
SOL_base_Efecto de bloque de color básico:
Entre ellos, el efecto del canal SOL_base_A:
- Entre ellos, el canal A consiste en dibujar en negro el área del patrón de texto del personaje.
SOL_ilm: como sigue
Distribución regional en el modelo SOL_ilm
como sigue:
- De izquierda a derecha, RGBA.
-
Canal R: controla la intensidad general de las luces.
-
Canal G: controla el valor de compensación de la iluminación NdotL, como las arrugas.
-
Canal B: controla el tamaño, rango o forma del resaltado.
-
Un canal: la línea de dibujo interior del modelo de personaje.
-
Color de vértice:
-
Canal R: AO, también controla la compensación de iluminación NdotL.
SOL_Sss: como sigue
El efecto de SOL_ilm en el modelo.
El color obtenido multiplicando el valor de SSSTexture y el color de la luz ambiental determina el color de la sombra. Color de luz clave para
zonas brillantes Color de luz ambiental para zonas oscuras
mejoramiento
Imagen de cuerpo completo antes de la optimización.
Imagen de cuerpo entero optimizada
cuadro comparativo facial
Cuadro comparativo de brazadas con cinturón y brazo
Cuadro comparativo
Código de renderizado
Shader "SOL"
{
Properties
{
[Header(BaseColor)]
_MainTex ("BaseTex", 2D) = "white" {
}
[Space(20)]
[Header(ILM)]
_ILMTex("ILMTex",2D) = "gray"{
}
[Space(20)]
[Header(SSS)]
_SssTex("SssTex",2D) = "black"{
}
[Space(20)]
[Header(Detail)]
_DetailTex("DetailTex",2D) = "white"{
}
_ToonThreshold("ToonThreshold",Range(0,1)) = 0.5
_ToonHardness("ToonHardness",Float) = 20.0
_SpecColor("spec color", Color) = (1,1,1,1)
_SpecSize("Spec Size",Range(0,1)) = 1
[Space(20)]
[Header(OutLine)]
_OutlineColor("Outline Color", Color) = (0,0,0,0)
_Outlinewidth("Outline Width",Range(0,1)) = 1
}
SubShader
{
Tags {
"RenderType"="Opaque" }
LOD 100
Pass
{
Tags{
"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
half3 normal : NORMAL;
half4 color : COLOR;
};
struct v2f
{
float4 uv : TEXCOORD0;
float4 pos : SV_POSITION;
half4 vertexColor : TEXCOORD1;
half3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
};
sampler2D _MainTex;
sampler2D _ILMTex;
sampler2D _SssTex;
sampler2D _DetailTex;
half _ToonThreshold;
half _ToonHardness;
half _SpecSize;
//没有定义"float4 _SpecColor" 是因为在#include "UnityLightingCommon.cginc"文件里已经被声明。
// float4 _SpecColor;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = float4 (v.uv0,v.uv1);
o.vertexColor = v.color;
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
half2 uv1 = i.uv.xy;
half2 uv2 = i.uv.zw;
//贴图采样、亮部和暗部的颜色、控制高光的强度
fixed4 ilm = tex2D(_ILMTex,i.uv.xy);
half spec_intensity = ilm.r;//控制高光强度
half diffuse_control = ilm.g * 2.0 - 1.0;//光照偏移
half spec_size = ilm.b;//控制高光形状
half inner_line = ilm.a;//内描线
fixed4 baseColor = tex2D(_MainTex, i.uv.xy);//亮部的颜色
fixed4 sssColor = tex2D(_SssTex,i.uv.xy);//暗部的颜色
fixed3 detail = tex2D(_DetailTex,i.uv.zw);//细节线条
//顶点处理
half ao = saturate((i.vertexColor.r - 0.7) * 50);
//向量
half3 worldNormal = normalize(i.worldNormal);
half3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//漫反射
half NdotL = dot(worldLightDir,worldNormal);
half halflambert = (NdotL + 1.0) * 0.5;
half lambertterm = halflambert * ao + diffuse_control;
half toondiffuse = saturate ((lambertterm - _ToonThreshold) * _ToonHardness);
half3 finaldiffuse = lerp(sssColor ,baseColor,toondiffuse);
//高光
float NdotV = (dot(worldNormal,worldViewDir) + 1.0) * 0.5;
float spec_term = NdotV * ao + diffuse_control;
spec_term = halflambert * 0.9 + spec_term * 0.1;
half toon_spec = saturate((spec_term - (1.0 - spec_size * _SpecSize)) * 500);
half3 speccolor = (_SpecColor.xyz + baseColor) * 0.5;
half3 finaspec = toon_spec * speccolor * spec_intensity;
//描线
half3 inner_line_Color = lerp(baseColor * 0.2 , float3(1.0,1.0,1.0),inner_line);
half3 ditailcolor = tex2D(_DetailTex , uv2);
ditailcolor = lerp(baseColor * 0.2, float3(1.0,1.0,1.0),ditailcolor);
half3 finalline = inner_line_Color * inner_line_Color * ditailcolor;
fixed3 finalColor = (finaldiffuse + finaspec) * finalline;
return float4(finalColor,1.0);
}
ENDCG
}
Pass
{
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv0 : TEXCOORD0;
half3 normal : NORMAL;
half4 color : COLOR;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
half4 vertexColor : TEXCOORD1;
half3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
};
sampler2D _MainTex;
float4 _OutlineColor;
float _Outlinewidth;
v2f vert (appdata v)
{
v2f o;
float3 pos_VS = UnityObjectToViewPos(v.vertex);
float3 normal_WS = UnityObjectToWorldNormal(v.normal);
float3 outline_dir = normalize(mul((float3x3)UNITY_MATRIX_V,normal_WS));
o.vertexColor = v.color;
pos_VS += outline_dir * _Outlinewidth * 0.001 * v.color.a;
o.pos = mul(UNITY_MATRIX_P,float4(pos_VS,1.0));
o.uv = v.uv0;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 baseColor = tex2D(_MainTex, i.uv.xy).xyz;
half maxComponent = max(max(baseColor.r,baseColor.g),baseColor.b) - 0.004;
half3 saturatedColor = step(maxComponent.rrr,baseColor) * baseColor;
saturatedColor = lerp(baseColor.rgb,saturatedColor,0.6);
half3 outlineColor = 0.8 * saturatedColor * baseColor * _OutlineColor.xyz;
return float4(outlineColor,1.0);
}
ENDCG
}
}
}
Enlace de referencia de representación de tarjetas:
Zenji Nishikawa: El secreto de los "gráficos 3D en tiempo real de pura animación de dibujos animados"
[Traducción] El secreto de los "gráficos 3D en tiempo real de pura animación de dibujos animados" realizado en los "gráficos de juegos experimentales" de Zenji Nishikawa "GUILTY GEAR Xrd -SIGN- ", Parte 1 (1)