Skip to content

Instantly share code, notes, and snippets.

@diska
Created October 21, 2018 04:14
Show Gist options
  • Save diska/9cbc2c5c28f08236da395786b9346da8 to your computer and use it in GitHub Desktop.
Save diska/9cbc2c5c28f08236da395786b9346da8 to your computer and use it in GitHub Desktop.
三葉レイ動画rt01の33:21時点(最終)相当のコードのGLSL化(Firefoxで軽く、Vivaldiで重い)。
<meta charset="utf-8"><br/>
<input id="SPP" type="range" min="0" max="8" value="2"><input id="SPPT"><br/>
<input id="DPP" type="range" min="1" max="7" value="2"><input id="DPPT"><br/>
<canvas id="CNVS"></canvas><hr/>
<script>
const vsrc=`attribute vec4 p;void main(){gl_Position=p;}`;
const fsrc0c=`precision highp float;
struct Ray{ vec3 o, d;};
struct Sph{ vec3 o; float r; vec3 ref; vec3 le; int m;float ior;};
const float PI=3.14;
const float fov=30.*PI/180.;
uniform Sph sphs[8];
// camera parameters for Cornell Box scene
uniform vec3 eye, center, up;
uniform int uIter;
uniform int uDepth;
uniform float uSeed;
vec3 tonemap(vec3 c){
return clamp(pow(c, vec3(1./2.2)), 0.,1.);
}
float rand(vec2 st){
return fract(sin(dot(st.xy, vec2(1234,5678))+uSeed)*1234.5678);
}
void tangentSpace(in vec3 n, out vec3 ps, out vec3 pt) {
float s= 1.*sign(n.z);
float a=-1./(s + n.z);
float b=n.x*n.y*a;
ps=vec3(1.+s*n.x*n.x*a, s*b, -s*n.x);
pt=vec3(b, s+n.y*n.y*a, -n.y);
return;
}
// basis vectors for camera coordinates
mat3 getUvw(){
vec3 wE=normalize(eye-center);
vec3 uE=normalize(cross(up, wE));
vec3 vE=cross(wE, uE);
return mat3(uE,vE,wE);
}
const float tf=tan(fov*.5);
`;
const fsrc1=fsrc0c+`
float intersect(Sph sph, Ray ray, float tmin, float tmax){
vec3 op=sph.o-ray.o;
float bo=dot(op, ray.d);
float det=bo*bo-dot(op,op)+sph.r*sph.r;
if(det<0.){return -1.;}
float t1=bo-sqrt(det); if(tmin<t1&&t1<tmax){return t1;}
float t2=bo+sqrt(det); if(tmin<t2&&t2<tmax){return t2;}
return -1.;
}
Ray mode0(Ray ray, Sph sph, vec3 n, vec3 p, vec2 rp, int depth){
ray.o=p;
float sd=float(depth)/11.11;
vec3 na=dot(n, -ray.d)>0.?n:-n;
vec3 u,v; tangentSpace(na, u, v);
float r=sqrt(rand(rp+vec2(sd,tan(sd))));
float ta=(2.*PI)*rand(rp+vec2(sd,tan(sd)+.1));
float xa=r*cos(ta), ya=r*sin(ta);
vec3 d=vec3(xa, ya, sqrt(max(.0, 1.-xa*xa-ya*ya)));
ray.d=u*d.x+v*d.y+na*d.z;
return ray;
}
Ray mode1(Ray ray, Sph sph, vec3 n, vec3 p){
ray.o=p;
// ray.d=reflect(ray.d, n);
vec3 wi=-ray.d;
ray.d=2.*dot(wi, n)*n-wi;
return ray;
}
Ray mode2(Ray ray, Sph sph, vec3 n, vec3 p){
ray.o=p;
vec3 wi=-ray.d;
bool into=dot(wi, n)>0.;
vec3 na=into?n:-n;
float ior=sph.ior;
float eta=into?1./ior:ior;
// ray.d=-refract(ray.d, na, eta);
float t=dot(wi, na);
float t2=1.-eta*eta*(1.-t*t);
if(t2<0.){
// Total internal reflection
ray.d=2.*dot(wi, n)*n-wi;
}else{
vec3 wt=eta*(na*t-wi)-n*sqrt(t2);
// Schlick's approximation
float cos=into?dot(wi, n):dot(wt, n);
float r=(1.-ior)/(1.+ior);
float Fr=r*r+(1.-r*r)*pow(1.-cos,5.);
ray.d=(rand(vec2(.1))<Fr)?(2.*dot(wi, n)*n-wi):wt;
}
return ray;
}
void main(){
const float width=800., height=600.;
const float aspect=width/height;
int spp=(uIter==0)?4:uIter;
int dpp=(uDepth==0)?2:uDepth;
vec2 rpxy=vec2(gl_FragCoord.x/width, gl_FragCoord.y/height)*2.-1.;
vec3 I=vec3(0);
Sph sph;
for(int j=0; j<512; j++){
if(j>=spp)break;
vec2 rp=rpxy;
rp+=vec2(rand(vec2(j))/width,rand(vec2(j))/height);
vec3 wo=normalize(vec3(aspect*tf*rp.x, tf*rp.y, -1));
Ray ray; ray.o=eye; ray.d=getUvw()*wo;
vec3 L=vec3(0), th=vec3(1);
for(int depth=0; depth<10; depth++){
if(depth>=dpp)break;
float minh=1e6;
for(int i=0; i<8; i++){
float h=intersect(sphs[i], ray, 1e-1, minh);
if(h<0.)continue; // ハズレ。次。
sph=sphs[i]; // 今見てる球
minh=h; // が一番近い。
}
if(minh>=1e6){break;} // 全部ハズレ。次。
L+=th*sph.le;
vec3 p=ray.o+ray.d*minh; // 球上の交点
vec3 n=(p-sph.o)/sph.r; // 球上の法線
if(sph.m==0){
ray=mode0(ray, sph, n, p, rp,depth);
th*=sph.ref;
if(max(max(th.x, th.y),th.z)==0.)break;
}
else if(sph.m==1){
ray=mode1(ray, sph, n, p);
}else if(sph.m==2){
ray=mode2(ray, sph, n, p);
}
}
I+=L/float(spp);
}
gl_FragColor=vec4(tonemap(abs(I)),1);
}`;
// ポリゴン描画するだけであとの処理はFragmentShaderに任せる。
var cx, pg;
const width=800, height=600; // Image size
function getCx(){
CNVS.width=width, CNVS.height=height;
cx=CNVS.getContext("webgl");
var vs=cx.createShader(cx.VERTEX_SHADER);cx.shaderSource(vs,vsrc);
var fs=cx.createShader(cx.FRAGMENT_SHADER);cx.shaderSource(fs,fsrc1);
pg=cx.createProgram();cx.attachShader(pg,vs);cx.attachShader(pg,fs);
cx.compileShader(vs);cx.compileShader(fs);cx.linkProgram(pg);
console.log(`vs:${cx.getShaderInfoLog(vs)}\n`);
console.log(`fs:${cx.getShaderInfoLog(fs)}\n`);
console.log(`pg:${cx.getProgramInfoLog(pg)}\n`);
cx.enableVertexAttribArray(0);
var abuf=cx.createBuffer(cx.ARRAY_BUFFER);
var data=new Float32Array([-1,-1, 1,-1, -1,1, 1,1]);
cx.bindBuffer(cx.ARRAY_BUFFER,abuf);
cx.bufferData(cx.ARRAY_BUFFER, data, cx.STATIC_DRAW);
cx.vertexAttribPointer(0, 2,cx.FLOAT, false, 0,0);
cx.bindBuffer(cx.ARRAY_BUFFER,null);
cx.useProgram(pg);
}; getCx();
function setScene(cx, pg){// Cornell Box scene
// struct Sph{ vec3 o; float r; vec3 ref; vec3 le; int m;float ior;};
const bnum=1e5; // =1e5;
var sphs={}, F=3+1+3+3+1+1;
const dat=([
bnum+1., 40.8, 81.6, bnum, .75,.25,.25, 0,0,0, 0,0,
-bnum+99., 40.8, 81.6, bnum, .25,.25,.75, 0,0,0, 0,0,
50., 40.8, bnum, bnum, .75,.75,.75, 0,0,0, 0,0,
50., bnum, 81.6, bnum, .75,.75,.75, 0,0,0, 0,0,
50., -bnum+81.6, 81.6, bnum, .75,.75,.75, 0,0,0, 0,0,
27., 16.5, 47., 16.5, .99,.99,.99, 0,0,0, 0,0,
73., 16.5, 78., 16.5, .99,.99,.99, 0,0,0, 0,1.5168,
50., 681.6-.27, 81.6, 600, .0,.0,.0, 12,12,12, 0,0,
]);
for(i=0; i<8; i++){
sphs.o=cx.getUniformLocation(pg, `sphs[${i}].o`);
cx.uniform3f(sphs.o, dat[i*F+0], dat[i*F+1], dat[i*F+2]);
sphs.r=cx.getUniformLocation(pg, `sphs[${i}].r`);
cx.uniform1f(sphs.r, dat[i*F+3]);
sphs.ref=cx.getUniformLocation(pg, `sphs[${i}].ref`);
cx.uniform3f(sphs.ref, dat[i*F+4], dat[i*F+5], dat[i*F+6]);
sphs.le=cx.getUniformLocation(pg, `sphs[${i}].le`);
cx.uniform3f(sphs.le, dat[i*F+7], dat[i*F+8], dat[i*F+9]);
sphs.m=cx.getUniformLocation(pg, `sphs[${i}].m`);
cx.uniform1i(sphs.m, dat[i*F+10]);
sphs.ior=cx.getUniformLocation(pg, `sphs[${i}].ior`);
cx.uniform1f(sphs.ior, dat[i*F+11]);
}
var view={};
view.eye=cx.getUniformLocation(pg, "eye");
cx.uniform3f(view.eye, 50.,52.,295.6);
view.center=cx.getUniformLocation(pg, "center");
cx.uniform3f(view.center, 50.+0., 52.-0.042612, 295.6-1.);
view.up=cx.getUniformLocation(pg, "up");
cx.uniform3f(view.up, 0,1,0);
}
setScene(cx, pg);
function setParam(cx,pg){
SPPT.value=`spp=${Math.pow(2,SPP.value)}`;
uIter=cx.getUniformLocation(pg, "uIter");
cx.uniform1i(uIter, Math.pow(2,SPP.value));
DPPT.value=`dpp=${DPP.value}`;
uDepth=cx.getUniformLocation(pg, "uDepth");
cx.uniform1i(uDepth, parseInt(DPP.value));
// uSeed=cx.getUniformLocation(pg, "uSeed");
// cx.uniform1f(uSeed, Math.random());
}
function draw(){
setParam(cx,pg);
// cx.clearColor(.5,.5,.5,1);
// cx.clear(0x4000);
cx.drawArrays(cx.TRIANGLE_STRIP,0,4);
// requestAnimationFrame(draw);
}; draw();
SPP.addEventListener("input", draw);
DPP.addEventListener("input", draw);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment