UnityのSkyboxシェーダーでレイマーチングをしてみました。
Skyboxシェーダーでは視線方向をTEXCOORD0
で、カメラ位置を_WorldSpaceCameraPos
を取得できるので、そのあたりを自分で計算せずにすぐにレイを飛ばすことができます。
Skyboxシェーダーの書き方自体は私が以前書いた記事を参考にしてください。
【Unity】Skyboxシェーダーの書き方 - Qiita
やっていること自体はよくあるレイマーチングなので説明は割愛します。
Raymarching.Shader
Shader "Skybox/Raymarching"
{
SubShader
{
Tags
{
"RenderType"="Background"
"Queue"="Background"
"PreviewType"="SkyBox"
}
Pass
{
ZWrite Off
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
float3 rayDir : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 rayDir : TEXCOORD0;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.rayDir = v.rayDir;
return o;
}
float sphere(float3 pos, float radius)
{
return length(pos) - radius;
}
float3 repeat(float3 pos, float3 span)
{
return abs(fmod(pos, span)) - span * 0.5;
}
float getDistance(float3 pos)
{
return sphere(repeat(pos, 10.0), 1.0f);
}
float3 getNormal(float3 pos) {
float d = 0.001;
return normalize(float3(
getDistance(pos + float3(d, 0, 0)) - getDistance(pos + float3(-d, 0, 0)),
getDistance(pos + float3(0, d, 0)) - getDistance(pos + float3(0, -d, 0)),
getDistance(pos + float3(0, 0, d)) - getDistance(pos + float3(0, 0, -d))
));
}
float3 raymarch(float3 cameraPos, float3 rayDir)
{
float3 pos = cameraPos;
for (int i = 0; i < 200; i++) {
float d = getDistance(pos);
pos += d * rayDir;
if (d < 0.001) {
return getNormal(pos) * 0.5 + 0.5;
}
}
return 0;
}
fixed4 frag (v2f i) : SV_TARGET
{
float3 rayDir = normalize(i.rayDir);
return fixed4(raymarch(_WorldSpaceCameraPos, rayDir), 1.0);
}
ENDCG
}
}
}