ユニティちゃんモデルをベースに作ったモデルをRenderTextureにキャプチャした時にほっぺたが黒ずむ問題の解決法
Twitterで最近作っているゲームの進捗を上げているのですが、
今日の進捗その2。
— MIYAKE (@ScreenPocket) October 8, 2019
・戦闘の邪魔にならないようにキャラのキャプチャの横側をアルファで消した
・勝利時の演出(カメラワーク)仮組み pic.twitter.com/uDhUyhYjBw
購入したアセットの女騎士モデルのほっぺたが黒ずんでしまっていました

右下がキャプチャ画像
こちらのモデルはユニティちゃんモデルをベースに作られているようなので、
解決法がブログに書けそうかなーと思いましたので記載します。
結論から書くと、頬の赤らみのアルファ値がRenderTextureに書き込まれてしまって、背景の色と合成されているので、赤らみ側のアルファ値を除去します。
- 新しい「ほっぺた用のシェーダ」を追加する
- 元の「ほっぺた用シェーダ」は「Unlit/Transparent」なのでbuiltInShaderをユニティ公式からDLしてUnlit-Alpha.shaderの内部を「自分が追加したほっぺた用シェーダ」にコピーする
- ブレンド係数の末尾に、Zero Oneを追加(描画元のアルファ値を0にし、描画済みのアルファ値を尊重するように)する
ということでコードは下記
Shader "ScreenPocket/3D/Character/Cheek"
{
Properties{
_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader{
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
LOD 100
ZWrite Off
//////////////////////////////////////////////////////////↓ここ!
Blend SrcAlpha OneMinusSrcAlpha, Zero One
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}

という事で、解決しました。
1枚のRawImageをRGBで3枚に分割して加算合成するジオメトリシェーダ

舌の根も乾かないうちにジオメトリシェーダを書いたので記事にしておきます
Shader "UI/Default(Geometry) RGB Divide"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
_Distance("Distance", Float) = 0
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest Always
Blend One One
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#pragma target 5.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct g2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
float _Distance;
//Geometryシェーダで処理するので何もしない
appdata_t vert(appdata_t v)
{
return v;
}
[maxvertexcount(9)]
void geom(triangle appdata_t input[3], inout TriangleStream<g2f> outStream)
{
float2 offsets[3] = { float2(0,1),float2(1,0),float2(-1,0) };//ずらす方向ベクトル
float4 colors[3] = { float4(1,0,0,1),float4(0,1,0,1),float4(0,0,1,1) };
[unroll]
for (int i = 0; i < 3; i++)
{
[unroll]
for (int j = 0; j < 3; j++)
{
appdata_t v = input[j];
g2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
o.worldPosition = v.vertex;
o.worldPosition.xy += offsets[i] * _Distance;
o.vertex = UnityObjectToClipPos(o.worldPosition);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.color = v.color * _Color * colors[i];
outStream.Append(o);
}
outStream.RestartStrip();
}
}
fixed4 frag(g2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}_Distanceをいじると、ずらし幅を調整できます。
本当にやりたい事はこれじゃないんだけども、意外とあっさりできたので記事化…!
UI用のジオメトリシェーダを書く時のプレーンなシェーダメモ
ちょっとジオメトリシェーダを触りたかったので、UI/Defaultにそのままジオメトリシェーダを噛ましたシェーダをメモがてら貼り付けておきます
Shader "UI/Default(Geometry)"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#pragma target 5.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct g2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
//Geometryシェーダで処理するので何もしない
appdata_t vert(appdata_t v)
{
return v;
}
//VertexShaderで行っていた処理をそのまま行う
[maxvertexcount(3)]
void geom(triangle appdata_t input[3], inout TriangleStream<g2f> outStream)
{
[unroll]
for (int i = 0; i < 3; i++)
{
appdata_t v = input[i];
g2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
o.worldPosition = v.vertex;
o.vertex = UnityObjectToClipPos(o.worldPosition);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.color = v.color * _Color;
outStream.Append(o);
}
outStream.RestartStrip();
}
fixed4 frag(g2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}
参考にさせて頂いたのはこちら
wordpress.notargs.com
edom18.hateblo.jp
これを起点として、何かできたらまた記事書きます。
音沙汰なかったら察してください
C#6以降で使える文字列補間
普段C#6が使えない環境なので知らなかったけど、これめっちゃ便利な予感。
docs.microsoft.com
C#6以降しか使えないので、とりあえず最新のUnity2018.3でテスト(2018.1以降なら使えるはず)。

$マークを付ければこんな指定ができる。
※$マークを付けなかったら普通にFormatException

表示された!
という事で、使っていこうと思います。
UI.Textの文字をジャンプさせる頂点シェーダ
それだけでは終わらず、
— MIYAKE (@ScreenPocket) October 26, 2018
上辺と下辺の動きもズラせばもうちょっと有機的な動きに近づくね pic.twitter.com/YH9HOVWovU
Twitterで投稿した文字をジャンプさせるシェーダを貼っておきます
- builtin_shaders-2018.2.12f1 の UI/Defaultベースで作成しています
Shader "ScreenPocket/UI/Default/Font/Jump"
{
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest[unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
uint index : SV_VertexID;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
float charId = floor(v.index / 2);
charId = (-charId + charId * 2) + 1;
float radian = radians(charId * 10 + frac(_Time.y) * -360);
OUT.worldPosition.y += saturate(sin(radian)) * 50;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
OUT.color = v.color * _Color;
return OUT;
}
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}使い方は新しく作ったマテリアルにこのシェーダをくっつけて、TextComponentのマテリアル項目にそのマテリアルをアタッチして下さい。
調整したい場合は * 10 の値とか、 * 50 の値とかを調整すればよいです
試しにベータ版のUnityをインストールしたら、2018.2.9f1もおかしくなってしまった時の対処法
試しにβ版を入れたところビルドが通らなくなってしまったので、対処法をメモ書き。
アンインストールやら再インストールやらを試しましたがそれでも治らず、
結局 Help > Reset Packages Default を選択 すると元に戻りました。