diff --git a/assets/uts-ondemand.js b/assets/uts-ondemand.js
index 2a3a89a..c2411b3 100644
--- a/assets/uts-ondemand.js
+++ b/assets/uts-ondemand.js
@@ -37,4 +37,10 @@ document.addEventListener('DOMContentLoaded', async () => {
if (embeded_shaders.length != 0) {
load_script('shader.js');
}
+
+ const embeded_shadertoys = document.querySelectorAll('.embeded-shadertoy');
+
+ if (embeded_shadertoys.length != 0) {
+ load_script('shadertoy.js');
+ }
});
\ No newline at end of file
diff --git a/bun/shadertoy.jsx b/bun/shadertoy.jsx
new file mode 100644
index 0000000..cfe6d28
--- /dev/null
+++ b/bun/shadertoy.jsx
@@ -0,0 +1,147 @@
+// The purpose of this file is to run shadertoy shaders without modification in React Three Fiber.
+// All shaders without external resources (e.g. images as sampler2D from channels ) should work out of the box.
+// This file is based on experiments in bun/hello-react-three-fiber.tsx
+// References:
+// - https://blog.maximeheckel.com/posts/the-study-of-shaders-with-react-three-fiber/
+// - https://www.thefrontdev.co.uk/workflow-from-shadertoy-to-react-three-fiber-r3f
+// - https://medium.com/@m.mhde96/implementing-a-shadertoy-in-react-three-fiber-eee4541a15b2
+// bun install three react-dom react @react-three/fiber @react-three/drei
+import * as THREE from 'three'
+import { createRoot } from 'react-dom/client'
+import React, { useRef, useMemo } from 'react'
+import { Canvas, useFrame } from '@react-three/fiber'
+import { Billboard, Text, Plane, CameraControls } from '@react-three/drei'
+
+const defaults = {
+ fragmentShaderPreamble: `varying vec2 vUv;
+uniform vec3 iResolution; // viewport resolution (in pixels)
+uniform float iTime; // shader playback time (in seconds)
+// uniform float iTimeDelta; // render time (in seconds)
+// uniform float iFrameRate; // shader frame rate
+uniform int iFrame; // shader playback frame
+// uniform float iChannelTime[4]; // channel playback time (in seconds)
+// uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
+uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
+// uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube
+// uniform vec4 iDate; // (year, month, day, time in seconds)
+`,
+ fragmentShaderPostemable: `void main() {
+
+ // call void mainImage(out vec4 pixelColor, in vec2 pixelCoordinate)
+ vec4 pixelColor;
+ mainImage(pixelColor, vUv * iResolution.xy);
+ gl_FragColor = pixelColor;
+}`,
+ vertexShader: `varying vec2 vUv;
+
+void main() {
+ vUv = uv;
+ vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+ vec4 viewPosition = viewMatrix * modelPosition;
+ vec4 projectedPosition = projectionMatrix * viewPosition;
+
+ gl_Position = projectedPosition;
+}`};
+
+// uts begin adapted from https://lygia.xyz/resolve.js
+
+// import resolveLygia from "https://lygia.xyz/resolve.esm.js"
+
+async function resolveIncludesAsync(lines) {
+ if (!Array.isArray(lines))
+ lines = lines.split(/\r?\n/);
+
+ let src = "";
+ const response = await Promise.all(
+ lines.map(async (line, i) => {
+ const line_trim = line.trim();
+ if (line_trim.startsWith('#include "lygia')) {
+ let include_url = line_trim.substring(15);
+ include_url = "https://lygia.xyz" + include_url.replace(/\"|\;|\s/g, "");
+ console.debug("fetching", include_url);
+ return fetch(include_url).then((res) => res.text());
+ }
+ // uts begin
+ else if (line_trim.startsWith('#include "') ) {
+ let include_url = line_trim.substring(10);
+ include_url = include_url.replace(/\"|\;|\s/g,'');
+ console.debug("fetching", include_url);
+ return fetch(include_url).then((res) => res.text());
+ }
+ // uts end
+ else
+ return line;
+ })
+ );
+
+ return response.join("\n");
+}
+
+// uts end
+
+const ShaderPlane = (props) => {
+ // This reference will give us direct access to the mesh
+ const mesh = useRef();
+
+ const uniforms = useMemo(
+ () => ({
+ iTime: {
+ value: 0.0,
+ },
+ iFrame: {
+ value: 0.0,
+ },
+ iMouse: {
+ value: new THREE.Vector4(),
+ },
+ iResolution: { value: new THREE.Vector3(16, 9, 1) }
+ }), []
+ );
+
+ useFrame((state) => {
+ const { clock } = state;
+ mesh.current.material.uniforms.iTime.value = clock.getElapsedTime();
+ });
+
+ return (
+
+
+
+ );
+}
+
+const embeded_shaders = document.querySelectorAll('.embeded-shadertoy');
+
+embeded_shaders.forEach((element) => {
+ let shader = element.textContent;
+ element.textContent = '';
+
+ element.classList.add('lazy-loading');
+
+ let handleMouseOver = (event) => {
+ element.removeEventListener('mouseover', handleMouseOver);
+ resolveIncludesAsync(shader).then((shader) => {
+ element.classList.remove('lazy-loading');
+ // const renderer = ImageEffectRenderer.createTemporary(element, shader, options);
+ createRoot(element).render(
+ ,
+ );
+ });
+ };
+
+ element.addEventListener('mouseover', handleMouseOver);
+});
+
+
diff --git a/bun/uts-style.css b/bun/uts-style.css
index c9361eb..a9e0bc0 100644
--- a/bun/uts-style.css
+++ b/bun/uts-style.css
@@ -568,13 +568,18 @@ button#langblock-toggle {
}
}
-.embeded-shader {
+.embeded-shader, .embeded-shadertoy {
color: transparent;
padding: 1em 1em 1em 1em;
max-height: 90ex;
}
-.embeded-shader.lazy-loading {
+.embeded-shadertoy {
+ width: 80ex;
+ height: 45ex;
+}
+
+.embeded-shader.lazy-loading, .embeded-shadertoy.lazy-loading {
color: transparent;
height: 70ex;
background-image: url(./images/3d-cube-scan-hover.svg);
diff --git a/dev.sh b/dev.sh
index 5b58564..93acd55 100755
--- a/dev.sh
+++ b/dev.sh
@@ -5,7 +5,7 @@
# rm -rf output
./build.sh
-watchexec --quiet --no-vcs-ignore --project-origin . --on-busy-update queue --poll 500ms -e tree,tex,css,js,xsl,ts,tsx,glsl,typ,domain,style,substance,trio.json -w trees -w assets -w tex -w bun --emit-events-to=stdio -- ./build_changed.sh &
+watchexec --quiet --no-vcs-ignore --project-origin . --on-busy-update queue --poll 500ms -e tree,tex,css,js,jsx,xsl,ts,tsx,glsl,typ,domain,style,substance,trio.json -w trees -w assets -w tex -w bun --emit-events-to=stdio -- ./build_changed.sh &
http-server -p 1314 output &
diff --git a/trees/uts-0017.tree b/trees/uts-0017.tree
new file mode 100644
index 0000000..c6431ca
--- /dev/null
+++ b/trees/uts-0017.tree
@@ -0,0 +1,652 @@
+\import{macros}
+% clifford hopf spin tt ag math draft tech exp notes
+\tag{tech}
+\tag{exp}
+
+\p{From \link{https://www.shadertoy.com/view/Xds3zN}:
+}
+
+\note{test shadertoy}{
+\[class]{embeded-shadertoy}\verb>>>|
+#define AA 1
+
+//------------------------------------------------------------------
+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 sdPlane( vec3 p )
+{
+ return p.y;
+}
+
+float sdSphere( vec3 p, float s )
+{
+ return length(p)-s;
+}
+
+float sdBox( vec3 p, vec3 b )
+{
+ vec3 d = abs(p) - b;
+ return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
+}
+
+float sdBoxFrame( vec3 p, vec3 b, float e )
+{
+ p = abs(p )-b;
+ vec3 q = abs(p+e)-e;
+
+ return min(min(
+ length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0),
+ length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)),
+ length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0));
+}
+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 sdCappedTorus(in vec3 p, in vec2 sc, in float ra, in float rb)
+{
+ p.x = abs(p.x);
+ float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy);
+ return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
+}
+
+float sdHexPrism( vec3 p, vec2 h )
+{
+ vec3 q = abs(p);
+
+ const vec3 k = vec3(-0.8660254, 0.5, 0.57735);
+ p = abs(p);
+ p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy;
+ vec2 d = vec2(
+ length(p.xy - vec2(clamp(p.x, -k.z*h.x, k.z*h.x), h.x))*sign(p.y - h.x),
+ p.z-h.y );
+ return min(max(d.x,d.y),0.0) + length(max(d,0.0));
+}
+
+float sdOctogonPrism( in vec3 p, in float r, float h )
+{
+ const vec3 k = vec3(-0.9238795325, // sqrt(2+sqrt(2))/2
+ 0.3826834323, // sqrt(2-sqrt(2))/2
+ 0.4142135623 ); // sqrt(2)-1
+ // reflections
+ p = abs(p);
+ p.xy -= 2.0*min(dot(vec2( k.x,k.y),p.xy),0.0)*vec2( k.x,k.y);
+ p.xy -= 2.0*min(dot(vec2(-k.x,k.y),p.xy),0.0)*vec2(-k.x,k.y);
+ // polygon side
+ p.xy -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
+ vec2 d = vec2( length(p.xy)*sign(p.y), p.z-h );
+ return min(max(d.x,d.y),0.0) + length(max(d,0.0));
+}
+
+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 sdRoundCone( in vec3 p, in float r1, float r2, float h )
+{
+ vec2 q = vec2( length(p.xz), p.y );
+
+ float b = (r1-r2)/h;
+ float a = sqrt(1.0-b*b);
+ float k = dot(q,vec2(-b,a));
+
+ if( k < 0.0 ) return length(q) - r1;
+ if( k > a*h ) return length(q-vec2(0.0,h)) - r2;
+
+ return dot(q, vec2(a,b) ) - r1;
+}
+
+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 sdTriPrism( vec3 p, vec2 h )
+{
+ const float k = sqrt(3.0);
+ h.x *= 0.5*k;
+ p.xy /= h.x;
+ p.x = abs(p.x) - 1.0;
+ p.y = p.y + 1.0/k;
+ if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0;
+ p.x -= clamp( p.x, -2.0, 0.0 );
+ float d1 = length(p.xy)*sign(-p.y)*h.x;
+ float d2 = abs(p.z)-h.y;
+ return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
+}
+
+// vertical
+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));
+}
+
+// arbitrary orientation
+float sdCylinder(vec3 p, vec3 a, vec3 b, float r)
+{
+ vec3 pa = p - a;
+ vec3 ba = b - a;
+ float baba = dot(ba,ba);
+ float paba = dot(pa,ba);
+
+ float x = length(pa*baba-ba*paba) - r*baba;
+ float y = abs(paba-baba*0.5)-baba*0.5;
+ float x2 = x*x;
+ float y2 = y*y*baba;
+ float d = (max(x,y)<0.0)?-min(x2,y2):(((x>0.0)?x2:0.0)+((y>0.0)?y2:0.0));
+ return sign(d)*sqrt(abs(d))/baba;
+}
+
+// vertical
+float sdCone( in vec3 p, in vec2 c, float h )
+{
+ vec2 q = h*vec2(c.x,-c.y)/c.y;
+ 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 sdCappedCone( in vec3 p, in float h, in float r1, in float r2 )
+{
+ vec2 q = vec2( length(p.xz), p.y );
+
+ vec2 k1 = vec2(r2,h);
+ vec2 k2 = vec2(r2-r1,2.0*h);
+ vec2 ca = vec2(q.x-min(q.x,(q.y < 0.0)?r1:r2), abs(q.y)-h);
+ vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 );
+ float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0;
+ return s*sqrt( min(dot2(ca),dot2(cb)) );
+}
+
+float sdCappedCone(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) );
+}
+
+// c is the sin/cos of the desired cone angle
+float sdSolidAngle(vec3 pos, vec2 c, float ra)
+{
+ vec2 p = vec2( length(pos.xz), pos.y );
+ float l = length(p) - ra;
+ float m = length(p - c*clamp(dot(p,c),0.0,ra) );
+ return max(l,m*sign(c.y*p.x-c.x*p.y));
+}
+
+float sdOctahedron(vec3 p, float s)
+{
+ p = abs(p);
+ float m = p.x + p.y + p.z - s;
+
+ // exact distance
+ #if 0
+ vec3 o = min(3.0*p - m, 0.0);
+ o = max(6.0*p - m*2.0 - o*3.0 + (o.x+o.y+o.z), 0.0);
+ return length(p - s*o/(o.x+o.y+o.z));
+ #endif
+
+ // exact distance
+ #if 1
+ vec3 q;
+ if( 3.0*p.x < m ) q = p.xyz;
+ else if( 3.0*p.y < m ) q = p.yzx;
+ else if( 3.0*p.z < m ) q = p.zxy;
+ else return m*0.57735027;
+ float k = clamp(0.5*(q.z-q.y+s),0.0,s);
+ return length(vec3(q.x,q.y-s+k,q.z-k));
+ #endif
+
+ // bound, not exact
+ #if 0
+ return m*0.57735027;
+ #endif
+}
+
+float sdPyramid( in vec3 p, in float h )
+{
+ float m2 = h*h + 0.25;
+
+ // symmetry
+ p.xz = abs(p.xz);
+ p.xz = (p.z>p.x) ? p.zx : p.xz;
+ p.xz -= 0.5;
+
+ // project into face plane (2D)
+ vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y);
+
+ float s = max(-q.x,0.0);
+ float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 );
+
+ float a = m2*(q.x+s)*(q.x+s) + q.y*q.y;
+ float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t);
+
+ float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b);
+
+ // recover 3D and scale, and add sign
+ return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y));;
+}
+
+// la,lb=semi axis, h=height, ra=corner
+float sdRhombus(vec3 p, float la, float lb, float h, float ra)
+{
+ p = abs(p);
+ vec2 b = vec2(la,lb);
+ float f = clamp( (ndot(b,b-2.0*p.xz))/dot(b,b), -1.0, 1.0 );
+ vec2 q = vec2(length(p.xz-0.5*b*vec2(1.0-f,1.0+f))*sign(p.x*b.y+p.z*b.x-b.x*b.y)-ra, p.y-h);
+ return min(max(q.x,q.y),0.0) + length(max(q,0.0));
+}
+
+float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w )
+{
+ p.x = abs(p.x);
+ float l = length(p.xy);
+ p.xy = mat2(-c.x, c.y,
+ c.y, c.x)*p.xy;
+ p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
+ (p.x>0.0)?p.y:l );
+ p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
+
+ vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
+ vec2 d = abs(q) - w;
+ return min(max(d.x,d.y),0.0) + length(max(d,0.0));
+}
+
+float sdU( in vec3 p, in float r, in float le, vec2 w )
+{
+ p.x = (p.y>0.0) ? abs(p.x) : length(p.xy);
+ p.x = abs(p.x-r);
+ p.y = p.y - le;
+ float k = max(p.x,p.y);
+ vec2 q = vec2( (k<0.0) ? -k : length(max(p.xy,0.0)), abs(p.z) ) - w;
+ return length(max(q,0.0)) + min(max(q.x,q.y),0.0);
+}
+
+//------------------------------------------------------------------
+
+vec2 opU( vec2 d1, vec2 d2 )
+{
+ return (d1.x0.0 )
+ {
+ tmax = min( tmax, tp1 );
+ res = vec2( tp1, 1.0 );
+ }
+ //else return res;
+
+ // raymarch primitives
+ vec2 tb = iBox( ro-vec3(0.0,0.4,-0.5), rd, vec3(2.5,0.41,3.0) );
+ if( tb.x0.0 && tb.x0.0 ) tmax = min( tmax, tp );
+
+ float res = 1.0;
+ float t = mint;
+ for( int i=ZERO; i<24; i++ )
+ {
+ float h = map( ro + rd*t ).x;
+ float s = clamp(8.0*h/t,0.0,1.0);
+ res = min( res, s );
+ t += clamp( h, 0.01, 0.2 );
+ if( res<0.004 || t>tmax ) break;
+ }
+ res = clamp( res, 0.0, 1.0 );
+ return res*res*(3.0-2.0*res);
+}
+
+// https://iquilezles.org/articles/normalsSDF
+vec3 calcNormal( in vec3 pos )
+{
+#if 0
+ vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
+ return normalize( e.xyy*map( pos + e.xyy ).x +
+ e.yyx*map( pos + e.yyx ).x +
+ e.yxy*map( pos + e.yxy ).x +
+ e.xxx*map( pos + e.xxx ).x );
+#else
+ // inspired by tdhooper and klems - a way to prevent the compiler from inlining map() 4 times
+ vec3 n = vec3(0.0);
+ for( int i=ZERO; i<4; i++ )
+ {
+ vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0);
+ n += e*map(pos+0.0005*e).x;
+ //if( n.x+n.y+n.z>100.0 ) break;
+ }
+ return normalize(n);
+#endif
+}
+
+// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf
+float calcAO( in vec3 pos, in vec3 nor )
+{
+ float occ = 0.0;
+ float sca = 1.0;
+ for( int i=ZERO; i<5; i++ )
+ {
+ float h = 0.01 + 0.12*float(i)/4.0;
+ float d = map( pos + h*nor ).x;
+ occ += (h-d)*sca;
+ sca *= 0.95;
+ if( occ>0.35 ) break;
+ }
+ return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
+}
+
+// https://iquilezles.org/articles/checkerfiltering
+float checkersGradBox( in vec2 p, in vec2 dpdx, in vec2 dpdy )
+{
+ // filter kernel
+ vec2 w = abs(dpdx)+abs(dpdy) + 0.001;
+ // analytical integral (box filter)
+ vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
+ // xor pattern
+ return 0.5 - 0.5*i.x*i.y;
+}
+
+vec3 render( in vec3 ro, in vec3 rd, in vec3 rdx, in vec3 rdy )
+{
+ // background
+ vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3;
+
+ // raycast scene
+ vec2 res = raycast(ro,rd);
+ float t = res.x;
+ float m = res.y;
+ if( m>-0.5 )
+ {
+ vec3 pos = ro + t*rd;
+ vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos );
+ vec3 ref = reflect( rd, nor );
+
+ // material
+ col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) );
+ float ks = 1.0;
+
+ if( m<1.5 )
+ {
+ // project pixel footprint into the plane
+ vec3 dpdx = ro.y*(rd/rd.y-rdx/rdx.y);
+ vec3 dpdy = ro.y*(rd/rd.y-rdy/rdy.y);
+
+ float f = checkersGradBox( 3.0*pos.xz, 3.0*dpdx.xz, 3.0*dpdy.xz );
+ col = 0.15 + f*vec3(0.05);
+ ks = 0.4;
+ }
+
+ // lighting
+ float occ = calcAO( pos, nor );
+
+ vec3 lin = vec3(0.0);
+
+ // sun
+ {
+ vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) );
+ vec3 hal = normalize( lig-rd );
+ float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
+ //if( dif>0.0001 )
+ dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
+ float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0);
+ spe *= dif;
+ spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0);
+ //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0);
+ lin += col*2.20*dif*vec3(1.30,1.00,0.70);
+ lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
+ }
+ // sky
+ {
+ float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 ));
+ dif *= occ;
+ float spe = smoothstep( -0.2, 0.2, ref.y );
+ spe *= dif;
+ spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 );
+ //if( spe>0.001 )
+ spe *= calcSoftshadow( pos, ref, 0.02, 2.5 );
+ lin += col*0.60*dif*vec3(0.40,0.60,1.15);
+ lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks;
+ }
+ // back
+ {
+ float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
+ dif *= occ;
+ lin += col*0.55*dif*vec3(0.25,0.25,0.25);
+ }
+ // sss
+ {
+ float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0);
+ dif *= occ;
+ lin += col*0.25*dif*vec3(1.00,1.00,1.00);
+ }
+
+ col = lin;
+
+ col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) );
+ }
+
+ return vec3( clamp(col,0.0,1.0) );
+}
+
+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 );
+}
+
+void mainImage( out vec4 fragColor, in vec2 fragCoord )
+{
+ vec2 mo = iMouse.xy/iResolution.xy;
+ float time = 32.0 + iTime*1.5;
+
+ // camera
+ vec3 ta = vec3( 0.25, -0.75, -0.75 );
+ vec3 ro = ta + vec3( 4.5*cos(0.1*time + 7.0*mo.x), 2.2, 4.5*sin(0.1*time + 7.0*mo.x) );
+ // camera-to-world transformation
+ mat3 ca = setCamera( ro, ta, 0.0 );
+
+ vec3 tot = vec3(0.0);
+#if AA>1
+ for( int m=ZERO; m1
+ }
+ tot /= float(AA*AA);
+#endif
+
+ fragColor = vec4( tot, 1.0 );
+}
+>>>
+}
\ No newline at end of file