diff options
| author | yum <yum.food.vr@gmail.com> | 2025-10-28 21:33:40 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2025-10-28 21:33:40 -0700 |
| commit | 15f9778a65a0f163627bd229b8f212cc5c7c0c22 (patch) | |
| tree | b4b6c90defa76b9086b3b1f02bcd2a6401e019e7 | |
| parent | 6c93421c1a10caf9e4f6996e1109379a504c19a7 (diff) | |
some ray marching
| -rw-r--r-- | 3ner.cginc | 22 | ||||
| -rw-r--r-- | 3ner.shader | 2 | ||||
| -rw-r--r-- | globals.cginc | 1 | ||||
| -rw-r--r-- | interpolators.cginc | 6 | ||||
| -rw-r--r-- | math.cginc | 43 | ||||
| -rw-r--r-- | pema99.cginc | 2 | ||||
| -rw-r--r-- | ray_marching.cginc | 88 | ||||
| -rw-r--r-- | ray_marching_maps.slang | 89 |
8 files changed, 236 insertions, 17 deletions
@@ -14,6 +14,7 @@ #include "lighting.cginc" #include "globals.cginc" #include "interpolators.cginc" +#include "ray_marching.cginc" #include "vertex.cginc" v2f vert(appdata v) { @@ -21,7 +22,7 @@ v2f vert(appdata v) { return (v2f) asfloat(-1); #endif - v2f o; + v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_TRANSFER_INSTANCE_ID(v, o); @@ -34,12 +35,18 @@ v2f vert(appdata v) { o.uv01.zw = v.uv1; o.uv23.xy = v.uv2; o.uv23.zw = v.uv3; + float3 obj_space_camera_pos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0)); o.objPos = v.vertex; #if defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) || defined(_VERTEX_DEFORMATION_TESSELLATION) o.objPos_orig = v.vertex; #endif +#if defined(_RAY_MARCHING_BAKED_ORIGINS) + o.color = v.color; +#endif - // Normal and tangent are in object space. + // Normal and tangent are in object space to allow for object-space + // vertex deformation logic later on. They are converted to world + // space inside the fragment shader. o.normal = v.normal; o.tangent = v.tangent; #if !defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) @@ -178,6 +185,9 @@ v2f domain( #endif o.normal = DOMAIN_INTERP(normal); o.tangent = DOMAIN_INTERP(tangent); +#if defined(_RAY_MARCHING_BAKED_ORIGINS) + o.color = DOMAIN_INTERP(color); +#endif propagateObjPos(o); @@ -240,6 +250,14 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace) : SV_Target { return 0; #endif +#if defined(_RAY_MARCHING) + // TODO consider doing assignment inside function. It would clean up this + // call site. + RayMarchResult res = ray_march(i); + i.objPos = res.objPos; + i.normal = res.objNorm; + i.tangent.xyz = res.objTan; +#endif #if defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) deform_normal(i.objPos_orig, i.normal, i.tangent.xyz); #endif diff --git a/3ner.shader b/3ner.shader index 58d4d23..7feb967 100644 --- a/3ner.shader +++ b/3ner.shader @@ -61,7 +61,7 @@ Shader "yum_food/3ner" [HideInInspector] m_start_Ray_Marching_Baked_Origins("Baked Origins", Float) = 0 [ThryToggle(_RAY_MARCHING_BAKED_ORIGINS)] _Ray_Marching_Baked_Origins_Enabled("Enable", Float) = 0 - [IntRange] _Baked_Origins_UV_Channel_Index("UV Channel Index", Range(0, 3)) = 0 + [IntRange] _Baked_Origins_UV_Channel_Index("UV Channel Index", Range(0, 3)) = 1 [HideInInspector] m_end_Ray_Marching_Baked_Origins("Baked Origins", Float) = 0 //endex diff --git a/globals.cginc b/globals.cginc index a9f8f30..f671b73 100644 --- a/globals.cginc +++ b/globals.cginc @@ -114,7 +114,6 @@ float getTime() { #endif // _LOGICAL_TIME } - #if defined(_RAY_MARCHING_BAKED_ORIGINS) int _Baked_Origins_UV_Channel_Index; #endif // _RAY_MARCHING_BAKED_ORIGINS diff --git a/interpolators.cginc b/interpolators.cginc index 77b96a8..6611039 100644 --- a/interpolators.cginc +++ b/interpolators.cginc @@ -9,7 +9,9 @@ struct appdata { float2 uv1 : TEXCOORD1; float2 uv2 : TEXCOORD2; float2 uv3 : TEXCOORD3; +#if defined(_RAY_MARCHING_BAKED_ORIGINS) float4 color : COLOR; // vertex color +#endif float3 normal : NORMAL; float4 tangent : TANGENT; @@ -33,8 +35,8 @@ struct v2f { #if defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) || defined(_VERTEX_DEFORMATION_TESSELLATION) float3 objPos_orig : TEXCOORD9; #endif -#if defined(_RAYMARCH_BAKED_ORIGIN) - float3 color : TEXCOORD10; +#if defined(_RAY_MARCHING_BAKED_ORIGINS) + float4 color : TEXCOORD10; #endif UNITY_VERTEX_INPUT_INSTANCE_ID @@ -1,10 +1,21 @@ #ifndef __MATH_INC #define __MATH_INC -#define PI 3.14159265358979f -#define RCP_PI (1.0f / PI) -#define TAU (2.0f * PI) -#define SQRT_3 1.73205081f +#define PI 3.14159265358979323846264f +#define TAU (2.0f * PI) +#define HALF_PI (PI * 0.5f) +#define RCP_PI (1.0f / PI) +#define RCP_TAU (1.0f / TAU) +#define PHI 1.618033989f +#define RCP_PHI 0.618033989f +#define SQRT_2 1.414213562f +#define SQRT_2_RCP 0.707106781f +#define RCP_SQRT_2 0.707106781f +#define RCP_SQRT_3 0.577350269f +#define TWO_OVER_THREE 0.6666666666666666f +#define SQRT_3 1.73205081f +#define SQRT_3_OVER_2 0.8660254037844386f +#define EULERS_CONSTANT 2.718281828f float sin_noise_3d(float3 uvw) { return sin(uvw[0]) * sin(uvw[1]) * sin(uvw[2]); @@ -39,5 +50,29 @@ float wrapDotProduct(float XoY, float k) { } } +// Vector rotation with a quaternion +// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ +float3 rotate_vector(float3 v, float4 q) +{ + float3 t = 2.0 * cross(q.xyz, v); + return v + q.w * t + cross(q.xyz, t); +} + +// Cartesian to cube hexagonal coordinates. +// Based on this: https://backdrifting.net/post/064_hex_grids +float3 cart_to_hex(float2 cart) { + float p = cart.x; + float q = dot(cart, float2(0.5f, SQRT_3_OVER_2)); + float r = dot(cart, float2(0.5f, -SQRT_3_OVER_2)); + + return float3(p, q, r) * TWO_OVER_THREE; +} + +float2 hex_to_cart(float3 cart) { + return float2( + cart[0] + (cart[1] + cart[2]) * 0.5f, + (cart[1] - cart[2]) * SQRT_3_OVER_2); +} + #endif // __MATH_INC diff --git a/pema99.cginc b/pema99.cginc index b05f36e..96fba0d 100644 --- a/pema99.cginc +++ b/pema99.cginc @@ -25,6 +25,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y)))) + bool textureExists(texture2D tex) { int width, height; diff --git a/ray_marching.cginc b/ray_marching.cginc index 69f6438..e5ae03d 100644 --- a/ray_marching.cginc +++ b/ray_marching.cginc @@ -1,35 +1,109 @@ #ifndef __RAY_MARCHING_INC #define __RAY_MARCHING_INC +#include "math.cginc" +#include "ray_marching_maps.hlsl" +#include "texture_utils.cginc" + struct RayMarchResult { float3 objPos; float3 objNorm; + float3 objTan; }; +float3 getObjPos(v2f i) +{ +#if defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) || defined(_VERTEX_DEFORMATION_TESSELLATION) + return i.objPos_orig; +#else + return i.objPos; +#endif +} + +#if defined(_RAY_MARCHING_BAKED_ORIGINS) float3 GetFragToOrigin(v2f i) { return (i.color * 2.0f - 1.0f) / i.color.a; } - float4 GetRotation(v2f i, float2 uv_channels) { float4 quat; quat.xy = get_uv_by_channel(i, uv_channels.x); quat.zw = get_uv_by_channel(i, uv_channels.y); return quat; } - -RayMarchResult march_c31_00(v2f i) { +void GetRoRd(v2f i, out float3 ro, out float3 rd) { float3 obj_space_camera_pos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0)); float3 frag_to_origin = GetFragToOrigin(i); - float2 uv_channels = float2(_Custom30_Quaternion_UV_Channel_0, _Custom30_Quaternion_UV_Channel_1); + float2 uv_channels = float2(_Baked_Origins_UV_Channel_Index, _Baked_Origins_UV_Channel_Index+1); float4 quat = GetRotation(i, uv_channels); float4 iquat = float4(-quat.xyz, quat.w); - float3 ro = -frag_to_origin; - float3 rd = normalize(i.objPos - obj_space_camera_pos); + ro = -frag_to_origin; + rd = normalize(getObjPos(i) - obj_space_camera_pos); rd = rotate_vector(rd, iquat); +} +void GetObjPosNorm(v2f i, float3 lclPos, float3 lclNorm, float3 lclTang, + out float3 objPos, out float3 objNorm, out float3 objTan) { + float3 frag_to_origin = GetFragToOrigin(i); + float2 uv_channels = float2(_Baked_Origins_UV_Channel_Index, _Baked_Origins_UV_Channel_Index+1); + float4 quat = GetRotation(i, uv_channels); + float4 iquat = float4(-quat.xyz, quat.w); + + float3 objHit = rotate_vector(lclPos, quat); + float3 objCenterOffset = rotate_vector(frag_to_origin, quat); + objPos = objHit + (getObjPos(i) + objCenterOffset); + + objNorm = rotate_vector(lclNorm, quat); + objTan = rotate_vector(lclTang, quat); +} +#else +void GetRoRd(v2f i, out float3 ro, out float3 rd) { + ro = getObjPos(i); + float3 objCamPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); + float3 obj_space_camera_pos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0)); + rd = normalize(getObjPos(i) - obj_space_camera_pos); +} +void GetObjPosNorm(v2f i, float3 lclPos, float3 lclNorm, float3 lclTang, + out float3 objPos, out float3 objNorm, out float3 objTan) { + objPos = lclPos; + objNorm = lclNorm; + objTan = lclTang; +} +#endif // _RAY_MARCHING_BAKED_ORIGINS + +RayMarchResult ray_march(v2f i) { + float3 ro, rd; + GetRoRd(i, ro, rd); + + const float kMinDist = 1e-3; + // TODO dial this in & parameterize + const float kMaxDist = 10; + // TODO parameterize + const uint kMaxIter = 10; + float d_acc = 0; + for (uint ii = 0; ii < kMaxIter; ++ii) { + float3 p = ro + rd * d_acc; + float d_cur = map(p); + d_acc += d_cur; + if (d_cur < kMinDist) { + break; + } + if (d_acc > kMaxDist) { + break; + } + } + + // TODO clip / hit detection + float3 lclPos = ro + rd * d_acc; + float3 lclNorm; + float3 lclTang; + map_normal(lclPos, lclNorm, lclTang); + + RayMarchResult res; + GetObjPosNorm(i, lclPos, lclNorm, lclTang, + res.objPos, res.objNorm, res.objTan); - ro -= rd * _Custom30_ro_Offset; + return res; } #endif // __RAY_MARCHING_INC diff --git a/ray_marching_maps.slang b/ray_marching_maps.slang new file mode 100644 index 0000000..d98248b --- /dev/null +++ b/ray_marching_maps.slang @@ -0,0 +1,89 @@ +#ifndef __RAY_MARCHING_MAPS_INC +#define __RAY_MARCHING_MAPS_INC + +#include "math.cginc" +#include "pema99.cginc" + +// Macros for transforming normal and tangent using autodiff. +// r3r1 refers to "r3 to r1 transform", aka a mapping from a 3d real-valued +// space to a 1d space. This is intended for use with a ray marcher. +#define R3R1_DECLARE_BASIS_VECTORS(xyz) \ + DifferentialPair<float3> dp_x = diffPair(xyz, float3(1, 0, 0)); \ + DifferentialPair<float3> dp_y = diffPair(xyz, float3(0, 1, 0)); \ + DifferentialPair<float3> dp_z = diffPair(xyz, float3(0, 0, 1)) + +#define R3R1_AUTODIFF_BASIS_VECTORS(fun, ...) \ + DifferentialPair<float> dp_x_out = fwd_diff(fun)(dp_x, ##__VA_ARGS__); \ + DifferentialPair<float> dp_y_out = fwd_diff(fun)(dp_y, ##__VA_ARGS__); \ + DifferentialPair<float> dp_z_out = fwd_diff(fun)(dp_z, ##__VA_ARGS__) + +#define R3R1_DEFORM_NORMAL_AND_TANGENT(normal, tangent) \ + { \ + float3 gradient = float3(dp_x_out.d, dp_y_out.d, dp_z_out.d); \ + normal = normalize(gradient); \ + float3 helper = abs(normal.z) < 0.999 ? float3(0, 0, 1) : float3(0, 1, 0); \ + tangent = normalize(cross(helper, normal)); \ + } + +// Syntactic sugar - wraps the previous three macros. +#define R3R1_RAY_MARCH_NORMALS(xyz, normal, tangent, fun, ...) \ + R3R1_DECLARE_BASIS_VECTORS(xyz); \ + R3R1_AUTODIFF_BASIS_VECTORS(fun, ##__VA_ARGS__); \ + R3R1_DEFORM_NORMAL_AND_TANGENT(normal, tangent) + +float distance_from_hex_comb( + float3 p, + float3 period, + float count) { + float3 p_hex = cart_to_hex(p.yz); + + float3 half_period = period * 0.5; + float3 which = abs(floor((p_hex + half_period) / period)); + + // The original code here was this: + // p_hex = glsl_mod(p_hex + half_period, period) - half_period; + // + // But you can simplify it. Given the definition of glsl_mod: + // #define glsl_mod(x,y) (((x)-(y)*floor((x)/(y)))) + // + // You can plug in terms: + // (p_hex + half_period) - (period) * floor((p_hex + half_period) / period) + // = p_hex + half_period - period * floor(p_hex/period + 0.5) + // + // For all x, + // round(x) = floor(x + 0.5) + // + // Continuing to simplify: + // (p_hex + half_period - period * round(p_hex/period)) - half_period + // = p_hex - period * round(p_hex / period) + p_hex = p_hex - period * round(p_hex / period); + + p.yz = hex_to_cart(p_hex); + + float hex_d = length(p) - 0.15; + return hex_d; +} + +// Just trace a sphere of radius 0.1 for now. +[Differentiable] +public float map(float3 p) { + float period = 0.2f; + float3 count = float3(6,5,1); + float half_period = period * 0.5f; + float3 which = abs(floor((p + half_period) / period)); + if (any(abs(which) >= count)) { + p = 1e6; + } else { + p = glsl_mod(p + half_period, period) - half_period; + } + p.z += 0.1f; + return length(p) - 0.1f; +} + +public void map_normal(inout float3 xyz, inout float3 normal, + inout float3 tangent) { + R3R1_RAY_MARCH_NORMALS(xyz, normal, tangent, map); +} + +#endif // __RAY_MARCHING_MAPS_INC + |
