0.00
60.0 fps

Log in to post a comment.

#version 300 es
precision highp float;

uniform float iTime;
uniform vec2  iResolution;

out vec4 fragColor;

// Wonderland

// Base:
//         Green Field Diorama by Gallo
//         https://www.shadertoy.com/view/7dSGW1
// Portal mechanics:
//         TARDIS by efelo
//         https://www.shadertoy.com/view/7dKSRG
// Glow effect:
//         3D GLOW TUTORIAL by alro
//         https://www.shadertoy.com/view/7stGWj

// SDF, Maths and CG techniques:
//         IQ's articles and shaders:
//         https://iquilezles.org/articles/
//         Raymarching - Primitives: https://www.shadertoy.com/view/Xds3zN
//         Rainforest by IQ: https://www.shadertoy.com/view/4ttSWf
//         Box Mapping: https://www.shadertoy.com/view/MtsGWH
//         Box Function: https://iquilezles.org/articles/boxfunctions
//         Extrusion and Revolution SDF: https://www.shadertoy.com/view/4lyfzw
//         Elongation SDF: https://www.shadertoy.com/view/Ml3fWj
//         Many more..!!: https://www.shadertoy.com/user/iq

// Utility functions:
//         Hash without Sine by Dave_Hoskins: 
//         https://www.shadertoy.com/view/4djSRW
//
//         Shortest rotation dot! by FabriceNeyret2
//         https://www.shadertoy.com/view/XlsyWX
//
//         hg_sdf:
//         https://mercury.sexy/hg_sdf/

// Inspiration and endless knowledge:
//         Shadertoy Community – thank you for everything you share

// Created with help from AI:
// Grok (≈90%), Claude (≈8%), Gemini (≈2%)
// Free tiers - We really are heading into an age of creative abundance!


// Any suggestions to improve performance, code, art — anything at all — are MOST welcome!
// Performance & compile time definitely needs improvement.
// Animations in next version, hopefully! 

//---------------------------------------------------------------------------


#define AA 2

#define PI 3.14159265
#define TAU 6.2831853

//---------------------------------------------------------------
// NOISE FUNCTIONS
// Hash without Sine by Dave_Hoskins: https://www.shadertoy.com/view/4djSRW
//---------------------------------------------------------------
float hash11(float p) {
    p = fract(p * .1031);
    p *= p + 33.33;
    p *= p + p;
    return fract(p);
}

float hash12(vec2 p) {
    vec3 p3 = fract(vec3(p.xyx) * .1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

float hash13(vec3 p) {
    p = fract(p * vec3(0.1031, 0.11369, 0.13787));
    p += dot(p, p.yxz + 19.19);
    return fract((p.x + p.y + p.z) * p.x);
}

vec2 hash22( vec2 p ) 
{
    const vec2 k = vec2( 0.3183099, 0.3678794 );
    float n = 111.0*p.x + 113.0*p.y;
    return fract(n*fract(k*n));
}
//---------------------------------------------------------------

//---------------------------------------------------------------
// UTILITY FUNCTIONS
//---------------------------------------------------------------

// Shortest rotation dot by @FabriceNeyret2
// https://www.shadertoy.com/view/XlsyWX
#define rot2d(theta) mat2(cos(theta+vec4(0,33,11,0)))

// Claude
float _sin(float x) {
    x = fract(x / TAU + .5) * 2.0 - 1.0;
    float y = x * (4.0 - 4.0 * abs(x));
    return y * (0.775 + 0.225 * abs(y));
}

vec2 _sin(vec2 x) {
    return vec2(_sin(x.x), _sin(x.y));
}

vec3 _sin(vec3 x) {
    return vec3(_sin(x.x), _sin(x.y), _sin(x.z));
}

vec4 _sin(vec4 x) {
    return vec4(_sin(x.x), _sin(x.y), _sin(x.z), _sin(x.w));
}

float _cos(float x) {
    return _sin(x-11.);
}

vec2 _cos(vec2 x) {
    return vec2(_cos(x.x), _cos(x.y));
}

vec3 _cos(vec3 x) {
    return vec3(_cos(x.x), _cos(x.y), _cos(x.z));
}

vec4 _cos(vec4 x) {
    return vec4(_cos(x.x), _cos(x.y), _cos(x.z), _cos(x.w));
}

//-----------------------------------------------
// https://iquilezles.org/articles/distfunctions
//-----------------------------------------------

float dot2( in vec2 v ) { return dot(v,v); }
float dot2( in vec3 v ) { return dot(v,v); }
float ndot( in vec2 a, in vec2 b ) { return a.x*b.x - a.y*b.y; }

float sdHexagon( in vec2 p, in float r )
{
    const vec3 k = vec3(-0.866025404,0.5,0.577350269);
    p = abs(p);
    p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
    p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
    return length(p)*sign(p.y);
}

float sdTunnel( in vec2 p, in vec2 wh )
{
    p.x = abs(p.x); p.y = -p.y;
    vec2 q = p - wh;

    float d1 = dot2(vec2(max(q.x,0.0),q.y));
    q.x = (p.y>0.0) ? q.x : length(p)-wh.x;
    float d2 = dot2(vec2(q.x,max(q.y,0.0)));
    float d = sqrt( min(d1,d2) );
    
    return (max(q.x,q.y)<0.0) ? -d : d;
}

float sdPie( in vec2 p, in vec2 c, in float r )
{
    p.x = abs(p.x);
    float l = length(p) - r;
	float m = length(p - c*clamp(dot(p,c),0.0,r) );
    return max(l,m*sign(c.y*p.x-c.x*p.y));
}

float sdSphere( vec3 p, float s )
{
    return length(p)-s;
}

float sdBox( vec3 p, vec3 b )
{
  vec3 q = abs(p) - b;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}

float sdRoundBox( vec3 p, vec3 b, float r )
{
  vec3 q = abs(p) - b + r;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r;
}

float sdCone( vec3 p, vec2 c, float h )
{
  // c is the sin/cos of the angle, h is height
  // Alternatively pass q instead of (c,h),
  // which is the point at the base in 2D
  vec2 q = h*vec2(c.x/c.y,-1.0);
    
  vec2 w = vec2( length(p.xz), p.y );
  vec2 a = w - q*clamp( dot(w,q)/dot(q,q), 0.0, 1.0 );
  vec2 b = w - q*vec2( clamp( w.x/q.x, 0.0, 1.0 ), 1.0 );
  float k = sign( q.y );
  float d = min(dot( a, a ),dot(b, b));
  float s = max( k*(w.x*q.y-w.y*q.x),k*(w.y-q.y)  );
  return sqrt(d)*sign(s);
}

float sdCone(vec3 p, vec3 a, vec3 b, float ra, float rb)
{
    float rba  = rb-ra;
    float baba = dot(b-a,b-a);
    float papa = dot(p-a,p-a);
    float paba = dot(p-a,b-a)/baba;
    float x = sqrt( papa - paba*paba*baba );
    float cax = max(0.0,x-((paba<0.5)?ra:rb));
    float cay = abs(paba-0.5)-0.5;
    float k = rba*rba + baba;
    float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 );
    float cbx = x-ra - f*rba;
    float cby = paba - f;
    float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0;
    return s*sqrt( min(cax*cax + cay*cay*baba,
                       cbx*cbx + cby*cby*baba) );
}

float sdRoundCone(vec3 p, vec3 a, vec3 b, float r1, float r2)
{
    // sampling independent computations (only depend on shape)
    vec3  ba = b - a;
    float l2 = dot(ba,ba);
    float rr = r1 - r2;
    float a2 = l2 - rr*rr;
    float il2 = 1.0/l2;
    
    // sampling dependant computations
    vec3 pa = p - a;
    float y = dot(pa,ba);
    float z = y - l2;
    float x2 = dot2( pa*l2 - ba*y );
    float y2 = y*y*l2;
    float z2 = z*z*l2;

    // single square root!
    float k = sign(rr)*rr*rr*x2;
    if( sign(z)*a2*z2 > k ) return  sqrt(x2 + z2)        *il2 - r2;
    if( sign(y)*a2*y2 < k ) return  sqrt(x2 + y2)        *il2 - r1;
                            return (sqrt(x2*a2*il2)+y*rr)*il2 - r1;
}

float sdEllipsoid( in vec3 p, in vec3 r ) // approximated
{
    float k0 = length(p/r);
    float k1 = length(p/(r*r));
    return k0*(k0-1.0)/k1;
}

float sdTorus( vec3 p, vec2 t )
{
    return length( vec2(length(p.xz)-t.x,p.y) )-t.y;
}

float sdCylinder( vec3 p, vec3 c )
{
  return length(p.xz-c.xy)-c.z;
}

float sdCylinder( vec3 p, vec2 h )
{
    vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}

float sdVerticalCapsule( vec3 p, float h, float r )
{
  p.y -= clamp( p.y, 0.0, h );
  return length( p ) - r;
}

float sdVesicaSegment( in vec3 p, in vec3 a, in vec3 b, in float w )
{
    // orient and project to 2D
    vec3  c = (a+b)*0.5;
    float h = length(b-a);
    vec3  v = (b-a)/h;
    float y = dot(p-c,v);
    vec2  q = vec2(length(p-c-y*v),abs(y));
    
    // shape constants
    h *= 0.5;
    w *= 0.5;
    float d = 0.5*(h*h-w*w)/w;
    
    // feature selection (vertex or body)
    vec3  t = (h*q.x < d*(q.y-h)) ? vec3(0.0,h,0.0) : vec3(-d,0.0,d+w);
 
    // distance
    return length(q-t.xy) - t.z;
}

float sdCapsule( vec3 p, vec3 a, vec3 b, float r )
{
  vec3 pa = p - a, ba = b - a;
  float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
  return length( pa - ba*h ) - r;
}

float sdTriPrism( vec3 p, vec2 h )
{
  vec3 q = abs(p);
  return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);
}

