1044 lines
25 KiB
HLSL
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
|