#ifndef __RAY_MARCHING_INC #define __RAY_MARCHING_INC #include "math.cginc" #include "ray_marching_maps.hlsl" #include "texture_utils.cginc" #include "vertex.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 } void GetRoRd(v2f i, out float3 ro, out float3 rd) { ro = getObjPos(i); float3 rd_world = i.worldPos.xyz - _WorldSpaceCameraPos; rd = normalize(mul(unity_WorldToObject, rd_world)); } float map(float3 p) { float d = 1e9; #if defined(_RAY_MARCHING_BALL) { float r = _Ray_Marching_Ball_Radius; d = min(d, map_ball(p, _Ray_Marching_Ball_Radius)); } #endif #if defined(_RAY_MARCHING_HEXAGON) { float r = _Ray_Marching_Hexagon_Radius; float h = _Ray_Marching_Hexagon_Height; d = min(d, map_hexagon(p, float2(r, h))); } #endif return d; } float domain_repeat(inout float3 p) { float d; #if defined(_RAY_MARCHING_CART_GRID) { float3 count = float3(_Ray_Marching_Cart_Grid_Count_X, _Ray_Marching_Cart_Grid_Count_Y, _Ray_Marching_Cart_Grid_Count_Z); float3 period = 1.0f / count; period *= 2; float3 half_period = period*0.5f; float3 which = floor((p + half_period) / period); if (any(abs(which) > count/2)) { p = 1e6; } else { p = glsl_mod(p + half_period, period) - half_period; } d = map(p); } #elif defined(_RAY_MARCHING_HEX_GRID) { const float3 count = _Ray_Marching_Hex_Grid_Count * 0.5f; const float3 period = 1.0f / count; const float3 hex = cart_to_hex(p.xy); float half_period = period * 0.5; float3 which = floor((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) const float3 hex_inst = hex - period * round_hex(hex / period); p.xy = (any(abs(which) > count/2) ? 1e9 : hex_to_cart(hex_inst)); d = map(p); } #else d = map(p); #endif return d; } void ray_march(inout v2f i) { #if defined(_RAY_MARCHING) float3 ro, rd; GetRoRd(i, ro, rd); #if defined(_VERTEX_DEFORMATION) // TODO optimize, we don't need to pass in `dummy`. { float3 dummy = 0; float3 tmp_pos = ro; undeform_normal(tmp_pos, dummy, rd); rd = normalize(rd); } #endif const float kMinDist = _Ray_Marching_Min_Dist; const float kMaxDist = _Ray_Marching_Max_Dist; const uint kMaxIter = _Ray_Marching_Max_Iter; float d_cur; float d_acc = 0; float3 hit_pos; for (uint ii = 0; ii < kMaxIter; ++ii) { float3 p = ro + rd * d_acc; d_cur = domain_repeat(p); #if defined(_RAY_MARCHING_OVERSTEP) d_cur *= (d_cur > 0 ? _Ray_Marching_Overstepping_Factor : 1.0f); #endif d_acc += d_cur; if (abs(d_cur) < kMinDist) { hit_pos = p; break; } if (d_acc > kMaxDist) { break; } } if (abs(d_cur) >= kMinDist) { discard; } float3 lclPos = hit_pos; float3 lclNorm; float3 lclTan; // TODO loop should say which primitive was hit, and be dispatched here. #if defined(_RAY_MARCHING_BALL) { float r = _Ray_Marching_Ball_Radius; map_ball_normal(r, lclPos, lclNorm, lclTan); } #elif defined(_RAY_MARCHING_HEXAGON) { float r = _Ray_Marching_Hexagon_Radius; float h = _Ray_Marching_Hexagon_Height; map_hexagon_normal(float2(r, h), lclPos, lclNorm, lclTan); } #endif #if defined(_VERTEX_DEFORMATION) { float3 tmp_pos = ro; deform_normal(tmp_pos, lclNorm, lclTan); } #endif i.objPos = lclPos; i.normal = lclNorm; i.tangent.xyz = lclTan; #endif // _RAY_MARCHING } #endif // __RAY_MARCHING_INC