Nowadays, it's hard to find new ones.
See title 3.4: Overshooting.
See title 2.5.1: Image coherence. (2nd paragraph)
These articles are from the inventor of sphere tracing himself so... he devised other improvements
Sometime ago I had an idea similar to your second one and have tested it using evaldraw. Thre are two stages:
Dig (/edit) the Z-buffer using the unbounding spheres until their radius is too small.
2- improve the result using raytracing.
() //press F1 for help
{
//static rparam[7][3];//for future use
static colr,colg,colb;
static rmin,xyinc;
static juliadist=2.1,bradius=1.5;
static juliac[4]={-0.25,0.9,0.0,0};
static juliao[4]={0,0,0,0};
static juliax[4]={1,0,0,0};
static juliay[4]={0,1,0,0};
static juliaz[4]={0,0,1,0};
static p[4],tmp1[4],tmp2[4],tmp3[4];
if (numframes==0)
{
cls(0);//cls(0x808080);
clz(1e16);
setcol(80,80,100);
drawsph(0,0,juliadist,bradius);
rmin=3;
xyinc=256;
}
//srand(klock());
//setcol(255,255,255);
if (rmin>1e-3){
rmin*=0.5;
xyinc=ceil(xyinc*0.5);
if(xyinc>1){
for (vy=0; vy<yres;vy+=xyinc)
for (vx=0; vx<xres;vx+=xyinc)
{
do
{
z=getpix(vx,vy,colr,colg,colb);
if (z<juliadist+bradius){
x=(2*vx-xres)*z/xres;
y=(2*vy-yres)*z/xres;
jx=x;jy=y;jz=z-juliadist;
//qrmul(jx,juliax,tmp1);qadd(juliao,tmp1,tmp2);
//qrmul(jy,juliay,tmp1);qadd(tmp1,tmp2,tmp3);
//qrmul(jz,juliaz,tmp1);qadd(tmp1,tmp3,p);
qequate(juliao,p);
p[0]+=jx;p[1]+=jy;p[2]+=jz;
D=getJuliaDist(p,juliac,100,16);
c=sin(60*z)*16+(z-juliadist+bradius)*128;
setcol(c,c,c);
drawhsph(x,y,z,D);
} else {
D=-100; setcol(0);
setpix(vx,vy,1e16);
};
}while(D>rmin);
}
}else{
for (vy=0; vy<yres;vy+=xyinc)
for (vx=0; vx<xres;vx+=xyinc)
{
i=100;
do
{
i--;
z=getpix(vx,vy,colr,colg,colb);
if (z<juliadist+bradius){
x=(2*vx-xres)*z/xres;
y=(2*vy-yres)*z/xres;
jx=x;jy=y;jz=z-juliadist;
//qrmul(jx,juliax,tmp1);qadd(juliao,tmp1,tmp2);
//qrmul(jy,juliay,tmp1);qadd(tmp1,tmp2,tmp3);
//qrmul(jz,juliaz,tmp1);qadd(tmp1,tmp3,p);
qequate(juliao,p);
p[0]+=jx;p[1]+=jy;p[2]+=jz;
D=getJuliaDist(p,juliac,100,16);
c=sin(60*z)*16+(z-juliadist+bradius)*128;
setcol(c,c,c);
setpix(vx,vy,z+D);
//drawhsph(x,y,z,D);
} else {
D=-100; setcol(0);
setpix(vx,vy,1e16);
};
}while((D>rmin) && i>0);
}
}
}
shadeit();
}
shadeit()
{
static lum[3]={0,0,-1};
static colr,colb,colg;
lum[0]=(mousx-0.5*xres)/xres;
lum[1]=(mousy-0.5*yres)/yres;
d=1/sqrt(lum[0]*lum[0]+lum[1]*lum[1]+lum[2]*lum[2]);
lum[0]*=d;lum[1]*=d;lum[2]*=d;
for (vy=0; vy<yres;vy++)
for (vx=0; vx<xres;vx++)
{
z=getpix(vx,vy,colr,colg,colb);
if (z<10)
{
x=(2*vx-xres)*z/xres;
y=(2*vy-yres)*z/xres;
zx=getpix(vx+1,vy,colr,colg,colb);
xx=(2*(vx+1)-xres)*zx/xres;
yx=(2*vy-yres)*zx/xres;
zy=getpix(vx,vy+1,colr,colg,colb);
xy=(2*vx-xres)*zy/xres;
yy=(2*(vy+1)-yres)*zy/xres;
zx-=z;xx-=x;yx-=y;
zy-=z;xy-=x;yy-=y;
if (0){
tmp=zx;zx=-xx;xx=tmp;
tmp=zy;zy=-yy;yy=tmp;
x=xx+xy;y=yx+yy;z=zx+zy;
} else
{x=-yx*zy+zx*yy; y=-zx*xy+xx*zy; z=-xx*yy+yx*xy;}
d=1/sqrt(x*x+y*y+z*z);
c=(x*lum[0]+y*lum[1]+z*lum[2])*d;
c=0.5+0.5*c+0.5*abs(c)^20;
c*=255;
setcol(c,c,c);
setpix(vx,vy);
}
else {
setcol(50,50,50);
setpix(vx,vy);
}
}
}
qadd(in1[4],in2[4],out[4])
{
out[0]=in1[0]+in2[0];out[1]=in1[1]+in2[1];
out[2]=in1[2]+in2[2];out[3]=in1[3]+in2[3];
}
qsub(in1[4],in2[4],out[4])
{
out[0]=in1[0]-in2[0];out[1]=in1[1]-in2[1];
out[2]=in1[2]-in2[2];out[3]=in1[3]-in2[3];
}
qmul(in1[4],in2[4],out[4])
{
out[0]=in1[0]*in2[0]-in1[1]*in2[1]-in1[2]*in2[2]-in1[3]*in2[3];
out[1]=in1[0]*in2[1]+in1[1]*in2[0]+in1[2]*in2[3]-in1[3]*in2[2];
out[2]=in1[0]*in2[2]+in1[2]*in2[0]+in1[3]*in2[1]-in1[1]*in2[3];
out[3]=in1[0]*in2[3]+in1[3]*in2[0]+in1[1]*in2[2]-in1[2]*in2[1];
}
qrmul(r,in[4],out[4])
{
out[0]=r*in[0];out[1]=r*in[1];out[2]=r*in[2];out[3]=r*in[3];
}
qnormsquared(in[4])
{
return in[0]*in[0]+in[1]*in[1]+in[2]*in[2]+in[3]*in[3];
}
qnorm(in[4])
{
return sqrt(in[0]*in[0]+in[1]*in[1]+in[2]*in[2]+in[3]*in[3]);
}
qconjugate(in[4],out[4])
{
out[0]=in[0];out[1]=-in[1];out[2]=-in[2];out[3]=-in[3];
}
qequate(in[4],out[4])
{
out[0]=in[0];out[1]=in[1];out[2]=in[2];out[3]=in[3];
}
getjuliadist(p[4],c[4],esc_r,max_iter)
{
static tmp[4],g[4];
g[0]=1; g[1]=0; g[2]=0; g[3]=0;
i=0;
while(i<max_iter){
if ((qnormsquared(p)>esc_r)){
z=qnorm(p);
return 0.5*z*log(z)/qnorm(g);
}
qmul(p,g,tmp);qrmul(2,tmp,g);
qmul(p,p,tmp);qadd(tmp,c,p);
i++;
}
return 0;
}
//slow and dirty sphere hollow
drawhsph(xc,yc,zc,r)
{
r=abs(r);
if (zc<r) return;//pour l'instant
D=xres*0.5;D1=yres*0.5;
//Bounding rectangle
xmax=-1e30; ymax=-1e30;
A=r*r-zc*zc;
B=zc*xc;
if (abs(A)<1e-10){
xmin=D*(r*r-xc*xc)*0.5/B;
} else {
Delta=B*B-A*(r*r-xc*xc);
A=1/A; Delta=sqrt(Delta);
xmin=ceil(-D*(B-Delta)*A);
xmax=ceil(-D*(B+Delta)*A);
if (xmin<-D) xmin=-D;
if (xmax>D) xmax=D;
}
A=r*r-zc*zc; B=zc*yc;
if (abs(A)<1e-10){
ymin=D*(r*r-yc*yc)*0.5/B;
} else {
Delta=B*B-A*(r*r-yc*yc);
Delta=sqrt(Delta);
A=1/A;
ymin=ceil(-D*(B-Delta)*A);
ymax=ceil(-D*(B+Delta)*A);
if (ymin<-D1) ymin=-D1;
if (ymax>D1) ymax=D1;
}
//A little Hack to obtain current color
static rcol,gcol,bcol,rc,gc,bc;
getpix(xmin+D,ymin+D1,rc,gc,bc);
setpix(xmin+D,ymin+D1);
getpix(xmin+D,ymin+D1,rcol,gcol,bcol);
setcol(rc,gc,bc);
setpix(xmin+D,ymin+D1);
setcol(rcol,gcol,bcol);
//let's rock (slowly!)
C=xc*xc+yc*yc+zc*zc-r*r;r1=1/sqrt(3)/r;
for (yp=ymin;yp<ymax;yp++){
A1=yp*yp+D*D;B1=yp*yc+D*zc;
for (xp=xmin;xp<xmax;xp++){
A=A1+xp*xp; B=B1+xp*xc;
Delta=B*B-A*C;
if (Delta>0){
Delta=sqrt(Delta);A=1/A;
tmin=(B-Delta)*A;zmin=D*tmin;
tmax=(B+Delta)*A;zmax=D*tmax;
z=getpix(xp+D,yp+D1,rc,gc,bc);
if (zmin<z)
if (zmax>z){
//col=(r1*((xp+yp)*tmax-(xc+yc+zc)+zmax));
//if (col<0) col=0;
//col=1+col*0.5;//+col^20;
//setcol(col*rcol,col*gcol,col*bcol);
setpix(xp+D,yp+D1,zmax);
}
}
}
}
setcol(rcol,gcol,bcol);
}
(just copy-past into evaldraw's editor then press F2. If it hangs or displays nothing -there is a bug;)- do [Esc] -options>select compiler>Ken's Eval then [Ctrl]-[Enter])
It's also possible to do coarse to fine rendering. Say... begin with 1/16 resolution with min unbounding sphere apparent radius >= 16x16 pixels then 1/4 resolution begining at previously reached distance... etc.