diff --git a/.gitignore b/.gitignore
index e43b0f9..96f05f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.DS_Store
+old/
+new/
diff --git a/RTXoff.svg b/RTXoff.svg
new file mode 100644
index 0000000..80488f7
--- /dev/null
+++ b/RTXoff.svg
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RTXon.svg b/RTXon.svg
new file mode 100644
index 0000000..d3124d7
--- /dev/null
+++ b/RTXon.svg
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
index 03424f2..6cde78c 100644
--- a/index.html
+++ b/index.html
@@ -21,6 +21,7 @@
+
@@ -56,7 +57,19 @@ Solar RTX
In this homework, I implemented Global illumination w/
Realtime Recursive Ray Tracing!
-Here is how it works:
+Usage:
+
+ Ctrl+Alt/Option+T: Toggle Texture.
+ Ctrl+S: Download fragment shader.
+ Ctrl+Alt/Option+R: Toggle Recursive Ray Tracing.
+ Ctrl+Alt/Option+N: Reset ViewPoint.
+ Ctrl+Alt/Option+P: Toggle Pause/Resume.
+ Please unfocus the Editing area (click somewhere else on the page) to use hotkeys.
+ Click on canvas (WITHOUT key modifiers): Toggle Pause/Resume.
+ SHIFT+Alt/Option+MOUSE DRAG/WHEEL ZOOM: Changing Viewing point.
+
+
+How it works:
First, I started with what I've already done in homework 1. Which already included complete Phong shading with
Specular light and much more (spherical texture mapping, simple interactions, improved UI/shader editor).
@@ -65,10 +78,19 @@ Solar RTX
I modified the ray tracing algorithm so that when hitting an object, instead of returning color calculated from
Phong model:
- I recursively traced the light reflected and refract from the object.
-
+ It recursively bounces and/or refract(NOT IMPLEMENTED YET) itself spawning new rays.
+ The color of this pixel equals to Ambient + Diffuse + ks*color[Reflected] + kt*color[Refracted].
+ (Turner Whitted Model )
+ The tracing will stop when a ray was reflected/refracted n_ref times.
+ The color of the final light is computed via specular component from the Phong model.
+ You may increase n_ref for more iterations, but please proceed with caution, because it may halt the computer.
+
-
+ I added more interactions, you can now change the viewpoint by
+ holding shift and alt key while dragging on canvas to rotate,
+ or holding shift and alt while scrolling on canvas to change focal length.
+ This is implemented by applying a transformation matrix to the viewpoint and projection surface.
+
Finally, I used super sampling via doubling the render dimensions of the canvas to reduce aliasing.
Repo on Github .
@@ -104,6 +126,9 @@ let vs = my_vertex_shader.innerHTML;
client.send();
document.body.innerHTML = [''
,'' + my_title.innerHTML
+ ,' '
+ ,'
'
,''
,'
'
,' '
@@ -111,7 +136,8 @@ document.body.innerHTML = [''
,' '
,''
,'
'
- ,' ' + document.body.innerHTML + '' + my_instructions.innerHTML + '
' + ' '
+ ,'' + document.body.innerHTML
+ ,'' + my_instructions.innerHTML + '
' + ' '
,'
'
,''
].join('');
@@ -137,52 +163,148 @@ editor.setAutoScrollEditorIntoView(true);
delete editor.KeyBinding;
let lastTime = Date.now();
let animating = true;
-let ctrl = false;
-canvas1.addEventListener('click',function(ev){
+let ctrl = false, alt = false, shift = false, fpson = true, moving = false;
+let mousedx = 0, mousedy = 0, mousedz = 0;
+let mouselastX, mouselastY;
+let pause_resume = function(){
if(animating)
- lastTime = Date.now();
+ lastTime = Date.now();
+ else
+ startTime += Date.now() - lastTime;
+ animating = !animating;
+};
+canvas1.addEventListener('click',function(ev){
+ if(!(shift && alt))
+ pause_resume();
+ //moving = false;
+});
+canvas1.addEventListener('mousedown', function(e){
+ if(shift && alt){
+ moving = true
+ mouselastX = mouselastY = undefined;
+ }
else
- startTime += Date.now() - lastTime;
- animating = !animating;
+ moving = false;
});
+canvas1.addEventListener('mousemove', function(e){
+ if(shift && alt && moving){
+ if(!(mouselastX==undefined || mouselastY == undefined)){
+ mousedx += (mouselastX - e.offsetX)/60;
+ mousedy += (mouselastY - e.offsetY)/60;
+ setUniform('4f', 'rot', Math.cos(mousedx), Math.sin(mousedx), Math.cos(mousedy), Math.sin(mousedy));
+ }
+ mouselastX = e.offsetX;
+ mouselastY = e.offsetY;
+ }
+ else
+ moving = false;
+});
+canvas1.addEventListener('mouseup', function(e){
+ // if(ctrl && alt && moving){
+ // }
+ moving = false;
+});
+canvas1.addEventListener('mouseout', function(e){
+ // if(ctrl && alt && moving){
+ // }
+ moving = false;
+});
+canvas1.addEventListener('wheel', function(e){
+ if(shift && alt){
+ mousedz += e.wheelDelta/600;
+ setUniform('1f', 'dFL', mousedz);
+ }
+});
+rtx.style.cursor="pointer";
+let rtswitch = function(){
+ const mask = 0x2;
+ let rtstatus = !(flags&mask);
+ if (rtstatus)
+ rtx.src='./RTXoff.svg';
+ else
+ rtx.src='./RTXon.svg';
+ flags = flags&!mask | (rtstatus?mask:0);
+ setUniform('1i', 'flags', flags);
+}
+rtx.addEventListener('click', rtswitch);
+var requestAnimationFrame = window.requestAnimationFrame ||
+ window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
+let fpscounter = function(time){
+ if (start === undefined)
+ start = time;
+ else
+ fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps';
+ start = time;
+ if(fpson)
+ requestAnimationFrame(fpscounter);
+ else{
+ start = undefined;
+ fps.innerHTML = '';
+ }
+ };
document.addEventListener('keydown',(e)=>{
+ if(e.code.startsWith('Shift'))
+ shift = true;
if(e.code.startsWith('Control'))
- {
ctrl = true;
- }
- else if(ctrl && e.code == 'KeyT')
- {
- mask = 0x1;
+ if(e.code.startsWith('Alt'))
+ alt = true;
+ else if(ctrl && alt && e.code == 'KeyT'){
+ const mask = 0x1;
flags = flags&!mask | (!(flags&mask)?mask:0);
setUniform('1i', 'flags', flags);
}
- else if (ctrl && e.code == 'KeyS')
- {
+ else if (ctrl &&e.code == 'KeyS'){
let a = document.createElement('a');
a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue());
a.download = 'shader.frag';
a.click();
}
+ else if(ctrl && alt&&e.code == 'KeyR')
+ rtswitch();
+ else if(ctrl && alt&&e.code == 'KeyN')
+ {
+ flags = 0;
+ moving = false;
+ mousedx = mousedy = mousedz = 0;
+ setUniform('4f', 'rot', Math.cos(mousedx), Math.sin(mousedx), Math.cos(mousedy), Math.sin(mousedy));
+ setUniform('1f', 'dFL', mousedz);
+ setUniform('1i', 'flags', flags);
+ }
+ else if(ctrl && alt&&e.code == 'KeyP')
+ pause_resume();
+ else if(ctrl && alt&&e.code == 'KeyF')
+ if(!fpson)
+ {
+ fpson = true;
+ requestAnimationFrame(fpscounter);
+ }
+ else
+ fpson = false;
+ // else if(e.code =='KeyV')
+ // alert(' '+ mousedx+ ' ' + mousedy + ' '+mousedz)
+
});
document.addEventListener('keyup',(e)=>{
if(e.code.startsWith('Control'))
- {
ctrl = false;
- }
+ if(e.code.startsWith('Alt'))
+ alt = false;
+ if(e.code.startsWith('Shift'))
+ shift = false;
});
// SET THE CURRENT TIME IN SECONDS BEFORE RENDERING EACH FRAME.
let startTime = Date.now();
-
+let lastFrameTime = 0;
function animate(gl) {
if(animating)
setUniform('1f', 'uTime', (Date.now() - startTime) / 1000);
else
setUniform('1f', 'uTime', (lastTime - startTime) / 1000);
-
}
-
-
+let start;
+requestAnimationFrame(fpscounter);
diff --git a/lib2.js b/lib2.js
index 3247002..196053f 100644
--- a/lib2.js
+++ b/lib2.js
@@ -90,7 +90,7 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
setTimeout(function () {
try {
- canvas.gl = canvas.getContext('webgl2'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD.
+ canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD.
} catch (e) { throw 'Sorry, your browser does not support WebGL.'; }
canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders:
@@ -151,7 +151,7 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
textures[i] = i;
}
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
-
+ setUniform('4f', 'rot', Math.cos(mousedx), Math.sin(mousedx), Math.cos(mousedy), Math.sin(mousedz));
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles.
[-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW);
diff --git a/shader.frag b/shader.frag
index ee12c36..04fa609 100644
--- a/shader.frag
+++ b/shader.frag
@@ -4,23 +4,35 @@ vec3 groundColor = vec3(.2, .3, .5);
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
uniform float uTime;// TIME, IN SECONDS
uniform int flags;
-//FLAGS 0-RT, 1-TEX, 2-
+uniform vec4 rot;
+uniform float dFL;
+//FLAGS 0-TEX, 1-RT, 2-CLOUD, 3-TEX_ROT
varying vec3 vPos;// -1 < vPos.x < +1
// -1 < vPos.y < +1
// vPos.z == 0
float fl=3.;
const float pi=3.14159265359;
-const int n_ref=2;
+const int n_ref=6;
const int ns=2;
vec4 Sph[ns];
uniform sampler2D uSampler[ns];
vec3 Ambient[ns];
vec3 Diffuse[ns];
vec4 Specular[ns];
+struct Sphere{
+ vec4 Pos;
+ vec3 Ambient;
+ vec3 Diffuse;
+ vec4 Specular;
+ int textureid;
+ float ks, kt;
+};
struct RT{
vec3 color;
float ks;
+ // vec3 colorr;
+ // float kt;
// vec3 ptr;
// vec3 normal;
} stack[n_ref];
@@ -62,13 +74,20 @@ void main(){
// INITIALIZE TO A BACKGROUND COLOR
vec3 color=vec3(.2, .3, .5);
-
+ float ca=rot.x, sa = rot.y, cb=rot.z, sb=rot.w;
// COMPUTE THE RAY ORIGIN AND DIRECTION
- float x=vPos.x;
- float y=vPos.y;
-
- vec3 V=vec3(0.,0.,fl);
- vec3 W=normalize(vec3(x,y,-fl));
+ mat3 transformation, invTr;
+ transformation[0] = vec3(ca, sb*sa, sa*cb);
+ transformation[1] = vec3(0, cb, -sb);
+ transformation[2] = vec3(-sa,ca*sb,ca*cb);
+ invTr[0] = vec3(ca, 0, -sa);
+ invTr[1] = vec3(sa*sb, cb, ca*sb);
+ invTr[2] = vec3(cb*sa, -sb, ca*cb);
+
+ vec3 trPos = transformation*vec3(vPos.xy, -2);
+
+ vec3 V=transformation*vec3(0.,0.,fl+dFL);
+ vec3 W=normalize(trPos-V);
// RAY TRACE TO ALL OBJECTS IN THE SCENE
bool rtxoff = getflag(flags, 1);
int cnt_ref = n_ref;
@@ -99,7 +118,7 @@ void main(){
if(i == iMin)
{
//*TEXTURE MAPPING
- vec3 tex_sph=S-Sph[i].xyz;
+ vec3 tex_sph=invTr*(S-Sph[i].xyz);
float R=Sph[i].w;
float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y));
if(tex_sph.x>0.)
@@ -124,7 +143,7 @@ void main(){
;
// + SPECULAR COMPONENT GOES HERE
if(rtxoff || j == n_ref - 1)
- color += float(j) * Specular[i].xyz*pow(max(0.,dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w);
+ color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0.,dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w);
stack[j] = RT(color, 0.15);
V = S;
W = -normalize(2. * dot(N, W) * N - W);
@@ -134,7 +153,7 @@ void main(){
else {
// TO SIMIPIFY THINGS UP, I'LL ASSUME THAT EVERYTHING
// IS INSIDE THE BOUNDING BOX [(-1,-1,-1), (1,1,1)]
- // AND THERE'S A INFINITE FLOOR [y = -1]
+ // AND THERE'S A FLOOR at [y = -1]
float t = -(.2+V.y)/W.y;
float sx = V.x + t* W.x, sz = V.z + t * W.z;
@@ -150,7 +169,7 @@ void main(){
;
// + SPECULAR COMPONENT GOES HERE
if(rtxoff || j == n_ref - 1)
- color += float(j)*groundSpecular.xyz*
+ color += sqrt(float(j+1))*groundSpecular.xyz*
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
stack[j] = RT(color, 0.1);
V = S;
@@ -159,7 +178,7 @@ void main(){
else{
if(j > 0)
{
- stack[j] = RT(vec3(12.,12.,12.)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), 0.);
+ stack[j] = RT(sqrt(float(j+1))*vec3(4.,4.,4)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), 0.);
cnt_ref = j + 1;
}
else