vec3 opRepetition(inout vec3 p, in vec3 s)
{
    vec3 cellId = floor(p / s);
    p -= (cellId + 0.5) * s;
    return cellId;
}

float opLimitedRepetition(inout float p, float spacing, int numLayers) {
    float cell = clamp(round(p / spacing), 0.0, float(numLayers - 1));
    p -= cell * spacing;
    return cell;
}

float smin(float a, float b, float k) {
    k *= 4.0;
    float h = max(k - abs(a - b), 0.0) / k;
    return min(a, b) - h * h * k * (1.0 / 4.0);
}

float smax(float a, float b, float k) {
    float h = max(k - abs(a - b), 0.0) / k;
    return max(a, b) + h * h * k * (1.0 / 4.0);
}

vec4 opElongate( in vec3 p, in vec3 h )
{
    //return vec4( p-clamp(p,-h,h), 0.0 ); // faster, but produces zero in the interior elongated box
    
    vec3 q = abs(p)-h;
    return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
}

float opExtrusion( in vec3 p, in float sdf, in float h )
{
    vec2 w = vec2( sdf, abs(p.z) - h );
  	return min(max(w.x,w.y),0.0) + length(max(w,0.0));
}

// Box Function by IQ: https://iquilezles.org/articles/boxfunctions
vec2 iBox( in vec3 ro, in vec3 rd, in vec3 rad ) 
{
    vec3 m = 1.0/rd;
    vec3 n = m*ro;
    vec3 k = abs(m)*rad;
    vec3 t1 = -n - k;
    vec3 t2 = -n + k;
	return vec2( max( max( t1.x, t1.y ), t1.z ),
	             min( min( t2.x, t2.y ), t2.z ) );
}

//--------------------------------------

//---------------------------------------
// SDF BLEND & DOMAIN REPETITION: 
// https://mercury.sexy/hg_sdf/
//---------------------------------------

float pModPolar(inout vec2 p, float repetitions) 
{
	float angle = 2.*PI/repetitions;
	float a = atan(p.y, p.x) + angle/2.;
	float r = length(p);
	float c = floor(a/angle);
	a = mod(a,angle) - angle/2.;
	p = vec2(_cos(a), _sin(a))*r;
	// For an odd number of repetitions, fix cell index of the cell in -x direction
	// (cell index would be e.g. -5 and 5 in the two halves of the cell):
	if (abs(c) >= (repetitions/2.)) c = abs(c);
	return c;
}

// Repeat in two dimensions
vec2 pMod2(inout vec2 p, vec2 size) {
	vec2 c = floor((p + size*0.5)/size);
	p = mod(p + size*0.5,size) - size*0.5;
	return c;
}
//-----------------------------------------

//---------------------------------------------------------------
// MAIN
//---------------------------------------------------------------

#define px 1./iResolution.y

// Materials
#define MAT_LEAVES 20
#define MAT_STEM 21
#define MAT_GROUND 30
#define MAT_GRASS 40
#define MAT_CLOUD 70
#define MAT_TREES 80
#define MAT_CASTLE_WHITE 90
#define MAT_CASTLE_FACADE 91
#define MAT_CASTLE_ROOF 100

#define MAT_BIFROST 110

#define MAT_GIRL_HAIR 120
#define MAT_GIRL_SKIN 121
#define MAT_GIRL_DRESS 122
#define MAT_GIRL_BOOTS 124
#define MAT_GIRL_FACE 127
#define MAT_GIRL_BLONDE 129

#define MAT_MUSHROOM_CAP 140
#define MAT_MUSHROOM_STEM 141

#define MAT_FLOWER_FACE 150
#define MAT_SMALL_FLOWER_PISTIL 151

#define MAT_BUNNY 160

#define MAT_GLOW 10001

#define RI_GRASSY_GROUND   (1u << 0)
#define RI_BIFROST         (1u << 1)
#define RI_ALICE           (1u << 2)
#define RI_BUNNY           (1u << 3)
#define RI_MUSHROOM        (1u << 4)
#define RI_TALKING_FLOWER  (1u << 5)
#define RI_FLOWER_BED      (1u << 6)
#define RI_CLOUDS          (1u << 7)
#define RI_GRASSY_MOUNTAIN (1u << 8)
#define RI_CASTLE          (1u << 9)

//---------------------------------------------------------------
// SDF Figures
//---------------------------------------------------------------

#   define opMin(_v, _m) res = (_v < res.x) ? vec2(_v, _m) : res
#   define opMinV(_v) res = (_v.x < res.x) ? _v : res

float sdPortalTorus(vec3 p)
{
    vec3 q = p - vec3(0.0, 0.1, 0.7);
    q.yz *= rot2d(PI/2.0);
    return sdTorus(q, vec2(1.0, 0.02));
}

void sdGrassyGround(vec3 p, inout vec2 res)
{
    float area = sdCylinder(p-vec3(0,-.17,0), vec2(1.2,0.001))-0.1;
    if(area-0.003 < res.x )
    {
        float distortion = (_sin(10.*p.x)+_cos(20.*p.z)) * 0.001;
        opMin(area-distortion, MAT_GRASS);
    }
}

//--------------- Bifrost Start ------------------
vec3 getRainbowColor(float t)
{
    t = clamp(t, 0.0, 1.0);
    float hue = t * 0.85;

    vec3 c = vec3(
        abs(hue*6.0-3.0)-1.0,
        2.0 - abs(hue*6.0-2.0),
        2.0 - abs(hue*6.0-4.0)
    );
    c = smoothstep(0.0, 1.0, clamp(c, 0.0, 1.0));

    c = mix(c, vec3(1.0), 0.15);

    return c;
}

vec3 worldToBifrost(vec3 p)
{
    p -=vec3(0.495, -0.08, .69);
    p.x += .5*_sin(p.z*2.);
    p.y -= .56*smoothstep(-.95,.95, p.z);
    return p;
}

vec3 getBifrostColor(vec3 p)
{
    vec3 q = worldToBifrost(p);
    
    float width = 0.25*(1.-.5*q.z);
    
    float colorT = (q.x + width * 0.5) / width;
    colorT = clamp(colorT, 0.0, 1.0);
    
    return getRainbowColor(colorT);
}

