Last active
March 7, 2021 09:02
-
-
Save rngtm/987133d7c19d5643412ad97ad2d92e1c to your computer and use it in GitHub Desktop.
レイマーチングで球をたくさん表示するシェーダーグラフカスタムノード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection; | |
using UnityEditor.ShaderGraph; | |
using UnityEngine; | |
/// <summary> | |
/// レイマーチングで球をたくさん表示するカスタムノード | |
/// </summary> | |
[Title ("Raymarching", "Raymarch Sphere")] | |
public class RaymarchingSphereNode : CodeFunctionNode { | |
public RaymarchingSphereNode () { | |
name = "Raymarching(Sphere)"; | |
} | |
protected override MethodInfo GetFunctionToConvert () { | |
return GetType ().GetMethod ("RaymarchingNode_Function", | |
BindingFlags.Static | BindingFlags.NonPublic); | |
} | |
public override void GenerateNodeFunction (FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode) { | |
registry.ProvideFunction ("distance_func", s => s.Append (@" | |
// 距離関数: 点pからオブジェクトまでの距離を求める | |
#define INTERVAL interval | |
float distance_func(float3 p, float size, float interval) { | |
p = frac(p / INTERVAL) * INTERVAL - INTERVAL / 2.0; // -INTERVAL/2.0 ~ +INTERVAL/2.0 の繰り返しを作る | |
return length(p) - size; | |
} | |
")); | |
registry.ProvideFunction ("getNormal", s => s.Append (@" | |
// 法線の計算 | |
float3 getNormal(float3 p, float size, float interval) { | |
float2 e = float2(0.0001, 0.0); | |
return normalize(float3( | |
distance_func(p + e.xyy, size, interval) - distance_func(p - e.xyy, size, interval), | |
distance_func(p + e.yxy, size, interval) - distance_func(p - e.yxy, size, interval), | |
distance_func(p + e.yyx, size, interval) - distance_func(p - e.yyx, size, interval) | |
)); | |
} | |
")); | |
base.GenerateNodeFunction (registry, graphContext, generationMode); | |
} | |
static string RaymarchingNode_Function ( | |
[Slot (0, Binding.MeshUV0)] Vector2 UV, | |
[Slot (1, Binding.None, 0f, 0f, 4f, 0f)] Vector3 CameraPos, // カメラ位置 | |
[Slot (2, Binding.None, 0f, 0f, -1f, 0f)] Vector3 CameraDir, // カメラの向きベクトル | |
[Slot (3, Binding.None, 0f, 1f, 0f, 0f)] Vector3 CameraUp, // カメラの上方向ベクトル | |
[Slot (4, Binding.None, 1f, 0f, 0f, 0f)] Vector1 ObjectSize, // 球のサイズ | |
[Slot (5, Binding.None, 2f, 0f, 0f, 0f)] Vector1 ObjectInterval, // 球の配置間隔 | |
[Slot (6, Binding.None, 32f, 0f, 0f, 0f)] Vector1 RaymarchLoop, // レイマーチングのループ回数(この数を大きくすると遠くまで描画されるようになりますが重くなります) | |
[Slot (7, Binding.None, 0f, 0f, 0f, 0f)] Vector1 RayStartLength, | |
[Slot (8, Binding.None, 1f, 1f, 1f, 0f)] Vector3 LightDir, | |
[Slot (10, Binding.None)] out Vector1 Hit, // レイがオブジェクトにぶつかったら1.0, ぶつからなかったら0.0 | |
[Slot (11, Binding.None)] out Vector1 Distance, // レイマーチングでレイが進んだ距離 | |
[Slot (12, Binding.None)] out Vector3 Normal, // オブジェクト上の法線 | |
[Slot (13, Binding.None)] out Vector1 Specular, // オブジェクト上の法線 | |
[Slot (14, Binding.None)] out Vector3 RayPos // レイの位置 | |
) { | |
Normal = Vector3.zero; | |
RayPos = Vector3.zero; | |
return @"{ | |
#define MAX_REPEAT 100 | |
float2 p = UV - 0.5; | |
// カメラに関する情報(Position, Direction, Up) | |
#define cPos CameraPos | |
#define cDir normalize(CameraDir) | |
#define cUp normalize(CameraUp) | |
#define cSide normalize(cross(cUp, cDir)) | |
// レイマーチング | |
float3 ray = normalize(p.x * cSide + p.y * cUp + 1.0 * cDir); // レイの向きベクトル | |
float3 rPos = cPos; // レイ位置 | |
float rLength = RayStartLength;// レイが進む長さ | |
float dist = 0.0; // レイとオブジェクト間の距離 | |
for (int i = 0; i < min(RaymarchLoop, MAX_REPEAT); i++) | |
{ | |
dist = distance_func(rPos, ObjectSize, ObjectInterval); // レイ位置からオブジェクトまでの距離を求める | |
rLength += dist; // 距離を足す(レイを進める) | |
rPos = cPos + ray * rLength; // レイ位置の更新 | |
} | |
Hit = step(dist, 0.01); // レイがオブジェクトにある程度近かったら1.0を出力、それ以外は0.0を出力 | |
Distance = rLength; // レイが進んだ距離を出力 | |
Normal = saturate(getNormal(rPos, ObjectSize, ObjectInterval)); // レイの交点におけるオブジェクト上の法線を出力 | |
RayPos = rPos; | |
#define viewDir ray | |
#define lightDir LightDir | |
float NdotL = max(0, dot (Normal, lightDir)); | |
float3 R = normalize( - lightDir + 2.0 * Normal * NdotL ); | |
Specular = max(0, dot(R, viewDir)); | |
}"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
カメラの位置と向きを指定してレイマーチングできるカスタムノードです。
Distanceは1.0以上の値が返ってくることが多いのでReciprocalノードを使って逆数をとると扱いやすいと思います。