本日はShader学習枠です。
ShaderAnimationを学んでいきます。
〇ジオメトリシェーダーでメッシュをアニメーションさせる。
今回は前回作成したジオメトリシェーダーで複製したメッシュをそれぞれアニメーションさせていきます。
ここでのコアとなる部分はジオメトリシェーダーでwhile文により頂点を複製、z軸の座標をi(ループの数)だけずらしたメッシュを生成することでメッシュを複製しています。
[maxvertexcount(120)]
void geom(triangle appdata input[3],uint pid :SV_PrimitiveID,inout TriangleStream<FragmentInput> triStream)
{
//頂点を宣言
float3 wp0 = input[0].vertex.xyz;
float3 wp1 = input[1].vertex.xyz;
float3 wp2 = input[2].vertex.xyz;
float3 wp3 = input[0].vertex.xyz;
float3 wp4 = input[1].vertex.xyz;
float3 wp5 = input[2].vertex.xyz;
各頂点ごとのUVデータを取得
...
//各頂点の法線を代入
...
int i = 0;
int n =_Size;
while ( i<n)
{
//各メッシュの座標
float direction;
direction= i ;
float3 wp6 = wp0+float3(0,0,direction);
float3 wp7 =wp1+float3(0,0,direction);
float3 wp8 = wp2+float3(0,0,direction);
float3 wp9 = wp3+float3(0,0,direction);
float3 wp10 = wp4+float3(0,0,direction);
float3 wp11 = wp5+float3(0,0,direction);
//VertexOutputに頂点データを渡す。
triStream.Append(VertexOutput(wp6, wn0 ,uv0));
triStream.Append(VertexOutput(wp7, wn1,uv1));
triStream.Append(VertexOutput(wp8, wn2,uv2));
triStream.Append(VertexOutput(wp9, wn3,uv3));
triStream.Append(VertexOutput(wp10, wn4,uv4));
triStream.Append(VertexOutput(wp11, wn5,uv5));
//メッシュを作成
triStream.RestartStrip();
i=i+1;
}
}
今回はこの複製したメッシュをそれぞれアニメーションするようにします。
〇アニメーション
今回は複製したメッシュがそれぞれ円軌道で回転運動するようなコードを書きました。

ジオメトリシェーダー部のコードは次のようになります。
[maxvertexcount(120)]
void geom(triangle appdata input[3],uint pid :SV_PrimitiveID,inout TriangleStream<FragmentInput> triStream)
{
...
int i = 0;
int n =_Size;
float l= _Size-1;
float ext= 0;
while ( i<n)
{
float x=0;
float result;
result=extr(i);
float3 direction;
direction.z= i + ext +result;
direction.x = circle(_Radius*floor(direction.z),i).x;
direction.y = circle(_Radius*floor(direction.z),i).y;
float3 wp6 = wp0+float3(direction.x,direction.y,direction.z);
float3 wp7 =wp1+float3(direction.x,direction.y,direction.z);
float3 wp8 = wp2+float3(direction.x,direction.y,direction.z);
float3 wp9 = wp3+float3(direction.x,direction.y,direction.z);
float3 wp10 = wp4+float3(direction.x,direction.y,direction.z);
float3 wp11 = wp5+float3(direction.x,direction.y,direction.z);
//Create 1st Triangles
triStream.Append(VertexOutput(wp6, wn0 ,uv0,i));
triStream.Append(VertexOutput(wp7, wn1,uv1,i));
triStream.Append(VertexOutput(wp8, wn2,uv2,i));
//Create 2nd Triangles
triStream.Append(VertexOutput(wp9, wn3,uv3,i));
triStream.Append(VertexOutput(wp10, wn4,uv4,i));
triStream.Append(VertexOutput(wp11, wn5,uv5,i));
// Create Plane(1st + 2nd =2Triangles)
triStream.RestartStrip();
i=i+1;
}
float3 wp12 = wp0+float3(0,0,l);
float3 wp13 = wp1+float3(0,0,l);
float3 wp14 = wp2 +float3(0,0,l);
float3 wp15 = wp3+float3(0,0,l);
float3 wp16 = wp4+float3(0,0,l);
float3 wp17 = wp5 + float3(0,0,l);
triStream.Append(VertexOutput(wp12, wn0,uv0,i));
triStream.Append(VertexOutput(wp13, wn1,uv1,i));
triStream.Append(VertexOutput(wp14, wn2,uv2,i));
triStream.Append(VertexOutput(wp15, wn3,uv3,i));
triStream.Append(VertexOutput(wp16, wn4,uv4,i));
triStream.Append(VertexOutput(wp17, wn5,uv5,i));
triStream.RestartStrip();
}
今回[direction]というそれぞれのメッシュの座標を格納する変数を作成しました。
direction.z= i + ext +result;
direction.x = circle(_Radius*floor(direction.z),i).x;
direction.y = circle(_Radius*floor(direction.z),i).y;
float3 wp6 = wp0+float3(direction.x,direction.y,direction.z);
z(奥行き)はループの数(2つ目のメッシュは奥行き2m)を主としてrandomで奥行きの変動を加えています。
x,y座標は[circle()]という関数を新たに作り円軌道を行うようにしています。
float2 circle(float radius , float rundam)
{
float2 result;
float theta = rundam *_Time;
result.x = radius*sin(theta);
result.y = radius*cos(theta);
return result;
}
[circle]にはそれぞれ半径、係数の引数を持っています。
これは円軌道の方程式
x= 半径×sinΘ y=半径×cosΘ
をしようしています。この際Θには_Timeの値を入れることで時間変化(回転)を行うようにしています。
これによってメッシュの座標が円軌道になります。