void sdBifrost(vec3 p, bool inside, inout vec2 res)
{
    if (!inside && p.z > 0.75) return;
    if (inside && p.z < 0.65) return;
    
    if(sdBox(p-vec3(.5,.2,.6),vec3(.7,.32,1.)) < res.x)
    {
        vec3 q = worldToBifrost(p);
        vec3 boxSize = vec3(0.25*(1.-.56*q.z), 0.005*(1.-.9*q.z), 1.8) * 0.5;
        opMin(sdBox(q, boxSize)-.01, MAT_BIFROST);
    }
}
//--------------- Bifrost End ---------------------


//--------------- Mountain Start -----------------
float getGroundHeight(vec2 p) {
    float h = (_cos(1.9*p.x)+_cos(1.9*p.y))*.6-.3;
    float distortion = (_sin(15.*h)+_cos(20.*p.y)) * 0.01;
    return h + distortion;
}

vec2 getPositionInXZGrid(vec3 p, float cellSize, float margin, vec2 seed)
{
    vec2 n = floor(p.xz / cellSize);
    vec2 o = hash22(n+seed);
    o = o * (1.0 - 2.0*margin) + margin;
    return (n + o) * cellSize;
}

// Rainforest by IQ: https://www.shadertoy.com/view/4ttSWf
void sdTrees(vec3 p, float innerBoundRadius, float outerBoundRadius, inout vec2 res)
{
    const float treeHeight = .025;
    const float treeWidth = .01;

    vec2 tp = getPositionInXZGrid(p, .15, .25, vec2(12,89));

    float d = length(tp);
    if (d > outerBoundRadius || d < innerBoundRadius) return;

    float base = getGroundHeight(tp);
    vec3 tree_center = vec3(tp.x,base+treeHeight*.5,tp.y);
    vec3 q = p - tree_center;
    
    d = sdEllipsoid(q, vec3(treeWidth, treeHeight, treeWidth));
    if(d-0.002>res.x) return;
    
    d += _sin(350.*p.y)*0.0008;
    opMin(d, MAT_TREES);
}

void sdGrassyMountain(vec3 p, inout vec2 res)
{
    float bound = sdCylinder(p-vec3(0,.23,0),vec2(1.,.33));
    if(bound < res.x)
    {
        float area = p.y-getGroundHeight(p.xz);
        area = smax(area,bound, .05);
        opMin(area, MAT_GRASS);
        
        if(max(bound, -sdCylinder(p, vec3(0,0,.58))) < res.x)
            sdTrees(p, .6, .95, res);
    }
}

//--------------- Mountain End -------------------

//--------------- Castle Start --------------------
float sdBattlement(vec3 p, float radius, vec2 crenelSize, float count)
{
    pModPolar(p.xz, count);
    return sdBox(p, vec3(radius, crenelSize));
}

vec2 sdCastleTower(vec3 p, float radius, float height, float floorCount, bool withBattlment)
{
    vec2 res = vec2( 1e10, 0.0 );
    vec3 top = p-vec3(0,height,0);
    
    float battH = radius * 0.7;
    float battY = height - battH;

    float r = radius;

    r = mix(r, r * 1.2, smoothstep(battY - 0.02, battY, p.y));
    
    if (floorCount > 1.0)
    {
        float y = p.y;
        opLimitedRepetition(y, battY/floorCount, int(floorCount));
        r = mix(r, r * 1.05, smoothstep(0.01, 0.0, abs(y)));
    }
    
    float base = max(sdCylinder(p, vec2(r, height)), -length(top)+radius+.01);
    base = withBattlment?max(base, -sdBattlement(top, radius*1.5, vec2(.015,.015), 6.)):base;
    
    opMin(base-.005, MAT_CASTLE_WHITE);
    
    vec3 q = top+vec3(0,.01,0);
    float h = 2.6*radius;
    float roof = sdCone(q, vec3(0.0), vec3(0.0,h,0.0), radius*.9, 0. );
    opMin(roof, MAT_CASTLE_ROOF);
    
    return res;
}

vec2 sdCastleTowers(vec3 p, float radius, float towerRadius, float towerHeight, float floorCount, float count, bool withBattlment)
{
    pModPolar(p.xz, count);
    p.x -= radius;
    return sdCastleTower(p, towerRadius, towerHeight, floorCount, withBattlment);
}

vec2 sdBuidlingBlock(vec3 p, vec4 rc, vec4 fc)
{
    float block = sdBox(p, vec3(.2,.22,.2));
    vec2 res = vec2(block-.005, MAT_CASTLE_WHITE);
    
    vec3 q = p;
    q.xy-=vec2(.135, .26);
    float blockRoof = sdTriPrism(q, vec2(rc.x,mix(rc.y,rc.z, q.y/rc.w)));
    opMin(blockRoof, MAT_CASTLE_ROOF);
    
    // unique facade!
    p-=vec3(.2,.10,0);
    q = p.zyx;
    float width = fc.y, height = fc.x;
    vec4 w = opElongate( q, vec3(0.,fc.x,0.02) );
    float facade = w.w+sdEllipsoid(w.xyz,vec3(fc.y,fc.y,0.01));
    opMin(facade, MAT_CASTLE_FACADE);
    
    return res;
}

void sdMainBuilding(vec3 p, inout vec2 res)
{
    opMin(max(length(p)-.45, p.y-.02), MAT_CASTLE_WHITE);
    float boundCylinder = sdCylinder(p-vec3(0,.30,0),vec2(.4,.3));
    if(boundCylinder < res.x)
    {
        vec3 q = p;
        q.xz *= rot2d(PI/2.);
        float faceId = pModPolar(q.xz, 6.);
        q.x -= .1;
        
        opMinV(sdBuidlingBlock(q, vec4(.08,.11,.045,.08), vec4(.15,.045,2.,.005)));
        
        opMinV(sdCastleTowers(p, .35, .04, .35, 3., 6., true));
        
        q = p;
        q.x = abs(q.x)-.22;
        q.z = q.z-.1;
        opMinV(sdCastleTower(q, .055, .45, 6., true));
        
        p.xz*=rot2d(PI/2.);
        opMinV(sdBuidlingBlock(p-vec3(0,.17,0), vec4(.1,.17,.08,.17), vec4(.13,.08,1.5,0.02)));
    }
}

void sdCastle(vec3 p, inout vec2 res)
{
    p-=vec3(0,.55,0);
    
    if(length(p)-.7 < res.x && p.y>-.1)
    {
        float fort = max(sdCylinder(p, vec2(.6,.05)),-length(p)+.59);
        fort = max(fort, -sdBattlement(p-vec3(0,.053,0),.7,vec2(.02), 50.));
        
        {
            vec3 q=p+vec3(0,0,.6);
            vec4 w = opElongate( q, vec3(-0.01,0.03,0.0) );
            float gateBuilding = w.w+opExtrusion( w.xyz,max(sdHexagon(w.yx,.08),-sdTunnel(q.xy+vec2(0,-.02),vec2(.04,.07))),.03);
            opMin(gateBuilding-.01, MAT_CASTLE_WHITE);
            opMin(sdBox(q,vec3(.07,.07,.005)), MAT_CASTLE_ROOF);
        }
        
        opMin(fort-.005, MAT_CASTLE_WHITE);
        
        opMinV(sdCastleTowers(p, .6, .03, .09, 1., 6., false));
        
        sdMainBuilding(p, res);
    }
}

#define COLOR_CASTLE_ROOF vec3(0.11, 0.306, 0.502)

void paintCastleFacade(vec3 p, inout vec3 FinalColor)
{
    p-=vec3(0, .56, 2.2);
    p.xz *= rot2d(PI/2.);
    pModPolar(p.xz, 6.);
    float d = sdTunnel(p.zy, vec2(.02,.04));
    FinalColor = mix(FinalColor, COLOR_CASTLE_ROOF, smoothstep(px, 0.,d));
}
//--------------- Castle End ----------------------

