荘東のTAノート(14・16) <特殊効果:炎+水流>
目次
文章:
1. ジョブの表示:
2. デモンストレーション: 火
(内炎+外炎)
参考文献:
1. 炎の参考ビデオ:
2. ブログの推薦: Simon schreibt。
実装のアイデア:
1. チャネルの使用状況
赤:外側の炎を表します。
緑:内なる炎を表します。
青:残りの部分
2. ノイズと形状の組み合わせ
赤 Rと緑 Gの2 つのチャネルをそれぞれ乗算します。 *ノイズ ノイズ マップ、UVフローとティリングを実行します。
3.ノイズノイズマップ:
ここでは 2 つのノイズ ノイズ画像を使用するため、2 つのグレースケール ノイズ画像はそれぞれR チャネルとG チャネルに配置されます。
このように、2 つのノイズを混合すると、流速と方向を変更することで、よりランダム性を高めることができます。
4. 下の図の右下隅にある外乱効果を実現します。
ノイズ 1 + ノイズ 2 * フィンガー マスク * グラデーション マスク = 流れるフィンガー ノイズ
ここにあります = 黒は透明ではなく、白は透明です。
彼は、炎の下の部分をあまり乱さないようにして、下にもっと黒い部分が見えるようにしたいと考えていました。
彼は炎が自分自身に集中するようにしたいので、真ん中にもう少し白を加えます。
5. UV を上のマスクに追加し、Y 軸である V のみを使用して、上にスクロールします。
結果は次の図に示すように、右下隅の効果になります。
Resultut (結果) = UV + R ノイズ+ G ノイズ* A-1 透明ペースト* A -2 透明ペースト
次に、ここでの結果にカスタムの色を追加できます。
緑の部分(内なる炎)が彼に何色を与えるべきか、
赤い部分(外炎)は何色にすればいいのか、
青またはAの部分(透明ペースト)を取り出して終了です。
以下に示すように
操作の練習:
AB テンプレートを参照して Code コードを開始します。
1. テクスチャと対応する制御パラメータを宣言します。
_Mask ("R: 外側の炎 G: 内側の炎 B: 透明ペースト", 2d) = "青" {}
_ノイズ("R: ノイズ 1、G: ノイズ 2",2d)="グレー"{}
対応する制御パラメータ:耕耘サイズ、流速、および2 つのノイズ画像の歪み強度を制御します。
_Noise 1 Parms ("X: サイズ Y: 速度 Z: 強度 W: なし"、ベクトル) = (1, 0.2, 0.2, 1)
_Noise 2 Parms ("X: サイズ Y: 速度 Z: 強度 W: なし"、ベクトル) = (1, 0.2, 0.2, 1)
2. 3 つの UV を出力する出力構造を定義し、3 つの UVを制御します。
UV1 をマスクにサンプリング
UV2 を Noise1 にサンプリング
UV3 サンプリングから Noise2
3. 頂点シェーダー内の上記 3 つの UV に対応します。
o.uv0 = v.uv0;
注:以前の Tilling の追加は _ST を追加することで直接制御されていたためですが、ここでは 2 つのノイズ マップを 1 つのテクスチャに配置します。_ST に従って記述されている場合、2 つのノイズ マップは Tillng 変換に従ってパラメーター化されます。したがって、ここではそれらを別々に記述する必要があります。
再確認: _ST のタイリングとオフセットの原理とは何ですか?
** UVに_ST のXYコンポーネントを乗算し、+ ZWコンポーネント =タイリングとオフセットを実装します。
ここで使用するグラフはすべて4 方向に連続しているため、フロートを使用してタイリング (XY) を制御できます。つまり、XY のコンポーネントはすべて値に等しく、比例してスケーリングされます。
次に、宣言された_Noise 1 Parmsの X コンポーネントに o.uv1 を乗算します。
(ここではノイズ 1 とノイズ 2 は同じです)
o.uv 1 = v.uv * _Noise 1 Parms 。バツ ;
o.uv 2 = v.uv * _Noise 2 Parms 。バツ ;
3.1. ノイズ 1 とノイズ 2を流す
剰余と Time.x に + を加え、制御 (流量) に _Noise1-2Parms の Y コンポーネントを乗算します。
o.uv1 = v.uv * _Noise 1 Parms 。x + frac(_Time.x * _Noise1Parms.y) ;
o.uv2 = v.uv * _Noise 2 Parms 。x + frac(_Time.x * _Noise2Parms.y) ;
3.2.上向きの流れモードを修正します。
訂正:必要なのは斜め上向きの流れではなく、上向きの流れです。ここでこのようなことが起こる理由は次のとおりです。
1次元ベクトル(float)を使って2次元ベクトルUV(float2)に加算しますが、Float1はfloat2のパラメータを2つ同時に加算するので、傾いた状態が現れます。
解決策:独自に2 次元ベクトルを構築し、これに==を追加すると、実際にはfloat2 (0, flow) になります。
o.uv1 = v.uv * _Noise 1 Parms 。x + float2( 0.0 , frac(_Time.x * _Noise1Parms.y) ) ;
o.uv2 = v.uv * _Noise 2 Parms 。x + float2( 0.0 , frac(_Time.x * _Noise2Parms.y) ) ;
このようにして、UV を 1 つの軸内で移動できます。
上下、+-を変更することで流れ方向を変更できます。
4.ピクセル シェーダーでのサンプリングと計算:
float var_Noise1 = tex2D(_Noise, i.uv1).r; (サンプル ノイズ マップ 1、Rチャネル)。
float var_Noise1 = tex2D(_Noise, i.uv1).g; (サンプル ノイズ イメージ 2、チャネルG )。
4.2.ピクセル シェーダーで2 つのノイズの混合を開始し、強度を制御します。
//混合ノイズ マップ = ノイズ 1 * Noise1Parms の Z コンポーネント (強度) + ノイズ 2 * Noise2Parms の Z コンポーネント (強度)。
float ノイズ = var_Noise1 * _Noise1Parms.z + var_Noise2 * _Noise2Parms.z;
4.3. ピクセル シェーダーでマスク マップへの妨害の構築を開始します。
ピクセル シェーダで 2 次元 UV ベクトルを宣言して妨害マスクに備えます。ここでは i.uv0 + ノイズ (フロー妨害) を置くだけで済みます。
//WarpUV を宣言し、ノイズを使用してマスクの UV を妨害します。
float2 warpUV = i.uv0 + ノイズ;
(ここで、i.uv0 は 2 次元、ノイズは 1 次元であり、2 次元ベクトルとして構築するには修正が必要であることに注意してください)。
float warpMask = tex2D(_Mask, i.uv0).b; (サンプル B チャネル勾配)
* warpMask はマスクの UV をワープする強さです
float2 warpUV = i.uv0 + float2(0 ,noise ) * warpMask ;
(ここで UV がどのように見えるかを確認できます)
float4 (i.uv0,0,1) を返します。
(UV 摂動後の様子は次のとおりです)
float4(warpUV,0,1)を返します;
4.4. 妨害された UV (WarpUV) を使用してマスク テクスチャをサンプリングします。
float3 var_Mask = tex2D(_Mask,warpUV);
float4 (finalRGB , 1) を返します。
4.5. カスタム カラーを追加し、透明度を差し引く:
外側の炎の色 *外側の炎のマスク+内側の炎の色 * 内側の炎のマスク = FinalRGB
float3finalRGB = _Color1 * var_Mask.r + _Color2 * var_Mask.g ;
//透明チャンネルの赤チャンネル領域のブランクを差し引く + 青チャンネル領域のブランク = 黒値の減算。
rerun の A チャネル自体は (1-入力領域値) に相当するため、黒値領域が差し引かれ、白値領域が確保されます。
float不透明度= var_Mask.r + var_Mask.g ;
float4(finalRGB, opacity )を返します;
コードテンプレート:
Shader "AP01/L16/Fire" {
Properties {
_Mask ("R:外焰 G:内焰 B:透贴", 2d) = "blue"{}
_Noise ("R:噪声1 G:噪声2", 2d) = "gray"{}
_Noise1Params ("噪声1 X:大小 Y:流速 Z:强度", vector) = (1.0, 0.2, 0.2, 1.0)
_Noise2Params ("噪声2 X:大小 Y:流速 Z:强度", vector) = (1.0, 0.2, 0.2, 1.0)
[HDR]_Color1 ("外焰颜色", color) = (1,1,1,1)
[HDR]_Color2 ("内焰颜色", color) = (1,1,1,1)
}
SubShader {
Tags {
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _Mask; uniform float4 _Mask_ST;
uniform sampler2D _Noise;
uniform half3 _Noise1Params;
uniform half3 _Noise2Params;
uniform half3 _Color1;
uniform half3 _Color2;
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv0 : TEXCOORD0; // UV信息 采样Mask
float2 uv1 : TEXCOORD1; // UV信息 采样Noise1
float2 uv2 : TEXCOORD2; // UV信息 采样Noise2
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex); // 顶点位置 OS>CS
o.uv0 = TRANSFORM_TEX(v.uv, _Mask);
o.uv1 = o.uv0 * _Noise1Params.x - float2(0.0, frac(_Time.x * _Noise1Params.y));
o.uv2 = o.uv0 * _Noise2Params.x - float2(0.0, frac(_Time.x * _Noise2Params.y));
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR {
// 扰动遮罩
half warpMask = tex2D(_Mask, i.uv0).b;
// 噪声1
half var_Noise1 = tex2D(_Noise, i.uv1).r;
// 噪声2
half var_Noise2 = tex2D(_Noise, i.uv2).g;
// 噪声混合
half noise = var_Noise1 * _Noise1Params.z + var_Noise2 * _Noise2Params.z;
// 扰动UV
float2 warpUV = i.uv0 - float2(0.0, noise) * warpMask;
// 采样Mask
half3 var_Mask = tex2D(_Mask, warpUV);
// 计算FinalRGB 不透明度
half3 finalRGB = _Color1 * var_Mask.r + _Color2 * var_Mask.g;
half opacity = var_Mask.r + var_Mask.g;
return half4(finalRGB, opacity); // 返回值
}
ENDCG
}
}
}
3. デモンストレーション: 水
実装のアイデア:
1. 水の効果を実現するためのアイデア:
RampTex 摂動マップ、
異なるタイリング サイズ値、
異なるフロー、_
異なる速度、_
異なる強度_
水面の乱れと重なりを実現する。
2. パラメータの概要:
BaseTex ベース テクスチャ
RampTex 妨害マップ:
ノイズ01 X:タイリングサイズY:流れ方向Z:速度W:強度
ノイズ02 X:タイリングサイズY:流れ方向Z:速度W:強度
操作の練習:
1. 水漫画テクスチャ MainTex ノイズ テクスチャ WarpTex その他の制御パラメータ
_MainTex ("カラー テクスチャ", 2d) = "ホワイト"{}//メインの水面の漫画のテクスチャ
_Speed ("X: 流量 XY: 流量 Y", Vector ) = (1.0, 1.0, 0.5, 1.0) //メイン画像の流速を制御
_WarpTex ("摂動マップ", 2d) = "グレー"{} // ノイズ マップ
//ノイズ1の制御パラメータ
_Warp1Params ("X: サイズ Y: 速度 XZ: 速度 YW: 強度",ベクトル) = (1.0, 1.0, 0.5, 1.0)
//ノイズ2の制御パラメータ
_Warp2Params ("X: サイズ Y: 速度 XZ: 速度 YW: 強度",ベクトル) = (2.0, 0.5, 0.5, 1.0)
2. 頂点構造に、MainTex と WarpTex に対応する UV コントロールを追加します。
MainTex は 1 UV を占めます。
WarpTex は 2 つの UV を使用します。
o.uv0 = TRANSFORM_TEX(v.uv, _MainTex) - frac(_Time.x * _Speed);
ここでは、水面が U 方向と V 方向の両方に流れる必要があるため、前の火災のケースに従ってオフセットを構築することはできません。
では、両方の軸をどのように記述する必要があるのでしょうか?
U 成分とV成分は、それぞれYとZの流速です。
o.uv1 = v.uv * _Warp1Params.x - frac(_Time.x * _Warp1Params . y z );
o.uv2 = v.uv * _Warp2Params.x - frac(_Time.x * _Warp2Params . y z );
3. ピクセル シェーダーで、Warp テクスチャと MainTex テクスチャをサンプルします。
Warp テクスチャと MainTex テクスチャのサンプル:
half3 var_Warp1 = tex2D(_WarpTex, i.uv1).rgb; // 外乱 1
half3 var_Warp2 = tex2D(_WarpTex, i.uv2).rgb; // 外乱 2
2 つの摂動強度をブレンドします
ハーフ 2 ワープ= (var_Warp1.yz - 0.5) * _Warp1Params.w +
(var_Warp2.yz - 0.5) * _Warp2Params .w;
UVと摂動を追加します。
float2 warpUV = i.uv0 + warp ;
MainTex のサンプルと出力:
ハーフ4 var_MainTex = tex2D( _MainTex , warpUV );
return float4( var_MainTex . xyz , 1.0);
コード例:
Shader "AP01/L16/Water" {
Properties {
_MainTex ("颜色贴图", 2d) = "white"{}
_WarpTex ("扰动图", 2d) = "gray"{}
_Speed ("X:流速X Y:流速Y", vector) = (1.0, 1.0, 0.5, 1.0)
_Warp1Params ("X:大小 Y:流速X Z:流速Y W:强度", vector) = (1.0, 1.0, 0.5, 1.0)
_Warp2Params ("X:大小 Y:流速X Z:流速Y W:强度", vector) = (2.0, 0.5, 0.5, 1.0)
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform sampler2D _WarpTex;
uniform half2 _Speed;
uniform half4 _Warp1Params;
uniform half4 _Warp2Params;
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput {
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv0 : TEXCOORD0; // UV信息 采样Mask
float2 uv1 : TEXCOORD1; // UV信息 采样Noise1
float2 uv2 : TEXCOORD2; // UV信息 采样Noise2
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex); // 顶点位置 OS>CS
o.uv0 = v.uv - frac(_Time.x * _Speed);
o.uv1 = v.uv * _Warp1Params.x - frac(_Time.x * _Warp1Params.yz);
o.uv2 = v.uv * _Warp2Params.x - frac(_Time.x * _Warp2Params.yz);
return o;
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR {
half3 var_Warp1 = tex2D(_WarpTex, i.uv1).rgb; // 扰动1
half3 var_Warp2 = tex2D(_WarpTex, i.uv2).rgb; // 扰动2
// 扰动混合
half2 warp = (var_Warp1.xy - 0.5) * _Warp1Params.w +
(var_Warp2.xy - 0.5) * _Warp2Params.w;
// 扰动UV
float2 warpUV = i.uv0 + warp;
// 采样MainTex
half4 var_MainTex = tex2D(_MainTex, warpUV);
return float4(var_MainTex.xyz, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}