cvr-props/Assets/test/RayMarchLib.cginc

1044 lines
25 KiB
HLSL

//double include guard
#ifndef RAY_MARCH_LIB_INCLUDED
#define RAY_MARCH_LIB_INCLUDED
#include "UnityCG.cginc"
// Multi compile stuff
#define V_X float3(1, 0, 0)
#define V_Y float3(0, 1, 0)
#define V_Z float3(0, 0, 1)
#define V_XZ float3(1, 0, 1)
#define V_XY float3(1, 1, 0)
#define V_YZ float3(0, 1, 1)
//ambient occlusion quality
#ifndef AO_STEPS
#define AO_STEPS 5
#endif
//normals for lighting
#ifndef NORMAL_DELTA
#define NORMAL_DELTA 0.001
#endif
//normals for reflection angles
#ifndef REFL_NORMAL_DELTA
#define REFL_NORMAL_DELTA 0.001
#endif
#ifndef MAX_REFLECTIONS
#define MAX_REFLECTIONS 2
#endif
#ifdef USE_DYNAMIC_QUALITY//quality settings as unity material properties
int _MaxSteps = 100;
float _MaxDist = 100;
float _SurfDist = 0.00001;
#else//pre compile quality settings
#ifndef MAX_STEPS
#define MAX_STEPS 256
//256
#endif
#ifndef MAX_DIST
#define MAX_DIST 128
#endif
#ifndef SURF_DIST
#define SURF_DIST 0.0001
//#define SURF_DIST 0.00001
#endif
#endif
#define col(r, g, b) fixed4(r, g, b, 1)
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 vCamPos : TEXCOORD1;
float3 vHitPos : TEXCOORD2;
};
struct fragOut
{
fixed4 col : SV_Target;
float depth : SV_Depth;
};
typedef struct material
{
fixed4 col;
fixed fRough;
} material_t;
#define DEFMAT {fixed4(.2,.2,.2,1), 1}
#define M_RED {fixed4(0.2, 0.001, 0.001, 1), 1}
#define M_ORANGE {fixed4(0.2, 0.1, 0.001, 1), 1}
#define M_YELLOW {fixed4(0.2, 0.2, 0.001, 1), 1}
#define M_GREEN {fixed4(0.001, 0.2, 0.001, 1), 1}
#define M_BLUE {fixed4(0.001, 0.001, 0.2, 1), 1}
#define M_LIGHT_BLUE{fixed4(0.001, 0.05, 0.2, 1), 1}
#define M_MAGENTA {fixed4(0.2, 0.001, 0.2, 1), 1}
#define M_PURPLE {fixed4(0.05, 0.001, 0.2, 1), 1}
#define M_WHITE {fixed4(0.5, 0.5, 0.5, 1), 1}
#define M_MIRROR {fixed4(0.1, 0.1, 0.1, 1), 0}
inline material mat(float r, float g, float b, float fRough = 1)
{
material m = {fixed4(r, g, b, 1), fRough};
return m;
}
inline material mat(float3 rgb, float fRough = 1)
{
material m = {fixed4(rgb, 1), fRough};
return m;
}
//used for lighting a point
struct rayData
{
float dist;
int iSteps;
material mat;
float3 vRayStart;
float3 vRayDir;
float3 vHit;
fixed3 vNorm;
bool bMissed;
float minDist;
float distToMinDist;
};
//returned from distance functions, including main scene
struct sdfData
{
float dist;
material mat;
};
sdfData scene(float3 p);
fixed4 lightPoint(rayData r);
fixed4 rayMarch(float3 p, float3 d);
rayData castRay(float3 p, float3 d, float startDist = 0);
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
#ifdef USE_WORLD_SPACE
o.vCamPos = _WorldSpaceCameraPos;
o.vHitPos = mul(unity_ObjectToWorld, v.vertex);
#else
o.vCamPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
o.vHitPos = v.vertex;
#endif
return o;
}
#ifdef USE_REFLECTIONS
#define CALC_NORM
fragOut frag (v2f i)
{
float fRayLen = 0;//since last bounce
#ifdef CONSTRAIN_TO_MESH
float3 vLastBounce = i.vHitPos;
fRayLen += length(i.vHitPos - i.vCamPos);
#else
float3 vLastBounce = i.vCamPos;
#endif
float3 vRayDir = normalize(i.vHitPos - i.vCamPos);//current direction
sdfData point_data;
rayData ray;
fixed4 col;
float colUsed = 0;// what amount of the final colour has been calculated
float prevRough = 0;
float3 vFirstHit;
for (int i = 0; i < MAX_REFLECTIONS+1; i++)
{
ray = castRay(vLastBounce, vRayDir);
if (i == 0)
{//before any bounces
col = lightPoint(ray);
vFirstHit = ray.vHit;
}
else
{
float colAmt = colUsed + (prevRough * (1-colUsed));
col = lerp(lightPoint(ray), col, colAmt);
colUsed = colAmt;
}
if (ray.bMissed || ray.mat.fRough > 0.99)
{
break;
}
prevRough = ray.mat.fRough;
vRayDir = reflect(vRayDir, ray.vNorm);
vLastBounce = ray.vHit + vRayDir * 0.01;
}
#ifdef DISCARD_ON_MISS
if (ray.bMissed && i == 0) discard;
#endif
fragOut o;
o.col = col;
#ifdef USE_WORLD_SPACE
float4 vClipPos = mul(UNITY_MATRIX_VP, float4(vFirstHit, 1));
#else
float4 vClipPos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(vFirstHit, 1)));
#endif
o.depth = vClipPos.z / vClipPos.w;
#if !defined(UNITY_REVERSED_Z) // basically only OpenGL (unity editor on linux)
o.depth = o.depth * 0.5 + 0.5; // remap -1 to 1 range to 0.0 to 1.0
#endif
return o;
}
#else
fragOut frag (v2f i)
{
float3 vRayDir = normalize(i.vHitPos - i.vCamPos);
#ifdef CONSTRAIN_TO_MESH
//rayData ray = castRay(i.vHitPos, vRayDir, length(i.vHitPos-i.vCamPos));
rayData ray = castRay(i.vCamPos, vRayDir, length(i.vHitPos-i.vCamPos));
//rayData ray = castRay(i.vCamPos, vRayDir, 1);
//rayData ray = castRay(i.vCamPos, vRayDir, 0);
#else
rayData ray = castRay(i.vCamPos, vRayDir);
#endif
#ifdef DISCARD_ON_MISS
if (ray.bMissed) discard;
#endif
fragOut o;
o.col = lightPoint(ray);
// writing to depth buffer costs about 1-2 frames at 4k
#ifdef USE_WORLD_SPACE
float4 vClipPos = mul(UNITY_MATRIX_VP, float4(ray.vHit, 1));
#else
float4 vClipPos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(ray.vHit, 1)));
#endif
o.depth = (vClipPos.z / vClipPos.w + 1.0) * 0.5;
return o;
}
#endif
//gets normal of a point
inline float3 getNormFull(float3 vPos, float fEpsilon = 0.001)
{
//if epsilon is smaller than 0.001, there are often artifacts
const float2 e = float2(fEpsilon, 0);
float3 n = scene(vPos).dist - float3(
scene(vPos - e.xyy).dist,
scene(vPos - e.yxy).dist,
scene(vPos - e.yyx).dist);
return normalize(n);
}
//gets normal, provided you have the distance for pos (1 less call to scene())
inline float3 getNorm(float3 vPos, float fPointDist, float fEpsilon = 0.001)
{
////if epilon is smaller than 0.001, there are often artifacts
const float2 e = float2(fEpsilon, 0);
float3 n = fPointDist - float3(
scene(vPos - e.xyy).dist,
scene(vPos - e.yxy).dist,
scene(vPos - e.yyx).dist);
return normalize(n);
}
//marches a ray through the scene once
rayData castRay(float3 vRayStart, float3 vRayDir, float startDist)
{
float fRayLen = startDist;//startDist;// total distance marched / distance from camera
float3 vPos;
sdfData sdf_data;
rayData ray;
ray.vRayDir = vRayDir;
ray.vRayStart = vRayStart;
ray.minDist = 30000.0;// budget "infinity"
ray.distToMinDist = 0;
#ifdef USE_DYNAMIC_QUALITY
for (int i = 0; i < _MaxSteps; i++)
#else
for (int i = 0; i < MAX_STEPS; i++)
#endif
{
vPos = vRayStart + fRayLen * vRayDir;
sdf_data = scene(vPos);
#ifdef USE_DYNAMIC_QUALITY
if (abs(sdf_data.dist) < _SurfDist) break;
#else
if (abs(sdf_data.dist) < SURF_DIST) break;
#endif
fRayLen += sdf_data.dist;// move forward
if (ray.minDist>sdf_data.dist)
{
ray.minDist = sdf_data.dist;
ray.distToMinDist = fRayLen;
}
#ifdef USE_DYNAMIC_QUALITY
if (fRayLen > _MaxDist) {ray.bMissed = true; break;}//flag this as transparent/sky
#else
if (fRayLen > MAX_DIST) {ray.bMissed = true; break;}//flag this as transparent/sky
#endif
}
ray.dist = fRayLen;
ray.iSteps = i;
ray.mat = sdf_data.mat;
ray.vHit = vPos;
#ifdef CALC_NORM
ray.vNorm = getNorm(vPos, sdf_data.dist);
#endif
return ray;
}
//////////////////////////////////////////////////////////////////////
//
// Lighting
//
//////////////////////////////////////////////////////////////////////
//generates a skybox, use when ray didn't hit anything (ray_data.bMissed)
inline fixed4 sky(float3 vRayDir)
{
float4 cRenderedSun = max(0, pow(dot(vRayDir, normalize(float3(8,4,2))) + 0.4, 10)-28) * float4(.8,.4,0,1);
return fixed4(0.7, 0.75, 0.8, 1) - abs(vRayDir.y) * 0.5 + cRenderedSun;
}
//calculate sun light based on normal
fixed4 lightSun(float3 vNorm, float3 vSunDir = float3(8, 4, 2), fixed4 cSunCol = fixed4(7.0, 5.5, 3.0, 1))
{
float fSunLight = max(dot(vNorm, vSunDir), 0);
return fSunLight * cSunCol;
}
//calculate shadow from sun
float lightShadow(float3 vPos, float3 vSunDir, float fSharpness = 8)
{
float fShadow = 1;
#ifdef USE_DYNAMIC_QUALITY
for (float fRayLen = 0.001; fRayLen < _MaxDist/2.0;)
#else
for (float fRayLen = 0.001; fRayLen < MAX_DIST/2.0;)
#endif
{
float dist = scene(vPos + vSunDir * fRayLen).dist;
#ifdef USE_DYNAMIC_QUALITY
if (dist < _SurfDist) return 0;
#else
if (dist < SURF_DIST) return 0;
#endif
fShadow = min(fShadow, fSharpness * dist/fRayLen);
fRayLen += dist;
}
return fShadow;
}
//calculate sky light
inline fixed4 lightSky(float3 vNorm, fixed4 cSkyCol = fixed4(0.5, 0.8, 0.9, 1))
{
return cSkyCol * (0.5 + 0.5 * vNorm.y);
}
//bad ambient occlusion (screen space) based on steps
float lightSSAO(rayData ray_data, float fDarkenFactor = 2)
{
#ifdef USE_DYNAMIC_QUALITY
return pow(1 - float(ray_data.iSteps) / _MaxSteps, fDarkenFactor);
#else
return pow(1 - float(ray_data.iSteps) / MAX_STEPS, fDarkenFactor);
#endif
}
//ambient occlusion
float lightAO(float3 vPos, float3 vNorm, float fEpsilon = 0.05)
{
float ao = 0;
for (int i = 0; i < AO_STEPS; i++)
{
float fOffset = i * fEpsilon;
float fDist = scene(vPos + vNorm * fOffset).dist;
ao += 1/pow(2, i) * (fOffset - fDist);
}
ao = 1 - AO_STEPS * ao;
return ao;
}
inline fixed4 lightFog(fixed4 col, fixed4 cFog, float fDist, float fStart=16, float fFull=32)
{
if (fDist < 0) return cFog;
return lerp(col, cFog, smoothstep(fStart, fFull, fDist));
}
//a light pass for debugging
fixed4 lightOnly(float3 vPos, float3 vNorm, float3 vSunDir)
{
float fLight = lightSun(vNorm, vSunDir, 1);
float fAO = lightAO(vPos, vNorm);
float fShadow = lightShadow(vPos, vSunDir);
return fLight * fAO * fShadow;
}
//////////////////////////////////////////////////////////////////////
//
// Interpolation and Math
//
//////////////////////////////////////////////////////////////////////
//soft min of a and b with smoothing factor k
inline float smin(float a, float b, float k)
{
float h = max(k - abs(a-b), 0) / k;
return min(a, b) - h*h*h*k * 1/6.0;
}
//soft max of a and b with smoothing factor k
inline float smax(float a, float b, float k)
{
float h = max(k - abs(a - b), 0) / k;
return max(a, b) + h*h*h*k * 1/6.0;
}
//interpolate between the colours of 2 SDFs
inline material mixMat(sdfData sdfA, sdfData sdfB)
{
material m;
float fac = clamp(sdfA.dist/(sdfA.dist + sdfB.dist), 0, 1);
m.col = lerp(sdfA.mat.col, sdfB.mat.col, fac);
m.fRough = lerp(sdfA.mat.fRough, sdfB.mat.fRough, fac);
return m;
}
//interpolate between the colours of 2 SDFs
inline material mixMat(material a, material b, float fac)
{
material m;
m.col = lerp(a.col, b.col, fac);
m.fRough = lerp(a.fRough, b.fRough, fac);
return m;
}
// from: https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h
// modified to be more "optimized" (WAY worse approximations)
static const float fsl_PI = 3.1415926535897932384626433f;
static const float fsl_PI_half = fsl_PI/2;
inline float acosFast4(float inX)
{
return 1.57-inX;
float x1 = abs(inX);
//float x2 = x1 * x1;
//float x3 = x2 * x1;
float s;
s = -0.2121144f * x1 + 1.5707288f;
//s = 0.0742610f * x2 + s;
//s = -0.0187293f * x3 + s;
s = sqrt(1.0f - x1) * s;
// acos function mirroring
// check per platform if compiles to a selector - no branch neeeded
return s;
//return inX >= 0.0f ? s : fsl_PI - s;
}
// polynomial degree 2
// Tune for positive input [0, infinity] and provide output [0, PI/2]
inline float ATanPos(float x)
{
const float C1 = 1.01991;
const float C2 = -0.218891;
float t0 = (x < 1.0f) ? x : 1.0f / x;
float t1 = (C2 * t0 + C1) * t0; // p(x)
return t1;//return (x < 1.0f) ? t1: fsl_PI_half - t1; // undo range reduction
}
// Common function, ATanPos is implemented below
// input [-infinity, infinity] and output [-PI/2, PI/2]
inline float ATan(float x)
{
float t0 = ATanPos(abs(x));
return t0;//(x < 0.0f) ? -t0: t0; // undo range reduction
}
inline float atanFast4(float inX)
{
//return atan(inX);
return ATan(inX);
float x = inX;
return x*(-0.1784f * abs(x) - 0.0663f * x * x + 1.0301f);
}
// https://en.wikipedia.org/wiki/Atan2#Definition_and_computation
inline float atanFast4_2(float y, float x)
{
//return sign(x)*sign(x)*atanFast4(y/x)+((1-sign(x))/2)*(1-sign(y)-sign(y)*sign(y))*fsl_PI;
return atanFast4(y/x)+(1-sign(x))*(sign(y))*fsl_PI/2;
}
//////////////////////////////////////////////////////////////////////
//
// SDF operations
//
//////////////////////////////////////////////////////////////////////
//union of SDF A and B
sdfData sdfAdd(float3 p, sdfData sA, sdfData sB)
{
sdfData sC;
sC.dist = min(sA.dist, sB.dist);
sC.mat = mixMat(sA, sB);
return sC;
}
//union of SDF A and B, with smoothing
sdfData sdfAdd(float3 p, sdfData sA, sdfData sB, float fSmooth)
{
sdfData sC;
sC.dist = smin(sA.dist, sB.dist, fSmooth);
sC.mat = mixMat(sA, sB);
return sC;
}
//remove the SDF B from A (colour is from A)
sdfData sdfSub(float3 p, sdfData sA, sdfData sB)
{
sdfData sC;
sC.dist = max(sA.dist, -sB.dist);
sC.mat = sA.mat;
return sC;
}
//remove the SDF B from A (colour is from A), with smoothing
sdfData sdfSub(float3 p, sdfData sA, sdfData sB, float fSmooth)
{
sdfData sC;
sC.dist = smax(sA.dist, -sB.dist, fSmooth);
sC.mat = sA.mat;
return sC;
}
//intersection of SDF A and B
sdfData sdfInter(float3 p, sdfData sA, sdfData sB)
{
sdfData sC;
sC.dist = max(sA.dist, sB.dist);
sC.mat = mixMat(sA, sB);
return sC;
}
//intersection of SDF A and B, with smoothing
sdfData sdfInter(float3 p, sdfData sA, sdfData sB, float fSmooth)
{
sdfData sC;
sC.dist = smax(sA.dist, sB.dist, fSmooth);
sC.mat = mixMat(sA, sB);
return sC;
}
//round edges of an SDF
sdfData sdfRound(float3 p, sdfData sdfIn, float fRadius)
{
sdfData sdfOut = sdfIn;
sdfOut.dist -= fRadius;
return sdfOut;
}
//////////////////////////////////////////////////////////////////////
//
// SDF shapes
//
//////////////////////////////////////////////////////////////////////
//create sphere
sdfData sdfSphere(float3 p, float fRadius, material mat = DEFMAT)
{
sdfData sdf;
sdf.dist = length(p) - fRadius;
sdf.mat = mat;
return sdf;
}
//create plane pointing to positive Y
sdfData sdfPlane(float3 p, float fHeight, material mat = DEFMAT)
{
sdfData sdf;
sdf.dist = p.y - fHeight;
sdf.mat = mat;
return sdf;
}
//create plane with normal
sdfData sdfPlane(float3 p, float3 vNorm, float fHeight, material mat = DEFMAT)
{
sdfData sdf;
sdf.dist = dot(p, normalize(vNorm)) - fHeight;
sdf.mat = mat;
return sdf;
}
//create cuboid
sdfData sdfBox(float3 p, float3 vDim, material mat = DEFMAT)
{
sdfData sdf;
float3 q = abs(p) - vDim/2.0;
sdf.dist = length(max(q, 0)) + min(max(q.x, max(q.y, q.z)), 0);
sdf.mat = mat;
return sdf;
}
//create cuboid
sdfData sdfBox(float3 p, float3 vDim, float fRound, material mat = DEFMAT)
{
sdfData sdf;
float3 q = abs(p) - vDim/2.0;
sdf.dist = length(max(q, 0)) + min(max(q.x, max(q.y, q.z)), 0) - fRound;
sdf.mat = mat;
return sdf;
}
//create line segment
sdfData sdfLine(float3 p, float3 vStart, float3 vEnd, float fRadius, material mat = DEFMAT)
{
sdfData sdf;
float h = min(1, max(0, dot(p-vStart, vEnd-vStart) / dot(vEnd-vStart, vEnd-vStart)));
sdf.dist = length(p-vStart-(vEnd-vStart)*h)-fRadius;
sdf.mat = mat;
return sdf;
}
//create cylinder
sdfData sdfCylinder(float3 p, float fRadius, float fHeight, material mat = DEFMAT)
{
sdfData sdf;
sdf.dist = max(abs(p.y) - fHeight/2.0, length(p.xz) - fRadius);
sdf.mat = mat;
return sdf;
}
//create cylinder
sdfData sdfCylinder(float3 p, float fRadius, float fHeight, float fRound, material mat = DEFMAT)
{
sdfData sdf;
sdf.dist = max(abs(p.y) - fHeight/2.0, length(p.xz) - fRadius) - fRound;
sdf.mat = mat;
return sdf;
}
//create torus
sdfData sdfTorus(float3 p, float fRadius, float fThickness, material mat = DEFMAT)
{
sdfData sdf;
float2 q = float2(length(p.xz) - fRadius, p.y);
sdf.dist = length(q) - fThickness;
sdf.mat = mat;
return sdf;
}
//triangular prism (BOUND)
sdfData sdfTriPrism(float3 p, float fSide, float fDepth, material mat = DEFMAT)
{
float3 q = abs(p);
sdfData sdf;
sdf.dist = max(q.z - fDepth, max(q.x * 0.866025 + p.y * 0.5, -p.y) - fSide * 0.5);
sdf.mat = mat;
return sdf;
}
//////////////////////////////////////////////////////////////////////
//
// Fractals, complex shapes and scenes (frac prefix)
//
//////////////////////////////////////////////////////////////////////
//TODO:
// complex :julia,
// simple sierpinsky, menger
// Mandelbolb - OPTIMIZED AF, still a fractal but visually diffrent.
sdfData fracMandelbolb(float3 p, material mat = DEFMAT)
{
// http://blog.hvidtfeldts.net/index.php/2011/09/distance-estimated-3d-fractals-v-the-mandelbulb-different-de-approximations/
float3 pos;
pos.x = p.x;
pos.y = p.y;
pos.z = p.z;
float dr = 1.0;
float r = 0;
const int iterations = 4;
const float maxRThreshold = 2;//2;
const float Power = 16;
for (int i = 0; i < iterations; i++)
{
r = length(p);
if (r>maxRThreshold) break;
// xyz -> polar
//float theta = acos( p.z / r );
float theta = acosFast4( p.z / r );
//float phi = atan2( p.y, p.x );
float phi = atanFast4_2( p.y, p.x );
dr = pow( r, Power-1.0)*Power*dr + 1.0;
// transform point
float zr = pow( r, Power );
theta = theta * Power;
phi = phi * Power;
// polar -> xyz
p = zr*float3(sin(theta)*cos(phi), sin(phi)*sin(theta), cos(theta));
p += pos;
}
sdfData sdf;
sdf.mat = mat;
sdf.dist = 0.5*log(r)*r/dr;
return sdf;
}
// Mandelbulb
sdfData fracMandelbulb(float3 p, material mat = DEFMAT)
{
// http://blog.hvidtfeldts.net/index.php/2011/09/distance-estimated-3d-fractals-v-the-mandelbulb-different-de-approximations/
float3 pos;
pos.x = p.x;
pos.y = p.y;
pos.z = p.z;
float dr = 1.0;
float r = 0;
// Lowest number of iterations without loosing a significant amount of detail
// Depends on maxRThreshold
//int iterations = 1;
//int iterations = 8;
const int iterations = 5;
//float maxRThreshold = 2;
const float maxRThreshold = 2;
// Z_(n+1) = Z(n)^?
// float Power = 8 + 6 * sin(_Time.x);
float Power = 8;
for (int i = 0; i < iterations; i++)
{
r = length(p);
if (r>maxRThreshold) break;
// xyz -> polar
float theta = acos( p.z / r );
float phi = atan2( p.y, p.x );
dr = pow( r, Power-1.0)*Power*dr + 1.0;
// transform point
float zr = pow( r, Power );
theta = theta * Power;
phi = phi * Power;
// polar -> xyz
p = zr*float3(sin(theta)*cos(phi), sin(phi)*sin(theta), cos(theta));
p += pos;
}
sdfData sdf;
sdf.mat = mat;
//sdf.mat.col.y = sin(p.x);
//sdf.dist = sdfSphere(pos, 10).dist;
//sdf.mat = mat;
sdf.dist = 0.5*log(r)*r/dr;
//sdf.mat = mat;
return sdf;
}
void sphereFold(inout float3 p, inout float dz, float minRadius, float fixedRadius);
void boxFold(inout float3 p, float dz, float foldingLimit);
// Mandelbox
sdfData fracMandelbox(float3 p, float scaleFactor, material mat = DEFMAT)
{
// http://blog.hvidtfeldts.net/index.php/2011/11/distance-estimated-3d-fractals-vi-the-mandelbox/
float3 offset = p;
float dr = 0;
// Parameters
int iterations = 8;//20;//14;
//scaleFactor = -2 + (_SinTime.x*4+2);
float fixedRadius = 1.0;
float minRadius = 0.5;
/*float foldingLimit = 0.2 + _SinTime.x/4 + 0.25;
float minRadius = 0.07;
float fixedRadius = 0.2;*/
//float scaleFactor = -0.8;
/*float foldingLimit = _FoldingLimit;
float minRadius = _MinRadius;
float fixedRadius = _FixedRadius;*/
for(int i=0; i<iterations; i++)
{
boxFold(p, dr, 1);
sphereFold(p, dr, minRadius, fixedRadius);
p = scaleFactor*p + offset;
//dr = dr*abs(scaleFactor)+1.0;
dr = dr*abs(scaleFactor)+1;
}
sdfData sdf;
sdf.mat = mat;
float r = length(p);
sdf.dist = r/abs(dr);
return sdf;
}
// Mandelbox alternate implementation, possibly faster
sdfData fracMandelbox2(float3 p, float foldingLimit, float minRadius, float fixedRadius, float scaleFactor, material mat = DEFMAT)
{
// http://www.fractalforums.com/3d-fractal-generation/a-mandelbox-distance-estimate-formula/
float scale = -2;
int iterations = 10;
float DEfactor;
for (int i = 0; i<iterations; i++)
{
DEfactor = scale;
fixedRadius = 1.0;
float fR2 = fixedRadius*fixedRadius;
minRadius = 0.5;
float mR2 = minRadius*minRadius;
// Box fold?
if (p.x > 1.0)
p.x = 2.0 - p.x;
else if (p.x < -1.0) p.x = -2.0 - p.x;
if (p.y > 1.0)
p.y = 2.0 - p.y;
else if (p.y < -1.0) p.y = -2.0 - p.y;
if (p.z > 1.0)
p.z = 2.0 - p.z;
else if (p.z < -1.0) p.z = -2.0 - p.z;
// radius squared
float r2 = dot(p,p);
if (r2 < mR2)
{
p*=(fR2/mR2);
DEfactor*=(fR2/mR2);
}
else if (r2 < fR2)
{
p*=(fR2/r2);
DEfactor*=(fR2/r2);
}
p=p*scale+1;
DEfactor*=scale;
}
sdfData sdf;
sdf.mat = mat;
sdf.dist = length(p)/abs(DEfactor);
return sdf;
}
// Feather
sdfData fracFeather(float3 p, float cx = 2.0, float cy = 2.7, float cz = 1.4, material mat=DEFMAT)
{
// https://fractalforums.org/index.php?action=gallery;sa=view;id=5732
int iterations = 5;
//float cx = 2.0;
//float cy = 2.7;
//float cz = 1.4;
float cw = 0.1;
float dx = 1.5;// + _FoldingLimit-0.5;
float lp,r2,s = 1;
float icy = 1.0 / cy;
float3 p2,cy3 = float3(cy,cy,cy);
for (int i=0; i<iterations; i++) {
p -= cx * round(p / cx);
p2 = pow(abs(p),cy3);
lp = pow(p2.x + p2.y + p2.z, icy);
r2 = dx / max( pow(lp,cz), cw);
p *= r2;
s *= r2;
}
sdfData o;
o.mat = mat;
o.dist = length(p)/s-.001;
return o;
}
//////////////////////////////////////////////////////////////////////
//
// Transforms
//
//////////////////////////////////////////////////////////////////////
// rotate point p around origin, a radians
float3 rotX(float3 p, float a)
{
return mul(float3x3(1, 0, 0, 0, cos(a), -sin(a), 0, sin(a), cos(a)), p);
}
// rotate point p around origin, a radians
float3 rotY(float3 p, float a)
{
return mul(float3x3(cos(a), 0, sin(a), 0, 1, 0, -sin(a), 0, cos(a)), p);
}
// rotate point p around origin, a radians
float3 rotZ(float3 p, float a)
{
return mul(float3x3(cos(a), -sin(a), 0, sin(a), cos(a), 0, 0, 0, 1), p);
}
// repeats space every r units, centered on the origin
inline float3 repXYZ(float3 p, float3 r)
{
float3 o = p;
o = fmod(abs(p + r/2.0), r) - r/2.0;
o *= sign(o);
return o;
}
// repeats space every r units, centered on the origin, no sign
inline float3 repXYZUnsigned(float3 p, float3 r)
{
return fmod(abs(p + r/2.0), r) - r/2.0;
}
// repeats space every r units, centered on the origin
inline float3 repXZ(float3 p, float x, float z)
{
float3 o = p;
o.x = fmod(abs(p.x) + x/2.0, x) - x/2.0;
o.x *= sign(p.x);
o.z = fmod(abs(p.z) + z/2.0, z) - z/2.0;
o.z *= sign(p.z);
return o;
}
// Reflect point if inside/outside sphere
void sphereFold(
inout float3 p,
inout float dz,
float minRadius,
float fixedRadius)
{
float r2 = dot(p,p);
float r = length(p);
if (r<minRadius)
{
// Inner scaling linear
float factor = fixedRadius/minRadius;
p *= factor;
dz *= factor;
}
else if (r2<fixedRadius)
{
// Sphere inversion
float factor = fixedRadius/r2;
p *= factor;
dz *= factor;
}
// else no transform
}
// Reflect if outside box
void boxFold(inout float3 p,
float dz,
float foldingLimit)
{
p = clamp(p, -foldingLimit, foldingLimit) * 2.0 - p;
//p = clamp(p, -foldingLimit, foldingLimit) * 2.0 - p;
}
//////////////////////////////////////////////////////////////////////
//
// Color transform
//
//////////////////////////////////////////////////////////////////////
fixed4 HSV (fixed h, fixed s, fixed v)
{
h *= 6;
fixed c = s * v;
float x = c * (1 - abs(fmod(h, 2) - 1));
float m = v-c;
c += m;
x += m;
fixed4 colors[6] = {
fixed4(c, x, m, 1),
fixed4(x, c, m, 1),
fixed4(m, c, x, 1),
fixed4(m, x, c, 1),
fixed4(x, m, c, 1),
fixed4(c, m, x, 1)};
return colors[int(h)];
}
#endif //RAY_MARCH_LIB_INCLUDED