//--------------- Clouds Start --------------------
vec2 sdCloud(vec3 p, vec2 seed, float cloudRadius)
{
    p.y += 1.;

    vec2 res = vec2(1000.,0);
    float bound = sdCylinder(p-vec3(0,1.,0),
                             vec2(cloudRadius + 0.35, 1.2));
    if (bound > .5) return vec2(bound, 0);
    
    const float spacing = 0.8;
    const float cellSize = 0.8;

    float rx = hash12(seed + vec2(.1, 0));
    float rz = hash12(seed + vec2(0, .1));
    vec2 gridOffset = vec2(mix(-0.4, -0.1, rx), mix(-0.4, -0.1, rz)) * cellSize;

    vec2 q_xz   = (p.xz + gridOffset) / spacing;
    vec2 cellId = floor(q_xz);


    for (int ix = -1; ix <= 1; ++ix) {
        for (int iz = -1; iz <= 1; ++iz) {
            vec2 neighborId = cellId + vec2(float(ix), float(iz));

            vec2 basePos = (neighborId + 0.5) * spacing - gridOffset;

            float baseDist = length(basePos);
            if (baseDist > cloudRadius) continue;
            
            float baseNRadius = baseDist / cloudRadius;

            float hx = hash12(neighborId + vec2(12.34, 56.78));
            float hz = hash12(neighborId + vec2(78.91, 23.45));
            float hy = hash12(neighborId + vec2(45.67, 89.01));
            vec3 jitter = vec3(hx, hy, hz) * 2.0 - 1.0;
            jitter.xz *= (spacing * 0.25);

            vec2 worldPos = basePos + jitter.xz;
            
            worldPos *= (1.0 - baseNRadius * 0.25);
            jitter.y *= (1.0 - baseNRadius * 0.5) * .4;

            float distFromCenter = length(worldPos);
            if (distFromCenter > cloudRadius) continue;

            float radius = mix(1.21, .34, distFromCenter / cloudRadius);

            vec3 sphereCenter = vec3(worldPos.x, radius + jitter.y, worldPos.y);

            float dist = length(p - sphereCenter) - radius;
            opMin(smin(res.x, dist, .015), MAT_CLOUD);
        }
    }
    
    res = res.x > 999. ? vec2(bound + 0.7, 0) : res;

    return res;
}

void sdCloudLayer(vec3 p, float scale, inout vec2 res)
{
    p*=scale;
    float bound = max(sdCylinder(p-vec3(0,2.1,0),vec2(13.5,2.7)),-sdCylinder(p, vec3(0,0,7.)));
    if(bound < res.x)
    {
        const float layerSpacing = 2.6;
        const int numLayers = 2;
        
        p.y-=.8;
        float layerId = opLimitedRepetition(p.y, layerSpacing, numLayers);
        
        float angleId = pModPolar(p.xz, 12.);
    
        vec2 seed = vec2(angleId, layerId);
    
        float showHash = hash12(seed + vec2(160.0, 123.0));//vec2(100.0, 120.0)
        if (showHash < 0.6) return;
    
        p.x -= 9.0 + 2.0 * hash12(seed + vec2(12.34, 56.78)) - layerId*.2;
    
        float cloudRadius = mix(1.0, 1.8, hash12(seed + vec2(0.5, 0)));
    
        vec2 clouds = sdCloud(p, seed, cloudRadius);
        clouds.x/=scale;
        opMinV(clouds);
    }
}
//--------------- Clouds End ----------------------

//--------------- Alice Start ----------------------
#define ALICE_SCALE .9

vec3 worldToAlice(vec3 p)
{
    p*=ALICE_SCALE;
    p-=vec3(0,.389,-.1);
    return p;
}

void sdAlice(vec3 p, inout vec2 res)
{   
    p = worldToAlice(p);
    
    float bound = sdCylinder(p-vec3(0,-.2,0),vec2(.105,.25));
    if(bound > res.x) return;
    
    // Head
    {
        if(p.y>-.06)
        {
            vec3 headP = p;
            float head = smin(length(headP-vec3(0,.01,0))-.0265, sdEllipsoid(headP-vec3(-.015, -.022, 0),vec3(.011, .012,.017)), .009);
            
            vec3 eyeSocketP = headP-vec3(-.04, -.005, 0);
            eyeSocketP.z = abs(eyeSocketP.z)-.01;
            head = smax(head, -length(eyeSocketP)+.0011, .02);
            
            vec3 noseP = headP-vec3(-.032, -.0175, 0);
            head = smin(head, sdCapsule(noseP,vec3(0,0,0), vec3(.0055,.01,0), .001 ), .001);
            
            vec3 jawP = headP-vec3(-.0212,-.036, 0);
            jawP.z = abs(jawP.z)-.0006;
            head = smin(head, sdCapsule(jawP,vec3(0,0,0), vec3(.017,.009,.01), .004 ), .003);
            
            vec3 neckP = p-vec3(.000,-.05, 0);
            head = min(head, sdCapsule(neckP,vec3(.002,0,0), vec3(.0,.02,.0), .01));
            
            opMin(head, MAT_GIRL_FACE);
        }
        
        if(p.y>-.11)
        {
            vec3 hairP = p;
            float hair = abs(sdRoundCone(hairP, vec3(-0.001,.0001,0), vec3(0, -.07, 0), .0285, .0385))-.007;
            hair+=.0015*_sin(60.*hairP.y-3.8)+.00038*_sin(200.*hairP.y);
            
            hairP -= vec3(-.06, -.032, 0);
            float hairCut = sdRoundBox(hairP, vec3(.05, .08,.027), .02);
            hairCut = max(hairCut, hairP.y-.047+abs(.024*_sin(50.*hairP.z-.1)));
            
            hair = smax(hair, -hairCut, .02);
            opMin(hair, MAT_GIRL_BLONDE);
        }
    }
    
    // Dress
    if(p.y<-.04&&p.y>-.35)
    {
        vec3 dressP = p-vec3(.006,0,0);
        
        vec3 topP = dressP-vec3(0,-.123, 0);
        float w = mix(.026,.0001,clamp((topP.y-.05)/-.1,.0,1.));
        float t = .0001+.007*abs(_sin(30.*topP.y+0.9));
        float dress = sdBox(topP, vec3(t, .05, w))-.025;
        
        vec3 skirtP = dressP-vec3(0,-.23, 0);
        float l = clamp((skirtP.y-.1)/-.2,.0,1.)+.3*_sin(18.*skirtP.y+1.97)+.03*_sin(50.*skirtP.y-.5);
        float r = mix(.009,.09,l);
        r += mix(1e-5,.005,l)*_sin(8.*atan(skirtP.z/skirtP.x));
        float belt = smoothstep(.065, .07, skirtP.y) * smoothstep(.085, .074, skirtP.y);
        r = mix(r, .023, belt);
        r*= max(0., min(1.-abs(skirtP.x)/.35, 1.-abs(skirtP.z)/1.));
        float skirt = sdCylinder(skirtP, vec2(r,.1))-.01;
        dress = min(dress, skirt);
        
        vec3 sleevesP = dressP-vec3(0,-.07,0);
        sleevesP.z = abs(sleevesP.z)-.047;
        float sleeves = sdSphere(sleevesP, .02);
        dress = min(dress, sleeves);
        
        vec3 collarP = dressP-vec3(0,-.034,0);
        float collar = sdCone(collarP,vec2(1,.8),.025)-.0015;
        collarP.z = abs(collarP.z);
        collarP.x+=.017;
        collarP.zx*=rot2d(PI/4.);
        collar = smax(collar, -collarP.x, .005);
        dress = min(dress, collar);
        
        opMin(dress, MAT_GIRL_DRESS);
    }
    
    // Legs
    if(p.y<-.33)
    {
        vec3 legsP = p-vec3(.006,-.45,0);
        float r = .009+.004*_sin(32.*legsP.y-1.5);
        float legs = min(sdVerticalCapsule(legsP-vec3(0,0,-.012),.12,r), 
                            sdVerticalCapsule(legsP-vec3(-.017,0,.012),.12,r));
        opMin(legs, MAT_CLOUD);
        
        float foot = min(sdCapsule(legsP, vec3(0,0,-.012), vec3(-.025,0,-.025), .01),
                            sdCapsule(legsP, vec3(-.017,0,.012), vec3(-.04,0,.02), .01));
        opMin(foot, MAT_GIRL_BOOTS);
    }
    
    // Arms
    if(p.y<-.06&&p.y>-.16)
    {
        vec3 armsP, armsBaseP = armsP = p-vec3(.006,-.063,0);
        armsP.z = abs(armsP.z)-.047;
        
        float r1 = .0085+.0008*_sin(100.*armsP.y-.05),
              r2 = .008+.0014*_sin(95.*armsP.z+1.2);
        float arms = sdCapsule(armsP, vec3(0,0,.005), vec3(0,-.085,.01),r1);
        arms = smin(arms, sdCapsule(armsP, vec3(-.005,-.083,.007), vec3(-.05,-.07,-.03),r2), .001);
        vec3 handsP = armsBaseP-vec3(-.05,-.07,.0018);
        float hands = min(sdEllipsoid(handsP, vec3(.012,.008,.022)), sdSphere(handsP-vec3(-0.001,-.0045,-.004),.01));
        arms = min(arms, hands);
        
        opMin(arms, MAT_GIRL_SKIN);
    }
    
    res.x/ALICE_SCALE;
}

