summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-10-28 21:33:40 -0700
committeryum <yum.food.vr@gmail.com>2025-10-28 21:33:40 -0700
commit15f9778a65a0f163627bd229b8f212cc5c7c0c22 (patch)
treeb4b6c90defa76b9086b3b1f02bcd2a6401e019e7
parent6c93421c1a10caf9e4f6996e1109379a504c19a7 (diff)
some ray marching
-rw-r--r--3ner.cginc22
-rw-r--r--3ner.shader2
-rw-r--r--globals.cginc1
-rw-r--r--interpolators.cginc6
-rw-r--r--math.cginc43
-rw-r--r--pema99.cginc2
-rw-r--r--ray_marching.cginc88
-rw-r--r--ray_marching_maps.slang89
8 files changed, 236 insertions, 17 deletions
diff --git a/3ner.cginc b/3ner.cginc
index da04aba..924e93e 100644
--- a/3ner.cginc
+++ b/3ner.cginc
@@ -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
diff --git a/math.cginc b/math.cginc
index 4cfed67..58a9c08 100644
--- a/math.cginc
+++ b/math.cginc
@@ -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
+