Skip to content

Commit 1e98b5b

Browse files
authored
Add files via upload
1 parent d4ce703 commit 1e98b5b

File tree

5 files changed

+365
-0
lines changed

5 files changed

+365
-0
lines changed

map2.bin.png

1.25 MB
Loading

pathTrace.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<HEAD>
2+
</HEAD>
3+
<BODY>
4+
<H1>webGL 2 Path Tracer - by Enki</H1>
5+
<A HREF="https://enkimute.github.io/webgl2PathTracing.js/">more info here ..</A>
6+
<DIV ID="info"></DIV>
7+
<SCRIPT SRC="pathTrace.js"></SCRIPT>
8+
</BODY>

pathTrace.js

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// helpers .. merge and download binary file.
2+
function m(a,b) { for (var i in b) a[i]=b[i]; return a; }
3+
function fileAsArray(name,complete,prog) {
4+
var r=m(new XMLHttpRequest(),{responseType:'arraybuffer',onprogress:prog,onload:function(){complete&&complete(this.response)}});
5+
r.open("GET",name,true); r.send();
6+
}
7+
8+
// helpers ..
9+
function rX(m, angle) {
10+
var s=Math.sin(angle),c=Math.cos(angle),a10=m[4],a11=m[5],a12=m[6],a13=m[7],a20=m[8],a21=m[9],a22=m[10],a23=m[11];
11+
m[4]=a10*c+a20*s;m[5]=a11*c+a21*s;m[6]=a12*c+a22*s;m[7]=a13*c+a23*s;
12+
m[8]=a10*-s+a20*c;m[9]=a11*-s+a21*c;m[10]=a12*-s+a22*c;m[11]=a13*-s+a23*c;
13+
return m;
14+
};
15+
function rY(m, angle) {
16+
var s=Math.sin(angle),c=Math.cos(angle),a00=m[0],a01=m[1],a02=m[2],a03=m[3],a20=m[8],a21=m[9],a22=m[10],a23=m[11];
17+
m[0]=a00*c+a20*-s;m[1]=a01*c+a21*-s;m[2]=a02*c+a22*-s;m[3]=a03*c+a23*-s;
18+
m[8]=a00*s+a20*c;m[9]=a01*s+a21*c;m[10]=a02*s+a22*c;m[11]=a03*s+a23*c;
19+
return m;
20+
};
21+
function t(m,v) {
22+
var x=v[0],y=v[1],z=v[2];
23+
m[12]=m[0]*x+m[4]*y+m[8]*z+m[12];
24+
m[13]=m[1]*x+m[5]*y+m[9]*z+m[13];
25+
m[14]=m[2]*x+m[6]*y+m[10]*z+m[14];
26+
m[15]=m[3]*x+m[7]*y+m[11]*z+m[15];
27+
return m;
28+
}
29+
function i() { return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]) };
30+
31+
// png compressed raw file support..
32+
function png_to_raw(png) {
33+
var c = document.createElement('canvas'), x=c.width=png.width, y=c.height=png.height, gl=c.getContext('webgl');
34+
35+
var texture = gl.createTexture();
36+
gl.bindTexture(gl.TEXTURE_2D, texture);
37+
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, png);
38+
39+
fb = gl.createFramebuffer();
40+
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
41+
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
42+
43+
var res = new Uint8Array(x*y*4);
44+
gl.readPixels(0,0,x,y,gl.RGBA,gl.UNSIGNED_BYTE,res);
45+
46+
gl.deleteTexture(texture);
47+
gl.deleteFramebuffer(fb);
48+
return res.buffer;
49+
}
50+
51+
// Download polygon and accelerator data. (they're called .html to fool github pages into compressing them ..)
52+
var todo=2,map,polyData;
53+
54+
var mapI = new Image(), polyDataI = new Image();
55+
mapI.onload = function() { map = new Float32Array(png_to_raw(this)); if(!--todo) allthere(); };
56+
polyDataI.onload = function() { polyData = new Float32Array(png_to_raw(this)); if (!--todo) allthere()};
57+
mapI.src = 'map2.bin.png';
58+
polyDataI.src = 'polydata2.bin.png';
59+
60+
// All data is there .. compile shaders and render ..
61+
function allthere(){
62+
63+
// settings.
64+
var gridres=128,totX=4096,totY=391,pbrtShowrender=true,pbrtBounces=2,pbrtBatch=1,pbrtSamples=Math.floor((parseInt(document.location.hash.slice(1))||200)/pbrtBatch);
65+
var pbrtGrid = { bbox : new Float32Array([-16.35320053100586,-3.3039399147033692,-13.719999885559082,31.68820018768311,13.706639957427978,24.6798002243042])};
66+
67+
// Shader helpers.
68+
function createShader(gl, source, type) { var shader=gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader; }
69+
function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
70+
var program = gl.createProgram();
71+
gl.attachShader(program, createShader(gl, vertexShaderSource, gl.VERTEX_SHADER));
72+
gl.attachShader(program, createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER));
73+
gl.linkProgram(program);
74+
return program;
75+
};
76+
77+
// Offscreen float buffers.
78+
function createOffscreen(gl,width,height) {
79+
var colorTexture = gl.createTexture();
80+
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
81+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0, gl.RGBA, gl.FLOAT, null);
82+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
83+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
84+
gl.bindTexture(gl.TEXTURE_2D, null);
85+
86+
var depthBuffer = gl.createRenderbuffer();
87+
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
88+
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
89+
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
90+
91+
var framebuffer = gl.createFramebuffer();
92+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
93+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
94+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
95+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
96+
97+
return {
98+
framebuffer:framebuffer,colorTexture:colorTexture,depthBuffer:depthBuffer,width:width,height:height,gl:gl,
99+
bind : function() { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer); this.gl.viewport(0,0,this.width,this.height); },
100+
unbind : function() { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); },
101+
delete : function() { this.gl.deleteRenderbuffer(this.depthBuffer); this.gl.deleteFramebuffer(this.framebuffer); this.gl.deleteTexture(this.colorTexture); }
102+
}
103+
}
104+
105+
// setup output canvas, gl context and offscreen.
106+
var canvas = m(document.body.appendChild(document.createElement('canvas')),{width:800,height:450});
107+
m(canvas.style,{width:'800px',height:'450px'});
108+
var gl = canvas.getContext('webgl2'); if (!gl) alert('webGL2 required !');
109+
var ext1 = gl.getExtension('EXT_color_buffer_float'); if (!ext1) alert('videocard required');
110+
var ofscreen1 = createOffscreen(gl,canvas.width,canvas.height);
111+
112+
// Shaders for accumulation and tonemap.
113+
var vs2=`#version 300 es
114+
#define POSITION_LOCATION 0
115+
precision lowp float;
116+
layout(location = POSITION_LOCATION) in vec3 position;
117+
void main() { gl_Position = vec4(position, 1.0); }`;
118+
119+
var fs2=`#version 300 es
120+
precision lowp float;
121+
precision lowp sampler2D;
122+
uniform sampler2D accum;
123+
uniform float count;
124+
out vec4 color;
125+
void main() { color = vec4(sqrt(texelFetch(accum,ivec2(gl_FragCoord.xy),0).rgb*count),1.0); }
126+
`;
127+
128+
// Actual Path tracing shaders.
129+
var vs=`#version 300 es
130+
#define POSITION_LOCATION 0
131+
precision highp float;
132+
precision highp int;
133+
uniform mat4 MVP;
134+
layout(location = POSITION_LOCATION) in vec3 position;
135+
out vec3 origin;
136+
void main()
137+
{
138+
origin = (MVP * vec4(0.0,0.0,0.0,1.0)).xyz;
139+
gl_Position = vec4(position, 1.0);
140+
}`;
141+
142+
var fs=`#version 300 es
143+
precision highp float;
144+
precision highp int;
145+
precision highp sampler3D;
146+
precision highp sampler2D;
147+
148+
#define EPSILON 0.000001
149+
150+
uniform vec2 resolution;
151+
uniform float inseed;
152+
uniform int incount;
153+
154+
uniform mat4 MVP, proj;
155+
156+
uniform sampler3D grid;
157+
uniform sampler2D tris;
158+
uniform vec3 bbina, bbinb;
159+
160+
vec3 bboxA, bboxB;
161+
162+
in vec3 origin;
163+
out vec4 color;
164+
165+
uint N = ${pbrtSamples}u, i;
166+
167+
float seed;
168+
float minc(const vec3 x) { return min(x.x,min(x.y,x.z)); }
169+
170+
float random_ofs=0.0;
171+
vec3 cosWeightedRandomHemisphereDirectionHammersley( const vec3 n ) {
172+
float x = float(i)/float(N);
173+
i = (i << 16u) | (i >> 16u);
174+
i = ((i & 0x55555555u) << 1u) | ((i & 0xAAAAAAAAu) >> 1u);
175+
i = ((i & 0x33333333u) << 2u) | ((i & 0xCCCCCCCCu) >> 2u);
176+
i = ((i & 0x0F0F0F0Fu) << 4u) | ((i & 0xF0F0F0F0u) >> 4u);
177+
i = ((i & 0x00FF00FFu) << 8u) | ((i & 0xFF00FF00u) >> 8u);
178+
vec2 r = vec2(x,(float(i) * 2.32830643653086963e-10 * 6.2831) + random_ofs);
179+
vec3 uu=normalize(cross(n, vec3(1.0,1.0,0.0))), vv=cross( uu, n );
180+
float sqrtx = sqrt(r.x);
181+
return normalize(vec3( sqrtx*cos(r.y)*uu + sqrtx*sin(r.y)*vv + sqrt(1.0-r.x)*n ));
182+
}
183+
184+
vec4 trace( inout vec3 realori, const vec3 dir) {
185+
float len=0.0,l,b,mint=1000.0;
186+
vec2 minuv, mintri, cpos;
187+
vec3 scaler=vec3(bbinb/${gridres}.0)/dir,orig=realori,v0,v1,v2;
188+
for (int i=0;i<150;i++){
189+
vec3 txc=(orig-bboxA)*bboxB;
190+
if ( txc != clamp(txc,0.0,1.0)) break;
191+
vec3 tex=textureLod(grid,txc,0.0).rgb;
192+
for(int tri=0; tri<512; tri++) {
193+
if (tex.b<=0.0) break; cpos=tex.rg; tex.rb+=vec2(3.0/4096.0,-1.0);
194+
v1 = textureLodOffset(tris,cpos,0.0,ivec2(1,0)).rgb;
195+
v2 = textureLodOffset(tris,cpos,0.0,ivec2(2,0)).rgb;
196+
vec3 P = cross(dir,v2); float det=dot(v1,P); if (det>-EPSILON) continue;
197+
v0 = textureLod(tris,cpos,0.0).rgb;
198+
vec3 T=realori-v0; float invdet=1.0/det; float u=dot(T,P)*invdet; if (u < 0.0 || u > 1.0) continue;
199+
vec3 Q=cross(T,v1); float v=dot(dir,Q)*invdet; if(v<0.0||u+v>1.0) continue;
200+
float t=dot(v2, Q)*invdet; if (t>EPSILON && t<mint) { mint=t; mintri=cpos; minuv=vec2(u,v); }
201+
}
202+
b=max(0.0,-tex.b-1.0); txc=fract(txc*${gridres}.0);
203+
l=minc(scaler*mix(b+1.0-txc,-b-txc,vec3(lessThan(dir,vec3(0.0)))))+EPSILON*50.0;
204+
len += l;
205+
if (mint <= len) {
206+
realori += dir*(mint);
207+
mintri += vec2(0.0,1.0/4.0);
208+
vec3 n0 = -textureLod(tris,mintri,0.0).rgb;
209+
vec3 n1 = -textureLodOffset(tris,mintri,0.0,ivec2(1,0)).rgb;
210+
vec3 n2 = -textureLodOffset(tris,mintri,0.0,ivec2(2,0)).rgb;
211+
return vec4(normalize(n0*(1.0-minuv.x-minuv.y) + n1*minuv.x + n2*minuv.y),mint);
212+
}
213+
orig += dir*l;
214+
}
215+
return vec4(0.0);
216+
}
217+
218+
void main()
219+
{
220+
bboxA=bbina; bboxB=1.0/bbinb; i=uint(incount);
221+
vec2 fc = vec2(gl_FragCoord.xy), fcu=fc/resolution;
222+
seed = inseed +fcu.x+fcu.y;
223+
vec2 aa = fract(sin(vec2(seed,seed+0.1))*vec2(43758.5453123,22578.1459123));
224+
random_ofs = fract(gl_FragCoord.x * gl_FragCoord.y * inseed + aa.x)*6.2831;
225+
vec4 view = proj * vec4((fc+aa)/(resolution/2.0)-1.0,0.0,1.0);
226+
view = normalize(MVP*vec4(view.xyz/view.w,0.0));
227+
vec3 orig=origin,v1=(bboxA-orig)/view.xyz,v2=v1+(bbinb-vec3(0.2))/view.xyz,far=max(v1,v2),near=min(v1,v2);
228+
float en=max(near.x,max(near.y,near.z)), ex=min(far.x,min(far.y,far.z));
229+
if (ex < 0.0 || en > ex) { color=vec4(1.0); return; }
230+
orig += max(0.0,en)*view.xyz;
231+
vec4 hit=trace(orig,view.xyz);
232+
if (hit.w <= 0.0) { color.rgb = vec3(1.0); return; }
233+
hit=trace(orig, -cosWeightedRandomHemisphereDirectionHammersley(hit.xyz));
234+
if (hit.w <= 0.0) { color.rgb = vec3(0.8); return; }
235+
}`;
236+
237+
// Upload polygon and acceleration data.
238+
var texture = gl.createTexture();
239+
gl.activeTexture(gl.TEXTURE0);
240+
gl.bindTexture(gl.TEXTURE_3D, texture);
241+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
242+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
243+
gl.texImage3D( gl.TEXTURE_3D, 0, gl.RGB32F, gridres, gridres, gridres, 0, gl.RGB, gl.FLOAT, map );
244+
245+
var texture2 = gl.createTexture();
246+
gl.activeTexture(gl.TEXTURE1);
247+
gl.bindTexture(gl.TEXTURE_2D, texture2);
248+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
249+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
250+
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB32F, totX, totY*4, 0, gl.RGB, gl.FLOAT, polyData );
251+
252+
// Create the path tracing program, grab the uniforms.
253+
var program = createProgram(gl, vs, fs);
254+
var mvpLocation = gl.getUniformLocation(program, 'MVP');
255+
var pLocation = gl.getUniformLocation(program, 'proj');
256+
var uniformgridLocation = gl.getUniformLocation(program, 'grid');
257+
var uniformtrisLocation = gl.getUniformLocation(program, 'tris');
258+
var uniformSeed = gl.getUniformLocation(program, 'inseed');
259+
var uniformCount = gl.getUniformLocation(program, 'incount');
260+
var uniformbbaLocation = gl.getUniformLocation(program, 'bbina');
261+
var uniformbbbLocation = gl.getUniformLocation(program, 'bbinb');
262+
var uniformresLocation = gl.getUniformLocation(program, 'resolution');
263+
264+
// Create the accumulation program, grab thos uniforms.
265+
var program2 = createProgram(gl, vs2, fs2);
266+
var uniformAccumLocation = gl.getUniformLocation(program2, 'accum');
267+
var uniformCountLocation = gl.getUniformLocation(program2, 'count');
268+
269+
// Setup the quad that will drive the rendering.
270+
var vertexPosBuffer = gl.createBuffer();
271+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
272+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0]), gl.STATIC_DRAW);
273+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
274+
275+
var vertexArray = gl.createVertexArray();
276+
gl.bindVertexArray(vertexArray);
277+
var vertexPosLocation = 0;
278+
gl.enableVertexAttribArray(vertexPosLocation);
279+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
280+
gl.vertexAttribPointer(vertexPosLocation, 3, gl.FLOAT, false, 0, 0);
281+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
282+
gl.bindVertexArray(null);
283+
284+
// Setup some matrices (stole values from jimmy|rig)
285+
var matrix = new Float32Array([6.123234262925839e-17, 0, 1, 0, -0.8660253882408142, 0.5, 5.302876566937394e-17, 0, -0.5, -0.8660253882408142, 3.0616171314629196e-17, 0, 6.535898208618164, 19.320507049560547, -4.0020835038019837e-16, 1]);
286+
var matrix2 = new Float32Array([1.7777777910232544,0,0,0,0,1,0,0,0,0,0,-0.49950000643730164,0,0,1,0.5005000233650208]);
287+
var viewportMV = new Float32Array(matrix);
288+
var accum_count=1, diff=true, abort=false;
289+
290+
// frame handler.
291+
function frame() {
292+
// Do we need to restart rendering (i.e. viewport change)
293+
if (diff) {
294+
matrix.set(viewportMV);
295+
ofscreen1.bind(); gl.clear(gl.COLOR_BUFFER_BIT); ofscreen1.unbind();
296+
accum_count=1;
297+
abort=undefined;
298+
diff=false;
299+
}
300+
301+
// Render more samples.
302+
if (!abort) {
303+
// Bind the offscreen and render a new sample.
304+
ofscreen1.bind();
305+
gl.useProgram(program);
306+
gl.uniformMatrix4fv(mvpLocation, false, matrix);
307+
gl.uniformMatrix4fv(pLocation, false, matrix2);
308+
gl.uniform1i(uniformgridLocation, 0);
309+
gl.uniform1i(uniformtrisLocation, 1);
310+
gl.uniform3fv(uniformbbaLocation, pbrtGrid.bbox.slice(0,3));
311+
gl.uniform3fv(uniformbbbLocation, pbrtGrid.bbox.slice(3,6));
312+
gl.uniform2fv(uniformresLocation, new Float32Array([canvas.width,canvas.height]));
313+
gl.uniform1f(uniformSeed,Math.random());
314+
gl.uniform1i(uniformCount,(accum_count)%pbrtSamples);
315+
316+
gl.activeTexture(gl.TEXTURE0);
317+
gl.bindTexture(gl.TEXTURE_3D, texture);
318+
gl.activeTexture(gl.TEXTURE1);
319+
gl.bindTexture(gl.TEXTURE_2D, texture2);
320+
321+
gl.enable(gl.BLEND);
322+
gl.blendFunc(gl.ONE,gl.ONE);
323+
gl.bindVertexArray(vertexArray);
324+
gl.drawArrays(gl.TRIANGLES, 0, 6);
325+
gl.bindVertexArray(null);
326+
gl.disable(gl.BLEND);
327+
ofscreen1.unbind();
328+
329+
// Display progress (mixdown from float to ldr)
330+
gl.useProgram(program2);
331+
gl.uniform1i(uniformAccumLocation, 0);
332+
gl.uniform1f(uniformCountLocation, 1.0/accum_count);
333+
gl.activeTexture(gl.TEXTURE0);
334+
gl.bindTexture(gl.TEXTURE_2D, ofscreen1.colorTexture);
335+
336+
gl.bindVertexArray(vertexArray);
337+
gl.drawArrays(gl.TRIANGLES, 0, 6);
338+
gl.bindVertexArray(null);
339+
340+
gl.bindTexture(gl.TEXTURE_2D, null);
341+
342+
// Stop if we're done.
343+
if (++accum_count>pbrtSamples) abort =true;
344+
}
345+
requestAnimationFrame(frame);
346+
}
347+
requestAnimationFrame(frame);
348+
349+
var angle=-Math.PI/2, angle2=Math.PI/3,zoom=0;
350+
canvas.oncontextmenu =function(e) { e.preventDefault(); e.stopPropagation(); }
351+
canvas.onmousemove = function(e) {
352+
if (!e.buttons) return;
353+
if (e.buttons==1) { angle += e.movementX/100; angle2 += e.movementY/100; } else { zoom += e.movementX/10; }
354+
viewportMV = t(rX(rY(i(),angle),angle2),[0,4,-20+zoom]);
355+
diff = true;
356+
}
357+
};

polydata2.bin.png

11.9 MB
Loading

webgl2_pathtrace.jpg

39.3 KB
Loading

0 commit comments

Comments
 (0)