void _paintFace(vec3 p, inout vec3 FinalColor)
{   
    float d;
    vec2 eyeP = p.yz - vec2(-.009,0);
    eyeP.y = abs(eyeP.y)-.01;
    d = max(abs(length(eyeP)-.005)-.0005, -eyeP.x+.0025);
    FinalColor = mix(FinalColor, vec3(0), smoothstep(px, 0.,d));
    
    vec2 mouthP = p.yz - vec2(-.0173,0);
    float ud = length(mouthP)-.01;
    d = max(abs(ud)-.0004, mouthP.x+.007);
    d = min(d, max(length(mouthP-vec2(-.017,0))-.01, ud));
    
    FinalColor = mix(FinalColor,vec3(0.7608, 0.1176, 0.3373), smoothstep(px, 0.,d));
}

void paintFace(vec3 p, inout vec3 FinalColor)
{
    p = worldToAlice(p);
    
    _paintFace(p, FinalColor);
}

// Triplanar/Boxmap Mapping by IQ : https://www.shadertoy.com/view/MtsGWH
vec3 paintGirlDress(vec3 p, vec3 n, float scale)
{
    vec3 scaledP = p*scale;
    
    float r=.2,s=1e-4;
    
    float x = smoothstep(s,-s,length(fract(scaledP.yz)-.5)*2.-r);
    float y = smoothstep(s,-s,length(fract(scaledP.xz)-.5)*2.-r);
    float z = smoothstep(s,-s,length(fract(scaledP.xy)-.5)*2.-r);
    
    vec3 m = pow(abs(n),vec3(10.));
    
    return vec3(.35)*(x*m.x + y*m.y + z*m.z) / (m.x + m.y + m.z + 1e-5);
}

//--------------- Alice End ----------------------

//--------------- Mushroom Start -----------------

#define MUSHROOM_POS vec3(0,-.06,-.8)
#define MUSHROOM_HEIGHT .42
#define MUSHROOM_SCALE 2.5

void worldToMushroom(inout vec3 p)
{
    p -= MUSHROOM_POS;
    p.x = abs(p.x)-.7;
    p *= MUSHROOM_SCALE;
}

void sdMushroom(vec3 p, inout vec2 res) 
{
    worldToMushroom(p);
    float h = MUSHROOM_HEIGHT;
    
    float bound = sdCylinder(p-vec3(0,.3,0),vec3(0,0,.31));
    if(bound > res.x) return;
    
    if(p.y<.80&&p.y>.25)
    {
        vec3 topP = p - vec3(0,h,0);
        float top = sdSphere(topP-vec3(0,.1,0), .08);
        top = smin(top, sdEllipsoid( topP, vec3(0.3,0.05,0.3) ), .08);
        top = smax(top, -sdSphere(topP-vec3(0,-.55,0), .5), .08);
        opMin(top, MAT_MUSHROOM_CAP);
    }
    
    if(p.y<.38&&p.y>-.06)
    {
        vec3 stemP = p;
        float t = stemP.y / h-.6;
        stemP.xz -= t*t*vec2(.12, .05);
        float stem = sdVerticalCapsule(stemP, h, .05+.025*smoothstep(1.,0.,stemP.y/h));
        opMin(stem, MAT_MUSHROOM_STEM);
    }
}

void paintMushroomCap(vec3 p, vec3 n, inout vec3 color, inout vec3 shadowColor)
{
    float side = sign(p.x);
    worldToMushroom(p);

    vec3 topColor, topShadowColor;
    if(side<0.) {
        topColor = vec3(0.9, 0.0, 0.25);
        topShadowColor = vec3(0.85,0.0,0.25);
    }
    else {
        topColor = vec3(0.95,0.1,0.0);
        topShadowColor = vec3(0.6,0.05,0.0);
    }
    color = n.y > -.85 ? topColor: vec3(0.95, 0.95, 0.9);
    shadowColor = n.y > -.85 ? topShadowColor: vec3(0.85,0.85,0.8);

    if (n.y > -0.85) 
    {
        vec2 cell = pMod2(p.xz, vec2(.1));
        if(length(cell*.1)<.28)
        {
            float r = mix(.01, .025, hash12(cell));
            color = mix(color,vec3(1.),smoothstep(px,0.,length(p.xz)-r));
        }
    }
}
//--------------- Mushroom End -------------------

//------------ Talkign Flower Start --------------
#define TALKING_FLOWER_POS vec3(-1.,-.05,-.3)
#define TALKING_FLOWER_ROTATION -PI/2.
#define TALKING_FLOWER_SCALE 1.2
#define TALKING_FLOWER_HEIGHT .42

vec3 worldToFlowerFace(vec3 p)
{
    p -= TALKING_FLOWER_POS;
    p *= TALKING_FLOWER_SCALE;
    p.xz*=rot2d(TALKING_FLOWER_ROTATION);
    return p - vec3(0,TALKING_FLOWER_HEIGHT,-.1);
}

void sdTalkingFlowerPlant(vec3 p, inout vec2 res)
{   
    p -= TALKING_FLOWER_POS;
    p.xz*=rot2d(TALKING_FLOWER_ROTATION);
    float h = TALKING_FLOWER_HEIGHT;
    
    float bound = sdCylinder(p-vec3(0,.2,-.06),vec3(0,0,.23));
    if(bound > res.x) return;

    vec3 topP = p - vec3(0,h,-.05);
    if(p.y<.5&&p.y>-.03)
    {
        vec3 stemP = p;
        stemP.z -= .06*_sin(12.*stemP.y-1.5);
        float stem = sdVerticalCapsule(stemP, h, .01);
        
        stem = min(stem, max(sdRoundCone(topP,vec3(0,0,-.05),vec3(0,0,.01), .08, .035), -topP.z-.03));
        opMin(stem, MAT_STEM);
    }

    if(p.y<.75&&p.y>.19)
    {
        float face = sdEllipsoid( topP-vec3(0,0,-.04), vec3(0.08,0.08,0.022));
        opMin(face, MAT_FLOWER_FACE);
        
        topP.z-=-.035;
        pModPolar(topP.xy, 10.);
        topP.x-=.145;
        float petals = sdEllipsoid( topP, vec3(.08,.045,.01));
        petals-=abs(_sin(50.*topP.y-0.))*.002*clamp((.06-topP.x)/.08,0.,1.);
        opMin(petals, MAT_MUSHROOM_STEM);
    }
    
    if(p.y<.13&&p.y>-.03)
    {
        vec3 leavesP = p-vec3(0,-.01,-.06);
        pModPolar(leavesP.xz, 4.);
        leavesP.xy*=rot2d(-PI/3.5);
        float leafLength = .1, t = leavesP.x/leafLength -.5;
        leavesP.x-=leafLength;
        leavesP.y += t*t*.05;
        float leaves = sdEllipsoid( leavesP, vec3(leafLength,.015,.045));
        leaves-=abs(_sin(70.*leavesP.z))*.005*clamp((leafLength-.02-leavesP.x)/leafLength,0.,1.);
        opMin(leaves, MAT_LEAVES);
    }
}

void paintFlowerFace(vec3 p, inout vec3 color)
{
    p = worldToFlowerFace(p);
    
    _paintFace((p.zyx-vec3(0,.12,0))*.34, color);
}
//------------ Talkign Flower End ----------------