[Radius]の値を0にすることで直線配置も行うことができます。

以上でジオメトリシェーダーでメッシュをアニメーションさせることができました。
〇コード全文
Shader "Unlit/TargetSite"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor("Main Color",color)=(1,1,1,1)
_Size("Size",float)=1
_Speed("Speed",float)=0
_Radius("Radius",float)=0
_SecondTex("SecondTex",2D)="wgite"{}
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent""IgnoreProjector"="True"}
LOD 100
ZWrite Off
Blend One One
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "TergetSite.hlsl"
ENDHLSL
}
}
}
hlslファイル
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
float _Speed;
float _Size;
float _Radius;
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainColor;
int _changeTexture;
sampler2D _SecondTex;
struct appdata
{
float4 vertex :POSITION;
float2 uv:TEXCOORD0;
float3 normal :NORMAL;
};
struct FragmentInput
{
float4 position : SV_POSITION;
float2 uv :TEXCOORD0;
// float4 col :TEXCOORD1;
};
FragmentInput VertexOutput(float3 wp,float3 wn, float2 uv ,int size)
{
FragmentInput i;
i.position = TransformObjectToHClip(wp);
i.uv = uv;
// i.col = float4(0,0,0,0);
return i;
}
float extr(float inp)
{
float t=_Time/100 * _Speed ;
return abs(cos(frac((sin(dot(inp,78.233f)) + 10+t) * 43758.5453)));
}
float2 circle(float radius , float rundam)
{
float2 result;
float theta = rundam *_Time;
result.x = radius*sin(theta);
result.y = radius*cos(theta);
return result;
}
appdata vert (appdata v)
{
v.vertex = mul(unity_ObjectToWorld, v.vertex);
return v;
}
//This shader get 4 vertex in Plane Models;
[maxvertexcount(120)]
void geom(triangle appdata input[3],uint pid :SV_PrimitiveID,inout TriangleStream<FragmentInput> triStream)
{
float3 wp0 = input[0].vertex.xyz;
float3 wp1 = input[1].vertex.xyz;
float3 wp2 = input[2].vertex.xyz;
float2 uv0 = input[0].uv;
float2 uv1 = input[1].uv;
float2 uv2 = input[2].uv;
float2 uv3 = input[0].uv;
float2 uv4 = input[1].uv;
float2 uv5 = input[2].uv;
float3 wn0 = input[0].normal;
float3 wn1 = input[1].normal;
float3 wn2 = input[2].normal;
float3 wp3 = input[0].vertex.xyz;
float3 wp4 = input[1].vertex.xyz;
float3 wp5 = input[2].vertex.xyz;
float3 wn3 = input[0].normal;
float3 wn4 = input[1].normal;
float3 wn5 = input[2].normal;
int i = 0;
int n =_Size;
float l= _Size-1;
float ext= 0;
while ( i<n)
{
float x=0;
float result;
result=extr(i);
float3 direction;
direction.z= i + ext +result;
direction.x = circle(_Radius*floor(direction.z),i).x;
direction.y = circle(_Radius*floor(direction.z),i).y;
float3 wp6 = wp0+float3(direction.x,direction.y,direction.z);
float3 wp7 =wp1+float3(direction.x,direction.y,direction.z);
float3 wp8 = wp2+float3(direction.x,direction.y,direction.z);
float3 wp9 = wp3+float3(direction.x,direction.y,direction.z);
float3 wp10 = wp4+float3(direction.x,direction.y,direction.z);
float3 wp11 = wp5+float3(direction.x,direction.y,direction.z);
//Create 1st Triangles
triStream.Append(VertexOutput(wp6, wn0 ,uv0,i));
triStream.Append(VertexOutput(wp7, wn1,uv1,i));
triStream.Append(VertexOutput(wp8, wn2,uv2,i));
//Create 2nd Triangles
triStream.Append(VertexOutput(wp9, wn3,uv3,i));
triStream.Append(VertexOutput(wp10, wn4,uv4,i));
triStream.Append(VertexOutput(wp11, wn5,uv5,i));
// Create Plane(1st + 2nd =2Triangles)
triStream.RestartStrip();
i=i+1;
}
float3 wp12 = wp0+float3(0,0,l);
float3 wp13 = wp1+float3(0,0,l);
float3 wp14 = wp2 +float3(0,0,l);
float3 wp15 = wp3+float3(0,0,l);
float3 wp16 = wp4+float3(0,0,l);
float3 wp17 = wp5 + float3(0,0,l);
triStream.Append(VertexOutput(wp12, wn0,uv0,i));
triStream.Append(VertexOutput(wp13, wn1,uv1,i));
triStream.Append(VertexOutput(wp14, wn2,uv2,i));
triStream.Append(VertexOutput(wp15, wn3,uv3,i));
triStream.Append(VertexOutput(wp16, wn4,uv4,i));
triStream.Append(VertexOutput(wp17, wn5,uv5,i));
triStream.RestartStrip();
}
float4 frag (FragmentInput i):SV_Target
{
float4 col=float4(1,1,1,1);
float4 mainTex = tex2D(_MainTex,i.uv);
float4 secondTex =tex2D(_SecondTex,i.uv);
col *= _MainColor *mainTex;
return col;
}