はじめに
Shader初心者の私がGeometryシェーダーでつまづいた点と修正方法をここに書きます。
Geometryシェーダーで下記の動画に映っているものを作ろうとしてました。
Unity UrpのGeometryシェーダーで作りたかったもの pic.twitter.com/V9gbwUCU2l
— Noriaki (@h4uWtExFss77493) June 5, 2023
注意
Shader初心者のため間違った知識を記載している場合がございます。
もし本記事内に間違っている箇所が存在した場合、指摘いただければ幸いです。
環境
Unity 2021.3.1f1
Universal RP 12.1.6
失敗点
Geometryシェーダー内で頂点座標を変換するとオブジェクトの座標と描画される座標がズレてしまいました。
Geometryシェーダーで失敗してしまったもの pic.twitter.com/2Ehc3OI1PV
— Noriaki (@h4uWtExFss77493) June 5, 2023
失敗時の頂点シェーダーとジオメトリシェーダー
struct appdata
{
float4 positionOS : POSITION;
float3 normal : NORMAL;
};
struct g2f
{
float4 positionCS : SV_POSITION;
};
appdata vert(appdata v)
{
return v;
}
// 法線情報から頂点を時間で変化させる
[maxvertexcount(3)]
void geom(triangle appdata input[3], inout TriangleStream<g2f> stream)
{
float3 vec1 = input[1].positionOS.xyz - input[0].positionOS.xyz;
float3 vec2 = input[2].positionOS.xyz - input[0].positionOS.xyz;
float3 normal = normalize(cross(vec1, vec2));
[unroll]
for (int i = 0; i < 3; i++)
{
appdata v = input[i];
g2f o;
v.positionOS.xyz += normal * (_SinTime.w * 0.5 + 0.5);
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
stream.Append(o);
}
stream.RestartStrip();
}
解決方法
URP環境でTransformObjectToHClip()
をGeometryシェーダー内で使用した場合、何故かワールド座標空間の原点を呼び出してしまいます。
そのため、構造体にクリップ空間座標
とワールド空間座標
を宣言し、Vertexシェーダー内でGetVertexPositionInputs()
を使いワールド座標を取得し、Geometryシェーダー内でTransformObjectToHClip()
を使いクリップ座標を取得することで解決しました。
解決時の頂点シェーダーとジオメトリシェーダー
struct appdata
{
float4 positionOS : POSITION;
float3 normal : NORMAL;
};
struct VertexOutPut
{
float3 positionWS : TEXCOORD0;
};
struct g2f
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
};
VertexOutPut vert(appdata v)
{
VertexOutPut o = (VertexOutPut)0;
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.positionOS.xyz);
o.positionWS = vertexInput.positionWS;
return o;
}
[maxvertexcount(3)]
void geom(triangle VertexOutPut input[3], inout TriangleStream<g2f> stream)
{
float3 vec1 = input[1].positionWS - input[0].positionWS;
float3 vec2 = input[2].positionWS - input[0].positionWS;
float3 normal = normalize(cross(vec1, vec2));
[unroll]
for (int i = 0; i < 3; i++)
{
VertexOutPut v = input[i];
g2f o;
v.positionWS.xyz += normal * (_SinTime.w * 0.5 + 0.5) * _ScaleFactor;
o.positionWS = v.positionWS;
o.positionCS = TransformObjectToHClip(v.positionWS);
stream.Append(o);
}
stream.RestartStrip();
}
おわりに
初めて記事を書いたので分かりづらい点やアドバイスなどございましたら是非意見をください。
また本記事が誰かの参考になれば幸いです。
最後まで見ていただきありがとうございました。
参考記事と参考動画