//---------------- Bunny Start -------------------
#define BUNNY_POS vec3(-.9,0,.4)
#define BUNNY_ROTATION -PI/4.
#define BUNNY_SCALE 1.2

void worldToBunny(inout vec3 p)
{
    p *= BUNNY_SCALE;
    p -= BUNNY_POS;
    p.xz*=rot2d(BUNNY_ROTATION);
}
void sdBunny(vec3 p, inout vec2 res)
{   
    worldToBunny(p);
    
    float bound = sdCylinder(p-vec3(0,.2,-.02),vec3(0,0,.32));
    if(bound > res.x) return;
    
    vec3 bodyP = p-vec3(0,.1,0);
    float bunny = sdRoundCone(bodyP,vec3(0),vec3(0,.24,-.1),.2,.13), t;
    
    if(p.y<.4&&p.y>-.09)
    {
        bunny = smin(bunny, sdSphere(bodyP-vec3(0,-.01,-.063), .15),.01);
        
        vec3 legsP = p;
        legsP.x = abs(legsP.x)-.1;
        bunny = smin(bunny, sdSphere(legsP-vec3(-0.05,0,.03), .14), .03);
        bunny = smin(bunny, sdRoundCone(legsP,vec3(-.04,0.01,.01),vec3(.06,.07,-.1),.16,.09), .001);
        bunny = smin(bunny, sdRoundBox(legsP-vec3(0.045,-.057,-.1),vec3(.05,.038,.08),.038), .003);
        
        bunny = min(bunny, sdSphere(bodyP-vec3(0,-.1,.2), .08));
        
        vec3 armsP = p-vec3(0,.25,0);
        armsP.x = abs(armsP.x)-.13;
        float armLength = .2;
        t = armsP.z/armLength +.5;
        armsP.xy += t*t*vec2(.11,-.07);
        bunny = smin(bunny, sdCapsule(armsP,vec3(0),vec3(0,0,-armLength),.05), .003);
    }
    
    if(p.y<1.&&p.y>.25)
    {
        vec3 headP = p-vec3(0,.48,-.13);
        float head = sdSphere(headP, .17);
        
        vec3 cheeksP = headP-vec3(0,-.065,-.13);
        float l = .22;
        t = cheeksP.x/l;
        cheeksP.yz += t*t*vec2(-.05,-.18);
        head = smin(head, sdCapsule(cheeksP,vec3(-l/2.,0,0),vec3(l/2.,0,0), .05), .015);
        
        vec3 noseP = headP-vec3(0,-.04,-.17);
        head = smin(head, sdSphere(noseP-vec3(0,0,-.005), .01), .01);
        
        vec3 earsP = headP-vec3(0,.08,.05);
        earsP.x = abs(earsP.x)-.07;
        float ears = smax(abs(sdVesicaSegment(earsP,vec3(0),vec3(.1,.36,0),.065)-.02)-.015, -earsP.z-.02, .02);
        head = smin(head, ears, .005);
        
        bunny = smin(bunny, head, .007);
    }
    
    opMin(bunny/BUNNY_SCALE, MAT_BUNNY);
}

void paintBunny(vec3 p, inout vec3 color)
{
    worldToBunny(p);
    p-=vec3(0,.48,-.13);
    
    if(p.z<0.)
    {
        float d;
        vec2 eyeP = p.xy;
        eyeP.x = abs(eyeP.x)-.06;
        d = max(abs(length(eyeP)-.02)-.004, -eyeP.y+.0025);
        color = mix(color, vec3(0), smoothstep(px,-px,d));
        
        vec2 noseP = p.xy-vec2(0,-.057);
        d = sdPie(noseP,vec2(.84,.54), 0.02)-.005;
        color = mix(color, vec3(1.0,0.35,0.25), smoothstep(px,-px,d));
        
        vec2 mouthP = p.xy-vec2(0,-.064);
        mouthP.x = abs(mouthP.x)-.01;
        d = max(abs(length(mouthP-vec2(.01,0))-.025)-.003, mouthP.y+.008);
        color = mix(color, vec3(1.0,0.35,0.25)*.4, smoothstep(px,-px,d));
    }
}
//---------------- Bunny End ---------------------

//------------- Flower Bed Start -----------------
void sdBedFlower(vec3 p, inout vec2 res) // Merge with talking flower
{
    opMin(sdSphere(p, .005)/.5,MAT_SMALL_FLOWER_PISTIL);
    
    pModPolar(p.xz, 7.);
    p.x-=.01;
    p.y-=.001;
    p.xy*=rot2d(-PI/12.);
    float petals = sdEllipsoid(p,vec3(.009,.001,.0045));
    opMin(petals/.5, MAT_MUSHROOM_STEM);
}

void sdFlowerBed(vec3 p, float radius, inout vec2 res)
{
    if(p.y>-.02||p.y<-.07||length(p)>1.3) return;

    vec2 tp = getPositionInXZGrid(p,.35,.1,vec2(11,33));

    float d = length(tp);
    if (d > radius) return;
    
    sdBedFlower(p-vec3(tp.x,-.065,tp.y), res);
}
//-------------- Flower Bed End ------------------

//--------------------------------------
// MAIN SDF 
//--------------------------------------
#define isVisible(mask) (riFlags & mask) != 0u
vec2 mainWorld(in vec3 p, in uint riFlags)
{
    vec2 res = vec2(sdTorus(p-vec3(0,-.18,.0),vec2(1.215,0.09)),MAT_GROUND);
    
    float bound = sdCylinder(p-vec3(0,.3,0),vec2(1.31,.5));
    if(bound > res.x) return res;
    
    if(isVisible(RI_GRASSY_GROUND)) sdGrassyGround(p, res);
    
    if(isVisible(RI_BIFROST)) sdBifrost(p, false, res);
    
    if(isVisible(RI_ALICE)) sdAlice(p, res);
    
    if(isVisible(RI_BUNNY)) sdBunny(p, res);
    
    if(isVisible(RI_MUSHROOM)) sdMushroom(p, res);
    
    if(isVisible(RI_TALKING_FLOWER)) sdTalkingFlowerPlant(p, res);
    
    if(isVisible(RI_FLOWER_BED)) sdFlowerBed(p, 1.2, res);
    
    return res;
}

vec2 portalWorld(in vec3 q, in uint riFlags)
{
    vec3 p = q-vec3(0,-.05,2.2);
    vec2 res = vec2(sdTorus(p-vec3(0,-.1,.0),vec2(1.,.09)),MAT_GROUND);
    
    if(isVisible(RI_BIFROST)) sdBifrost(q, true, res);
    
    if(isVisible(RI_CLOUDS)) sdCloudLayer(p-vec3(0,.05,0), 10., res);
    
    if(isVisible(RI_GRASSY_MOUNTAIN)) sdGrassyMountain(p, res);
    
    if(isVisible(RI_CASTLE)) sdCastle(p, res);
    
    return res;
}

vec2 map(in vec3 p, in bool inside, in uint riFlags)
{
    return !inside? mainWorld(p, riFlags): portalWorld(p, riFlags);
}

float portal_intersect(vec3 ro, vec3 rd)
{
    const float planeZ = 0.7;
    const float radius = 0.98;

    if (abs(rd.z) < 1e-6) return 1e5; 
    float t = (planeZ - ro.z) / rd.z;
    if (t <= 0.0) return 1e5;

    vec3 hit = ro + rd * t;

    vec2 center2D = hit.xy - vec2(0.0, 0.1);
    if (length(center2D) < radius)
        return t;

    return 1e5;
}

// ----- Ray Intersection with Object Bounding Box : Start -----
bool rayIntersectsBox(vec3 ro, vec3 rd, vec3 boxCenter, vec3 boxSize, float tmax, inout vec2 tb)
{
    tb = iBox(ro-boxCenter,rd,boxSize);
    
    return (tb.x < tb.y && tb.y > 0.0 && tb.x < tmax);
}

uint getRayIntersectionFlags(vec3 ro, vec3 rd, float tmax)
{
    vec2 tb;
    uint riFlags = 0u;
    
    if(rayIntersectsBox (ro,rd, vec3(0,-.11,0), vec3(1.35,0.075,1.3), tmax, tb))
        riFlags |= RI_GRASSY_GROUND;
        
    if(rayIntersectsBox (ro,rd, vec3(.5,.2,.6), vec3(.7,.32,1.), tmax, tb))
        riFlags |= RI_BIFROST;
        
    if(rayIntersectsBox (ro,rd, vec3(0.01,.2,-.11), vec3(.1,.28,.12), tmax, tb))
        riFlags |= RI_ALICE;
        
    if(rayIntersectsBox(ro,rd, vec3(-.73,.3,.31), vec3(.21,.5,.21), tmax, tb))
        riFlags |= RI_BUNNY;
        
    if(rayIntersectsBox(ro,rd, vec3(.0,.05,-.8), vec3(.84,.15,.13), tmax, tb))
        riFlags |= RI_MUSHROOM;
        
    if(rayIntersectsBox(ro,rd, vec3(-.94,.25,-.3), vec3(.22,.34,.23), tmax, tb))
        riFlags |= RI_TALKING_FLOWER;
        
    if(rayIntersectsBox(ro,rd, vec3(0,-.065,-.15), vec3(1.2,.02,1.), tmax, tb))
        riFlags |= RI_FLOWER_BED;
        
    if(rayIntersectsBox(ro,rd, vec3(0,.26,2.2), vec3(1.2,.27,1.12), tmax, tb))
        riFlags |= RI_CLOUDS;
        
    if(rayIntersectsBox(ro,rd, vec3(0,.28,2.2), vec3(1.,.35, 1.), tmax, tb))
        riFlags |= RI_GRASSY_MOUNTAIN;
        
    if(rayIntersectsBox(ro,rd, vec3(0,.72,2.2),  vec3(.66,.36,.66), tmax, tb))
        riFlags |= RI_CASTLE;

    return riFlags;
}
// ----- Ray Intersection with Object Bounding Box : End -----

// 3D GLOW TUTORIAL by alro : https://www.shadertoy.com/view/7stGWj
float getGlow(float dist, float radius, float intensity)
{
    return pow(radius / max(abs(dist), 1e-6), intensity);
}

vec3 standardMaterial(vec3 base, vec3 shadow, float fresnelMult, float diffuse, float fresnel, float shadowMult) {
    return mix(shadow, base, diffuse) * shadowMult + base * fresnel * fresnelMult;
}

vec3 calcColor(int matId, vec3 p, vec3 normal, float diffuse, float fresnel, bool insidePortal)
{
    vec3 base = vec3(0.0);
    vec3 shadow = vec3(0.0);
    float fresnelMult = 1.0;
    float shadowMult = 1.0;

    vec3 FinalColor = vec3(0.0);

    switch (matId)
    {
        case MAT_GIRL_SKIN:
        case MAT_GIRL_FACE:
            base = vec3(1.0, 0.58, 0.4);
            shadow = vec3(0.78, 0.46, 0.30);
            fresnelMult = 1.2;
            break;

        case MAT_GIRL_BLONDE:
            //base = vec3(0.780, 0.450, 0.080);
            //shadow = vec3(0.680, 0.380, 0.060);
            base = vec3(0.13, 0.05, 0.01);
            shadow = base * 0.5;
            fresnelMult = 3.0;
            break;

        case MAT_GIRL_BOOTS:
            base = vec3(0.02, 0.02, 0.03);
            shadow = vec3(0.01, 0.01, 0.02);
            fresnelMult = 0.3;
            break;

        case MAT_LEAVES:
        case MAT_TREES:
            base = vec3(0.0882, 0.447, 0.04);
            shadow = vec3(0.00582, 0.247, 0.02);
            fresnelMult = 2.5;
            shadowMult = 0.7;
            break;

        case MAT_STEM:
            base = vec3(0.05, 0.25, 0.05);
            shadow = vec3(0.05, 0.15, 0.05);
            fresnelMult = 2.5;
            shadowMult = 0.7;
            break;

        case MAT_GRASS:
            base = vec3(0.0882, 0.247, 0.04);
            shadow = vec3(0.00582, 0.147, 0.02);
            fresnelMult = 1.5;
            break;

        case MAT_MUSHROOM_STEM:
        case MAT_BUNNY:
            base = vec3(0.95, 0.95, 0.9);
            shadow = vec3(0.85, 0.85, 0.8);
            fresnelMult = 2.5;
            break;

        case MAT_SMALL_FLOWER_PISTIL:
            base = vec3(0.780, 0.450, 0.080);
            shadow = vec3(0.680, 0.380, 0.060);
            fresnelMult = 2.5;
            break;

        case MAT_FLOWER_FACE:
            base = vec3(1.00, 0.80, 0.15);
            shadow = vec3(1.0, 0.6, 0.1);
            fresnelMult = 2.5;
            break;

        case MAT_CASTLE_WHITE:
        case MAT_CASTLE_FACADE:
            base = vec3(1.0, 1.0, 1.0);
            shadow = vec3(0.5, 0.8, 0.9);
            fresnelMult = 0.5;
            break;

        case MAT_CASTLE_ROOF:
            base = COLOR_CASTLE_ROOF;
            shadow = base * 0.7;
            fresnelMult = 0.5;
            break;

        case MAT_GROUND:
            base = vec3(0.28, 0.15, 0.05);
            shadow = base * 0.5;
            FinalColor = mix(shadow, base, mix(0.3, 1.0, diffuse));
            return FinalColor + base * fresnel * 0.8;

        case MAT_CLOUD:
            base = vec3(1.0, 1.0, 1.0);
            shadow = vec3(0.18, 0.70, 0.87);
            FinalColor = mix(shadow, base, mix(0.3, 1.0, diffuse));
            return FinalColor + base * fresnel * 0.8;

        case MAT_GIRL_DRESS:
            vec3 pattern = paintGirlDress(p, normal, 50.0);
            base = vec3(0.08, 0.18, 0.42) + pattern;
            shadow = vec3(0.03, 0.09, 0.22) + pattern;
            fresnelMult = 0.4;
            break;

        case MAT_MUSHROOM_CAP:
            paintMushroomCap(p, normal, base, shadow);
            FinalColor = mix(shadow, base, diffuse);
            return FinalColor + vec3(0.8, 0.5, 0.5) * fresnel * 2.5;

        case MAT_BIFROST:
            {
                vec3 bifrost = getBifrostColor(p);
                FinalColor = bifrost + bifrost * fresnel * (insidePortal ? 2.2 : 0.8);
                return FinalColor + vec3(0.5, 0.7, 1.0) * pow(fresnel, 0.5) * (insidePortal ? 0.1 : 0.4);
            }

        default:
            return vec3(0.1, 0.1, 0.1) * diffuse;
    }

    FinalColor = standardMaterial(base, shadow, fresnelMult, diffuse, fresnel, shadowMult);

    switch(matId)
    {
        case MAT_GIRL_FACE:
            paintFace(p, FinalColor);
            break;
        case MAT_FLOWER_FACE:
            paintFlowerFace(p, FinalColor);
            break;
        case MAT_BUNNY:
            paintBunny(p, FinalColor);
            break;
        case MAT_CASTLE_FACADE:
            paintCastleFacade(p, FinalColor);
            break;
    }

    return FinalColor;
}

const vec3 SUN_DIR_PORTAL = normalize(vec3(-1.5, 0.15, 0.2));

vec3 getSkyColor(vec3 dir, bool insidePortal)
{
    if (!insidePortal) {
        float t = smoothstep(0.1,0.6,0.5*(dir.y+1.0));
        return mix(vec3(0.4,0.4,0.2), vec3(0.4,0.6,1.0), t);
    } else {
        float up = dir.y;
        float skyDot = up*0.5 + 0.5;
        
        float sunDot = dot(dir, SUN_DIR_PORTAL);
        float sunProximity = (sunDot + 1.0) * 0.5;
        
        vec3 warm = mix(vec3(1.0,0.35,0.25), vec3(1.0,0.6,0.7), pow(skyDot,0.1));
        
        vec3 cool = mix(vec3(0.2,0.2,0.4), vec3(0.35,0.25,0.7), pow(skyDot,0.5));
        
        float blend = smoothstep(0.1, 0.9, 1.0 - sunProximity);
        vec3 sky = mix(warm, cool, blend);
        
        float sunGlow = max(sunDot, 0.0);
        sky += vec3(1.0,0.48,0.35) * pow(sunGlow, 10.0) * 2.5;
        
        return sky;
    }
}

//--------------------------------------
// RAYMARCHING 
// https://iquilezles.org/articles/rmshadows
//--------------------------------------
float calcSoftshadow( in vec3 ro, in vec3 rd, in float tmin, in float tmax, in bool inside )
{
    uint riFlags = getRayIntersectionFlags(ro, rd, tmax);
    float res = 1.0;
    float t = tmin;
    for( int i=0; i<60; i++ )
    {
		float h = map( ro + rd*t , inside, riFlags).x;
        if (h < 0.0001) return 0.0;
        float s = clamp(30.*h/t,0.0,1.0);
        res = min( res, s*s*(3.0-2.0*s) );
        t += clamp(h, 0.001, 0.025);
        if( res<0.005 || t>tmax ) break;
    }
    return clamp( res, 0.0, 1.0 );
}

// https://iquilezles.org/articles/normalsSDF
vec3 calcNormal( in vec3 p, in bool inside )
{
    // inspired by tdhooper and klems - a way to prevent the compiler from inlining map() 4 times
    vec3 n = vec3(0.0);
    for( int i=0; i<4; i++ )
    {
        vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0);
        n += e*map(p+0.0005*e, inside, 0xFFFu).x;
    }
    return normalize(n);
}

float calcAO( in vec3 p, in vec3 nor, in bool inside )
{
	float occ = 0.0;
    float sca = 1.0;
    for( int i=0; i<5; i++ )
    {
        float hr = 0.001 + 0.003 * float(i)/4.0;
        vec3 aopos =  nor * hr + p;
        float dd = map( aopos , inside, 0xFFFu ).x;
        occ += -(dd-hr)*sca;
        sca *= 0.75;
    }
    return clamp( 1.0 - 2.5*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
}

mat3 setCamera( in vec3 ro, in vec3 ta, float cr )
{
	vec3 cw = normalize(ta-ro);
	vec3 cp = vec3(sin(cr), cos(cr),0.0);
	vec3 cu = normalize( cross(cw,cp) );
	vec3 cv =          ( cross(cu,cw) );
    return mat3( cu, cv, cw );
}

vec4 castRay(vec3 ro, vec3 rd, float time, inout float glow)
{
    float tmin = 3.0, tmax = 8.0, gt = 0.4; vec2 tb;
    bool inside = false;
    float portalDist = portal_intersect(ro, rd);
    glow = 0.0;
    
    if (rayIntersectsBox(ro,rd,vec3(0,.47,1.),vec3(1.31,.72, 2.35), tmax, tb))
    {
        tmin = max(tb.x, tmin);
        tmax = min(tb.y, tmax);
        
        float t = tmin;
        
        uint riFlags = getRayIntersectionFlags(ro, rd, tmax);
        
        for (int i = 0; i < 256; i++)
        {
            if (t > portalDist && portalDist < 1e5)
            {
                inside = !inside;
                portalDist = 1e5;
            }
        
            vec3 p = ro + rd * t;
            vec2 h = map(p, inside, riFlags);
        
            float distToTorus = sdPortalTorus(p);
            if (distToTorus < gt) {
                glow += getGlow(distToTorus, 0.001, 0.55) * smoothstep(1.0, 0.0, distToTorus / gt);
            }
            
            if (distToTorus < h.x)
            {
                h.x = distToTorus;
                h.y = float(MAT_GLOW);
            }
            float distToScene = h.x;
        
            if (abs(distToScene) < 1e-4 * t)
                return vec4(t, distToScene, h.y, inside ? 1.0 : 0.0);
        
            t += min(distToScene * 0.5, 0.12);
            
            if (t > tmax) break;
        }
    }
    
    return vec4(tmax, 1e10, 0.0, inside ? 1.0 : 0.0);
}

vec3 render(in vec3 ro, in vec3 rd, in float t)
{
    float glow = 0.0;
    vec4 res = castRay(ro, rd, t, glow);
    bool insidePortal = res.w > 0.5;
    
    vec3 finalColor = getSkyColor(rd, insidePortal);
    vec3 glowColor = vec3(0.2,0.5,1.0);
    float glowFactor = .1;
    
    if(int(res.z) == MAT_GLOW)
    {
        finalColor = vec3(1);
    }
    else if(res.y < 0.002)
    {
        vec3 lightDir = normalize( insidePortal
            ? SUN_DIR_PORTAL
            : vec3(-0.5,1.1,-0.6) );
        float lightIntensity = insidePortal ? 6.0 : 12.0;
        
        vec3 p = ro + rd * res.x;
        vec3 normal = calcNormal(p, insidePortal);
        float ao = calcAO(p, normal, insidePortal);
        float shadow = calcSoftshadow(p, lightDir, 0.0005, 12., insidePortal);
        float NdL = clamp(dot(normal, lightDir),0.0,1.0);
        float fresnel= pow(clamp(1.0+dot(normal,rd),0.0,1.0),2.4);
        float diffuse= shadow * NdL * lightIntensity;
        
        vec3 surfaceColor = calcColor(int(res.z), p, normal, diffuse, fresnel, insidePortal);
        surfaceColor *= mix(0.22,1.0,ao);
        surfaceColor = surfaceColor*0.4 + 0.6*surfaceColor*getSkyColor(normal,insidePortal);
        
        finalColor = surfaceColor;
        
        if(insidePortal)
            finalColor = mix(finalColor, vec3(0.9, 0.65, 0.7), clamp(1.0 - exp(-.01 * res.x), 0.0, 0.95));
    }
    
    finalColor += glowColor * glow * glowFactor;
    
    return finalColor;
}

void main()
{
    float time = iTime;

    // ————————————————————————————————————————
    // 1. ORBIT SETTINGS
    // ————————————————————————————————————————
    const float ORBIT_RADIUS = 4.6;           // distance from pivot
    const float HEIGHT_MIN   = 0.7;
    const float HEIGHT_RANGE = 3.0;
    const float MAX_MOUSE_YAW_RAD  = radians(75.0);
    const float MAX_TIME_YAW_RAD  = radians(40.0);

    // ————————————————————————————————————————
    // 2. AUTO-OSCILLATION (idle) + MOUSE OVERRIDE
    // ————————————————————————————————————————

    float autoYaw = sin(time * 0.25) * MAX_TIME_YAW_RAD;   // 4-second swing

    float yaw = autoYaw;

    // ————————————————————————————————————————
    // 3. CAMERA POSITION: starts at (0, y, -radius), always Z < 0
    // ————————————————————————————————————————
    float camHeight = HEIGHT_MIN;

    vec3 ro = vec3(
        sin(yaw) * ORBIT_RADIUS,      // X
        camHeight,                    // Y
        -cos(yaw) * ORBIT_RADIUS      // Z → negative! (cos(±100°) > 0 → -cos < 0)
    );

    // ————————————————————————————————————————
    // 4. LOOK-AT: original pivot (0, 0.35, 0)
    // ————————————————————————————————————————
    vec3 ta = vec3(0.0, 0.35, 0.0);

    // ————————————————————————————————————————
    // 5. CAMERA MATRIX & RAY
    // ————————————————————————————————————————
    mat3 ca = setCamera(ro, ta, 0.0);

    vec3 tot = vec3(0.0);

    for( int m=0; m<AA; m++ )
    for( int n=0; n<AA; n++ )
    {
        vec2 o = vec2(float(m),float(n)) / float(AA) - 0.5;
        vec2 p = (-iResolution.xy + 2.0*(gl_FragCoord.xy+o)) / iResolution.y;

        const float fl = 6.0;

        vec3 rd = ca * normalize(vec3(p, fl));

        float t = time * 0.5;
        vec3 col = render(ro, rd, t); 

        col = pow(col, vec3(0.4545));
        col = mix(col, smoothstep(0.0, 1.0, col), 0.5);

        tot += col;
    }
    tot /= float(AA*AA);

    fragColor = vec4(tot, 1.0);
}