Merge pull request #3 from sunyinqi0508/hw3

Hw3 kinda finished (w/o proper comments)
master
sunyinqi0508 4 years ago committed by GitHub
commit 0d17c09211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

3
.gitignore vendored

@ -1,3 +1,6 @@
.DS_Store .DS_Store
old/ old/
new/ new/
pjsk.mp4
_5.jpg
*.bak*

BIN
22.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

BIN
5.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

@ -1,2 +1,2 @@
# graphics_hw2 # graphics_hw3
[link](https://billsun.dev/graphics/hw2) [link](https://billsun.dev/graphics/hw3)

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

@ -1,4 +1,4 @@
<script src=lib2.js></script> <script src=lib3.js></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-language_tools.js" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-language_tools.js" crossorigin="anonymous"></script>
<style> <style>
@ -22,7 +22,7 @@
<body bgcolor=white text=black link=black alink=blue vlink=blue> <body bgcolor=white text=black link=black alink=blue vlink=blue>
<center> <center>
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.---> <!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
<canvas id='canvas1' style="overflow: auto; width: 600px; height:600px;" width=1200 height=1200></canvas> <canvas id='canvas1' style=" overflow: hidden !important; width: 600px !important; height:600px !important;" width=600 height=600></canvas>
</center> </center>
</body> </body>
@ -47,7 +47,7 @@
<!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW --------> <!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW -------->
<script id='my_title' type='text/html'> <script id='my_title' type='text/html'>
Solar RTX RTX Extreme
</script> </script>
@ -61,40 +61,32 @@ Solar RTX
<ul> <ul>
<li>Ctrl+Alt/Option+T: Toggle Texture.</li> <li>Ctrl+Alt/Option+T: Toggle Texture.</li>
<li>Ctrl+S: Download fragment shader.</li> <li>Ctrl+S: Download fragment shader.</li>
<li>Ctrl+Alt/Option+R: Toggle Recursive Ray Tracing.</li>
<li>Ctrl+Alt/Option+N: Reset ViewPoint.</li> <li>Ctrl+Alt/Option+N: Reset ViewPoint.</li>
<li>Ctrl+Alt/Option+P: Toggle Pause/Resume.</li> <li>Ctrl+Alt/Option+P: Toggle Pause/Resume.</li>
<li style="color:red;">Please unfocus the Editing area (click somewhere else on the page) to use hotkeys.</li> <li style="color:red;">Please unfocus the Editing area (click somewhere else on the page) to use hotkeys.</li>
<li>Double Click on canvas (WITHOUT key modifiers): Toggle Pause/Resume.</li> <li>Double Click on canvas (WITHOUT key modifiers): Toggle Pause/Resume.</li>
<li>MOUSE DRAG, SCROLL/WHEEL ZOOM: Changing Viewing point.</li> <li>DRAG, SCROLL on canvas: Changing Viewing point.</li>
<li>Use Chromium based browser for better performance.</li> <li>Please use Chromium based browser.</li>
<li>Super Sampling(0.25x-4x): increase rendering size for better visual or decrease rendering size for better performance.</li>
<li>Spheres(1 - 4): number of spheres, performance will suffer if adding too many spheres.</li>
</ul> </ul>
<i style="font-size:25px;">How it works:</i> <i style="font-size:25px;">How it works:</i>
<ul> <ul>
<li>First, I started with what I've already done in <a href="https://billsun.dev/graphics/hw1">homework 1</a>. Which already included complete Phong shading with <li>I added recursive ray tracing with fraction support.</li>
Specular light and much more (spherical texture mapping, simple interactions, improved UI/shader editor). <li>Each hit will now spawn 2 rays, but there're serious performance issues, because
the number of rays increases exponentially. I resolved this issue by:
</li> </li>
<li> I then merged the code from hw2 and added texture to each sphere.</li>
<li> I modified the ray tracing algorithm so that when hitting an object, instead of just returning color calculated from
Phong model:<br>
<ul> <ul>
<li>It recursively bounces and/or refract(NOT IMPLEMENTED YET) itself spawning new rays.</li> <li>Pruning: If the weight of this ray is too small, dispose it.</li>
<li>The color of this pixel equals to Ambient + Diffuse + ks*color[Reflected] + kt*color[Refracted]. <li>Smarter Stack frame utilization: Now the stack frame will only store last rays and next rays.
(<a href="https://www.cs.drexel.edu/~david/Classes/Papers/p343-whitted.pdf">Turner Whitted Model</a>)</li> By alternating 2 arrays storing last ray and next ray, I don't need to store other rays.
<li>The tracing will stop when a ray was not hitting any object or was reflected/refracted n_ref times. </li>
<li>The color/intensity of the final lights are computed via specular component from the Phong model.</li>
<li>You may increase n_ref for more iterations, but please proceed with caution, because it may halt the computer.
</li> </li>
</ul> <li>By combining these methods I managed to significantly reduce RT depth and 'stack' size,
<li>I added more interactions, you can now change the viewpoint by While supporting nested object and objects both reflection and refraction rays on the same surface.
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.
</li>
<li>Finally, I used super sampling via doubling the render dimensions of the canvas to reduce aliasing. SEE comments on index.html</li>
<li>Repo on <a href="https://github.com/sunyinqi0508/graphics_hw1">Github</a>.</li>
</li> </li>
</ul> </ul>
<img src="./img.jpg"></img>
</ul>
<p> <p>
</script> </script>
@ -137,8 +129,10 @@ document.body.innerHTML = [''
,'<td valign=top>' ,'<td valign=top>'
,'<div id="ace" style="width:800px;height:2200px;"></div>' ,'<div id="ace" style="width:800px;height:2200px;"></div>'
,'</td><td valign=top>' + document.body.innerHTML ,'</td><td valign=top>' + document.body.innerHTML
,'<input type="number" id="ins" style="margin-left:3px;font-size:24px;width:100px;height:45px" value="4">' ,'<input type="number" id="ins" style="margin-left:3px;font-size:24px;width:100px;height:45px" value="2" max="4">'
,'<button id="bns" style="margin-left:5px;font-size:24px;width:150px;height:45px">Set Spheres</button>' ,'<button id="bns" style="margin-left:5px;font-size:24px;width:180px;height:45px">Set Spheres</button>'
,'<input type="number" id="insamp" style="margin-left:3px;font-size:24px;width:100px;height:45px" value="1" max="4" min = "0.25">'
,'<button id="bnsamp" style="margin-left:5px;font-size:24px;width:200px;height:45px">Super Sampling</button>'
,'<div style=\'font-size:25px\'>' + my_instructions.innerHTML + '</div>' + '</td>' ,'<div style=\'font-size:25px\'>' + my_instructions.innerHTML + '</div>' + '</td>'
,'</tr></table>' ,'</tr></table>'
,'</TR></TABLE>' ,'</TR></TABLE>'
@ -149,12 +143,18 @@ bns.onclick=function(e){
cns = ins.value; cns = ins.value;
fragmentShaderDefs = '\n const int cns = ' + cns + ';'; fragmentShaderDefs = '\n const int cns = ' + cns + ';';
if(typeof canvas1.setShaders === "function") if(typeof canvas1.setShaders === "function")
{
canvas1.setShaders(vs, editor.getSession().getValue()); canvas1.setShaders(vs, editor.getSession().getValue());
setUniform('1i', 'flags', flags);
}
} }
} }
bnsamp.onclick=function(e){
let multiplier = insamp.value;
let w = parseInt(canvas1.style.width)*multiplier;
let h = parseInt(canvas1.style.height)*multiplier;
canvas1.height = h;
canvas1.width = w;
gl.viewport(0, 0, w, h);
gl.clearRect(0, 0, w, h);
}
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE. // SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
ace.require("ace/ext/language_tools"); ace.require("ace/ext/language_tools");
var editor = ace.edit("ace", { var editor = ace.edit("ace", {
@ -174,6 +174,7 @@ editor.setOptions({
editor.setAutoScrollEditorIntoView(true); editor.setAutoScrollEditorIntoView(true);
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE. // REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
delete editor.KeyBinding; delete editor.KeyBinding;
let lastTime = Date.now(); let lastTime = Date.now();
let animating = true; let animating = true;
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false; let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
@ -212,32 +213,15 @@ canvas1.addEventListener('mousemove', function(e){
sx = Math.sin(mousedx); sx = Math.sin(mousedx);
cy = Math.cos(mousedy); cy = Math.cos(mousedy);
sy = Math.sin(mousedy); sy = Math.sin(mousedy);
setUniform('4f', 'rot', cx, sx, cy, sy); setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]);
const mask = 0x4;
flags |= mask;
setUniform('1i', 'flags', flags);
} }
// if(over){
// let x=e.offsetX/300-1;
// let y=e.offsetY/300-1;
// let z=-1-3-mousedz;
// let tx = cx*x+sy*sx*y+sx*cy*z;
// let ty = cy*y-sy*z;
// let tz = -sx*x+cx*sy*y+cx*cy*z;
// let len = Math.sqrt(tx*tx + ty*ty+tz*tz);
// setUniform('3f', 'fDir', tx/len, ty/len, tz/len);
// }
mouselastX = e.offsetX; mouselastX = e.offsetX;
mouselastY = e.offsetY; mouselastY = e.offsetY;
}); });
canvas1.addEventListener('mouseup', function(e){ canvas1.addEventListener('mouseup', function(e){
// if(ctrl && alt && moving){
// }
moving = false; moving = false;
}); });
canvas1.addEventListener('mouseout', function(e){ canvas1.addEventListener('mouseout', function(e){
// if(ctrl && alt && moving){
// }
const mask = 0x8; const mask = 0x8;
flags &= !mask; flags &= !mask;
setUniform('1i', 'flags', flags); setUniform('1i', 'flags', flags);
@ -252,14 +236,8 @@ canvas1.addEventListener('wheel', function(e){
canvas1.scroll(function(e) {e.stopPropagation();}); canvas1.scroll(function(e) {e.stopPropagation();});
rtx.style.cursor="pointer"; rtx.style.cursor="pointer";
let rtswitch = function(){ let rtswitch = function(){
const mask = 0x2; alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.')
let rtstatus = !(flags&mask);
if (rtstatus)
rtx.src='./RTXoff.svg';
else
rtx.src='./RTXon.svg'; rtx.src='./RTXon.svg';
flags = (flags&(!mask)) | (rtstatus?mask:0);
setUniform('1i', 'flags', flags);
} }
rtx.addEventListener('click', rtswitch); rtx.addEventListener('click', rtswitch);
var requestAnimationFrame = window.requestAnimationFrame || var requestAnimationFrame = window.requestAnimationFrame ||
@ -271,7 +249,7 @@ let fpscounter = function(time){
fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps'; fps.innerHTML = Math.round(10000/(time-start))/10 + ' fps';
start = time; start = time;
if(fpson) if(fpson)
requestAnimationFrame(fpscounter); ;//requestAnimationFrame(fpscounter);
else{ else{
start = undefined; start = undefined;
fps.innerHTML = ''; fps.innerHTML = '';
@ -307,7 +285,7 @@ document.addEventListener('keydown',(e)=>{
cy = Math.cos(mousedy); cy = Math.cos(mousedy);
sy = Math.sin(mousedy); sy = Math.sin(mousedy);
rtx.src='./RTXon.svg'; rtx.src='./RTXon.svg';
setUniform('4f', 'rot', cx, sx, cy, sy); setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]);
setUniform('1f', 'dFL', mousedz); setUniform('1f', 'dFL', mousedz);
setUniform('1i', 'flags', flags); setUniform('1i', 'flags', flags);
} }
@ -321,9 +299,6 @@ document.addEventListener('keydown',(e)=>{
} }
else else
fpson = false; fpson = false;
// else if(e.code =='KeyV')
// alert(' '+ mousedx+ ' ' + mousedy + ' '+mousedz)
}); });
document.addEventListener('keyup',(e)=>{ document.addEventListener('keyup',(e)=>{
@ -334,15 +309,24 @@ document.addEventListener('keyup',(e)=>{
if(e.code.startsWith('Shift')) if(e.code.startsWith('Shift'))
shift = false; shift = false;
}); });
// SET THE CURRENT TIME IN SECONDS BEFORE RENDERING EACH FRAME.
let startTime = Date.now(); let startTime = Date.now();
let lastFrameTime = 0; let lastFrameTime = 0;
function animate(gl) { function animate(gl) {
let uTime;
if(animating) if(animating)
setUniform('1f', 'uTime', (Date.now() - startTime) / 1000); {
uTime = (Date.now() - startTime) / 1000;
setUniform('1f', 'uTime', uTime);
}
else else
setUniform('1f', 'uTime', (lastTime - startTime) / 1000); {
uTime = (lastTime - startTime) / 1000;
setUniform('1f', 'uTime', uTime);
}
setUniform('4f', 'Sph[3]', .9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),.25);
setUniform('4f', 'Sph[2]', .22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),.05);
setUniform('4f', 'Sph[0]', 0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime),.15);
setUniform('4f', 'Sph[1]', 0,0.,0,.25);
} }
let start; let start;
requestAnimationFrame(fpscounter); requestAnimationFrame(fpscounter);

@ -15,7 +15,7 @@ let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);' , ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
, '} return .5 * sin(r); }' , '} return .5 * sin(r); }'
].join('\n'); ].join('\n');
let ns = 4, cns = 4; let ns = 5, cns = 3;
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n'; fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
let fragmentShaderDefs = 'const int cns = ' + cns + ';\n'; let fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
@ -152,7 +152,25 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
textures[i] = i; textures[i] = i;
} }
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures); gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
setUniform('4f', 'rot', Math.cos(mousedx), Math.sin(mousedx), Math.cos(mousedy), Math.sin(mousedz)); let cx = Math.cos(mousedx), cy = Math.cos(mousedy), sx = Math.sin(mousedx), sy = Math.sin(mousedy);
setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]);
let attribs = [
.05,.05,.1, .5,.5,1., 1.,.5,.5,20., 0., .0, 1.3,
.1,.05,.05, 1.,.5,.5, 1.,.5,.5,10., .2,0.8,1.3,
.1,.05,.05, .71,.71,.71, .71,.71,.71,10., 0.3,.0,1.5,
.1,.1,.1, .71,.71,.71, .71,.71,.71,10., 0.05,0., 1.
]
var offset = 0;
for(let i = 0; i < ns-1; i++){
setUniform('3fv', 'Ambient['+i+']', attribs.slice(offset, offset += 3));
setUniform('3fv', 'Diffuse['+i+']', attribs.slice(offset, offset += 3));
setUniform('4fv', 'Specular['+i+']', attribs.slice(offset, offset += 4));
setUniform('1fv', 'ks['+i+']', attribs.slice(offset, offset += 1));
setUniform('1fv', 'kr['+i+']', attribs.slice(offset, offset += 1));
setUniform('1fv', 'kf['+i+']', attribs.slice(offset, offset += 1));
}
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip 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. 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); [-1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]), gl.STATIC_DRAW);
@ -179,7 +197,10 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
function animate() { } function animate() { }
function setUniform(type, name, a, b, c, d, e, f) { function setUniform(type, name, a, b, c, d, e, f) {
if(gl)
{
let loc = gl.getUniformLocation(gl.program, name); let loc = gl.getUniformLocation(gl.program, name);
(gl['uniform' + type])(loc, a, b, c, d, e, f); (gl['uniform' + type])(loc, a, b, c, d, e, f);
}
} }

@ -1,252 +1,252 @@
#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;}
#define REFRACTION normalize(eta*W + (eta*c1 - sqrt(1.-eta*eta*(1.-c1*c1)))*N)
vec3 foregroundColor = vec3(.0841, .5329, .9604); vec3 foregroundColor = vec3(.0841, .5329, .9604);
vec3 groundColor = vec3(.2, .3, .5); vec3 groundColor = vec3(.2, .3, .5);
vec4 groundSpecular = vec4(.71, .71, .71, 10.); vec4 groundSpecular = vec4(.71, .71, .71, 10.);
uniform float uTime;// TIME, IN SECONDS uniform float uTime;// TIME, IN SECONDS
uniform int flags; uniform int f_tex, f_rt, f_moved;
//FLAGS 0-TEX, 1-RT, 2-MOVED, 3-FLASH, 4-TEX_ROT, 5-CLOUD
uniform vec4 rot; //ROTATION VALUES USED TO CALCULATE TRANSFORMATION MATRIX
//rot=[cosx, sinx, cosy, siny], x, y BING ROTATED ANGLE
uniform float dFL; //DELTA on FOCAL LENGTH uniform float dFL; //DELTA on FOCAL LENGTH
uniform vec3 fDir;//Flash light direction uniform mat3 transformation, invTr;
varying vec3 vPos;// -1 < vPos.x < +1 uniform vec3 Ambient[ns], Diffuse[ns];
// -1 < vPos.y < +1 uniform vec4 Specular[ns];
// vPos.z == 0 uniform float ks[ns], kr[ns], kf[ns];
uniform vec4 Sph[ns];
uniform sampler2D uSampler[ns];
const float kf_air = 1.000293;
varying vec3 vPos;
float fl=3.;//ORIGINAL FOCAL LENGTH float fl=3.;//ORIGINAL FOCAL LENGTH
const float pi=3.14159265359; const float pi=3.14159265359;
const float _2pi=2.*pi; const float _2pi=2.*pi;
const int n_ref=5; //<<=======***********************MAX NUMBER OF RAY TRACING RECURRSIONS. INCREASE IT IF YOUR GRAPHICS CARD CAN HANDLE.****************************
//const int ns=4; ns is added from .js
vec4 Sph[ns];
uniform sampler2D uSampler[ns];
vec3 Ambient[ns];
vec3 Diffuse[ns];
vec4 Specular[ns];
float ks[ns];
struct Sphere{ //UPDATED SPHERE STRUCTURE THAT SUPPORTS TRANSPARENCY.(UNUSED)
vec4 Pos;
vec3 Ambient;
vec3 Diffuse;
vec4 Specular;
int textureid;
float ks, kt;
};
struct RT{ //STACK FOR RECURSIVE RAY TRACING.
vec3 color;
float ks;
// vec3 colorr;
// float kt;
// vec3 ptr;
// vec3 normal;
} stack[n_ref];
bool getflag(int flag,int bit){ /***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/
float shifted = float(int(float(flag)/ pow(2.,float(bit)))); /*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays.
return fract(shifted/2.)>0.; /**BUT BE CAUTIOUS IF YOU DON'T HAVE A DECENT GRAPHICS CARD (below GTX 950M)**/
const int max_stack = (n_ref+1)/4;
vec3 scolor = vec3(0,0,0); //Actually 2^n_ref
struct Ray{
vec3 V;
vec3 W;
float kf, cumulativeK;
} stack1[max_stack], stack2[max_stack];
bool modulo2(int n){
return n-2*(n/2) == 1;
} }
float clampv(float val,float l,float h){ vec2 getTextCoord(vec3 tex_sph, float R){
return val<l?l:val>h?h:val; float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y));
if(tex_sph.x>0.)
tex_x=pi-tex_x;
tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
tex_x=tex_x+float(uTime);
float quo=float(int(tex_x/_2pi));
tex_x=tex_x/_2pi - quo;
return vec2(tex_x,((R-tex_sph.y)/(2.*R)));
} }
void main(){ void main(){
////////////////////////////////////////////////
//
// HERE, FOR YOUR HOMEWORK, YOU CAN WRITE ANY
// CODE YOU LIKDEFINE A COLOR FOR THIS FRAGMENT.
// LIGHT DIRECTION AND COLOR
//* I USED LDir AS LIGHT POSITION
//* I NORMALIZED IT AFTER GETTING THE
//* DIRECTION BY SUBTRACTING IT FROM THE POINT
vec3 LDir=vec3(.5,.5,.5); vec3 LDir=vec3(.5,.5,.5);
vec3 LCol=vec3(1.,1.,1.); vec3 LCol=vec3(1.,1.,1.);
float currKf = kf_air;
// SPHERE
Sph[3]=vec4(.9*sin(uTime*.4),0.,.9*cos(uTime*.4),.25);
Sph[2]=vec4(.22*sin(uTime*1.2),0.05,.22*cos(uTime*1.2),.02);
Sph[0]=vec4(.45*sin(uTime),0.05*cos(uTime + 1.),.45*cos(uTime),.1);
Sph[1]=vec4(0.,0.,0.,.15);
// SURFACE REFLECTANCE PROPERTIES, can be transferred from .js
Ambient[3]=vec3(.1,.1,.1);// r,g,b
Diffuse[3]=vec3(.71,.71,.71);// r,g,b
Specular[3]=vec4(.71,.71,.71,10.);// r,g,b,power
Ambient[2]=vec3(.1,.05,.05);// r,g,b
Diffuse[2]=vec3(.71,.71,.71);// r,g,b
Specular[2]=vec4(.71,.71,.71,10.);// r,g,b,power
Ambient[1]=vec3(.1,.05,.05);// r,g,b
Diffuse[1]=vec3(1.,.5,.5);// r,g,b
Specular[1]=vec4(1.,.5,.5,10.);// r,g,b,power
Ambient[0]=vec3(.05,.05,.1);// r,g,b
Diffuse[0]=vec3(.5,.5,1.);// r,g,b
Specular[0]=vec4(1.,.5,.5,20.);// r,g,b,power
ks[0] = 0.25;
ks[1] = 0.1;
ks[2] = 0.3;
ks[3] = 0.05;
// INITIALIZE TO A BACKGROUND COLOR
vec3 color=vec3(.2, .3, .5); vec3 color=vec3(.2, .3, .5);
float ca=rot.x, sa = rot.y, cb=rot.z, sb=rot.w;
mat3 transformation, invTr;//Transformation matrix for viewpoint.
transformation[0] = vec3(ca, sb*sa, sa*cb);//because the matrices are all the same,
transformation[1] = vec3(0, cb, -sb);//We don't need to calculate it for every pixel
transformation[2] = vec3(-sa,ca*sb,ca*cb);//So, we get it from the CPU
invTr[0] = vec3(ca, 0, -sa);//it's inverse, to calculate texture mapping.
invTr[1] = vec3(sa*sb, cb, ca*sb);
invTr[2] = vec3(cb*sa, -sb, ca*cb);
vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1); vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1);
// COMPUTE THE RAY ORIGIN AND DIRECTION
vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0; vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0;
vec3 W=normalize(trPos-V); vec3 W=(trPos-V);
// RAY TRACE TO ALL OBJECTS IN THE SCENE bool rtxoff = false, showtexture = true, moved = false;
bool rtxoff = getflag(flags, 1), float currentK = 1.;
showtexture = !getflag(flags,0), int curr_ptr = 0, curr_top = 0, next_top = 0;
moved = getflag(flags,2)//, bool final = false, stackswap = false;
// flash = true;//getflag(flags, 3)
;//get flags.
// bool hit = false;
int cnt_ref = n_ref;
for(int j=0;j<n_ref;j++) for(int j=0;j<n_ref;j++)
{ {
for(int curr = 0; curr < max_stack; ++curr){
if(curr == curr_ptr){
bool skip = false;
if(j > 0){
Ray currR;
if(stackswap)
currR = stack1[curr];
else
currR = stack2[curr];
currKf = currR.kf;
currentK = currR.cumulativeK;
if(currKf <= 0.001 || currentK <= 0.001)
skip = true;
V = currR.V;
W = currR.W;
}
else
W = normalize(W);
if(!skip){
float tMin=10000.; float tMin=10000.;
int iMin = -1; int iMin = -1;
for(int i=0;i<cns;i++){ for(int i=0;i<cns;i++){
// SHIFT COORDINATES, SO THAT SPHERE IS AT (0,0,0)
vec3 Vp=V-Sph[i].xyz; vec3 Vp=V-Sph[i].xyz;
// SOLVE FOR QUADRATIC EQUATION IN t
float B=dot(W,Vp); float B=dot(W,Vp);
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w; float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
float D=B*B-C; float D=B*B-C;
if(D>0.){ if(D>0.){
float t=-B-sqrt(D); float t=-B-sqrt(D);
if(t > 0. && t < tMin){ if(t >= 0.01 && t < tMin){
tMin = t; //This is an optimization, we don't have to do lighting/tex tMin = t; // This is an optimization, we don't have to do lighting/tex
iMin = i; // for objects that are occuluded, which is expensive! iMin = i; // for objects that are occuluded, which is expensive!
} }
else if (t >= -0.01 && t <0.01){
t = -(t + 2.*B);
if(t > 0.01 && t < tMin){
tMin = t;
iMin = i;
}
}
} }
} }
// IF RAY HITS SPHERE
if(iMin >= 0){ if(iMin >= 0){
float t = tMin; float t = tMin;
vec3 S=V+t*W; vec3 S=V+t*W;
for(int i = 0; i < cns; ++ i) for(int i = 0; i < cns; ++ i)
if(i == iMin) //* Because GLSL doesn't support non-const index, if(i == iMin)
{ //* we have to get Sph[iMin], uSampler[iMin], etc. this way {
//*Good old TEXTURE MAPPING from hw1
vec3 tex_sph = (S-Sph[i].xyz);
if(moved)
tex_sph=invTr*tex_sph;//* transform the sphere to original place if view point moved;
//* This is super expensive! plus it's in the inner loop!!
//* We added a flag to disable it when the viewport is not moved!
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.)
tex_x=pi-tex_x;
tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
tex_x=tex_x+float(uTime);
float quo=float(int(tex_x/_2pi));
tex_x=tex_x/_2pi -quo;
vec3 texture_color; vec3 texture_color;
if(showtexture) if(showtexture)
texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz; {
vec3 tex_sph = (S-Sph[i].xyz);
if(moved)
;//tex_sph=invTr*tex_sph; too expensive
texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz;
}
else texture_color = foregroundColor; else texture_color = foregroundColor;
vec3 N=normalize(S-Sph[i].xyz); vec3 N=normalize(S-Sph[i].xyz);
//*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING
vec3 realLDir=normalize(LDir-S); vec3 realLDir=normalize(LDir-S);
color=( float c1 =dot(N, W);
Ambient[i] if(c1<0.){
+Diffuse[i]*max(0.,dot(N,realLDir))*LCol color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
)*texture_color if(rtxoff || final) //if it's the last hit
; {
// + SPECULAR COMPONENT GOES HERE color += Specular[i].xyz*pow(max(0.,
if(rtxoff || j == n_ref - 1) //if it's the last ray dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0., scolor += color * currentK;
dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w); }
//*Pushing current color and ks into stack. else{
//*suppose ks is 0.15 for all spheres, we can c1 = -c1;
//*of course support different ks, kt for different object float eta =currKf/kf[i];
//*but I didn't have time to do that, just a proof of concept, float nextks = currentK * ks[i], nextkr = currentK * kr[i];
//*I defined the new sphere structure that could be used in the future. bool refl = nextks > 0.001, refr = nextkr > 0.001;
stack[j] = RT(color, ks[i]); if(refl || refr)
V = S; //*NEXT RAY SHOOTING FROM THE INTERSECTION POINT for(int k = 0; k < max_stack; ++k)
// if(flash && j == 0){ if(k == next_top){
// V0 = V - V0; if(stackswap){
// hit = true; if(refl)
// } {
W = -normalize(2. * dot(N, W) * N - W);//*W is the next direction of the next ray. stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
currentK -= nextks;
break;// this is only the innerloop, RT is still going! next_top ++;
}
if(refr)
{
if(refl)
stack2[k+1] = Ray(S, REFRACTION, kf[i], nextkr); //refraction
else
stack2[k] = Ray(S, REFRACTION, kf[i], nextkr); //refraction
currentK -= nextkr;
next_top ++;
}
}else{
if(refl)
{ //remember, c1 = -NW now
stack1[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
currentK -= nextks;
next_top ++;
}
if(refr)
{
if(refl)
stack1[k+1] = Ray(S, REFRACTION, kf[i], nextkr); //refraction
else
stack1[k] = Ray(S, REFRACTION, kf[i], nextkr); //refraction
currentK -= nextkr;
next_top ++;
}
}
break;
}
scolor += currentK * color;
}
}
else{
float eta = currKf/kf_air;
N = -N; //inside the sphere, normal is inward!
float c2 = (1.-eta*eta*(1.-c1*c1));
for(int k = 0; k < max_stack; ++k)
if(k == next_top){
if(stackswap)
{
stack2[k+1] = Ray(S, -2. * c1 * N + W, currKf, currentK*ks[i]); //reflection inside
if(c2 >= 0.)
stack2[k] = Ray(S, normalize(eta*W + (eta*c1 - sqrt(c2))*N), kf_air, currentK*kr[i]); //refraction
else //on the edge, the light won't bend anymore and will keep perpendicular to normal
stack2[k] = Ray(S, normalize((W + c1*N)/sqrt(1.-c1*c1)), kf_air, currentK*kr[i]); //refraction
}else{
stack1[k+1] = Ray(S, -2. * c1 * N + W, currKf, currentK*ks[i]); //reflection inside
if(c2 >= 0.)
stack1[k] = Ray(S, normalize(eta*W + (eta*c1 - sqrt(c2))*N), kf_air, currentK*kr[i]); //refraction
else
stack1[k] = Ray(S, normalize((W + c1*N)/sqrt(1.-c1*c1)), kf_air, currentK*kr[i]); //refraction
}
next_top += 2;
break;
}
}
break;
} }
} }
else { 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 FLOOR at [y = -1] THE NORMAL IS (0,1,0)
// Because We assumed that the light always hit sphere first,
// It will have wirld behavior when you rotate the scene upsidedown.
float t = -(.2+V.y)/W.y; float t = -(.2+V.y)/W.y;
float sx = V.x + t* W.x, sz = V.z + t * W.z; float sx = V.x + t* W.x, sz = V.z + t * W.z;
if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.) if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
{ {
vec3 S = vec3(sx, -.2, sz); vec3 S = vec3(sx, -.2, sz);
vec3 realLDir=normalize(LDir - S); vec3 realLDir=normalize(LDir - S);
color=( color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz;
0.5 //ambient for ground if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
+0.5*max(0.,realLDir.y)*LCol //diffusion for ground {
)*groundColor color += groundSpecular.xyz* //specular for ground.
;
// + SPECULAR COMPONENT GOES HERE
if(rtxoff || j == n_ref - 1)
color += sqrt(float(j+1))*groundSpecular.xyz* //specular for ground.
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w); pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
stack[j] = RT(color, 0.15); //ks of ground is 0.1 scolor += currentK * color;
V = S; //Same as above, trace again from S, dir = reflect(W, N). }
// if(flash && j == 0){ else
// V0 = W; {
// hit = true; for(int k = 0; k < max_stack; ++k)
// } if(k == next_top){
W = vec3(W.x, -W.y, W.z); if(stackswap)
stack2[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection
else
stack1[k] = Ray(S, vec3(W.x, -W.y, W.z), kf_air, currentK * 0.15); //reflection
next_top ++;
break;
}
scolor += (currentK*.85)*color;
}
} }
else{ else{
if(j > 0) if(j > 0)
{ scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(1.,1.,1.) + foregroundColor*0.1);
// If the light bounces away! The color of it is calculated by else scolor = foregroundColor*0.6;
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 //If the light hits the void in the first place, it's just black!
cnt_ref = j;//j is always 0 in this case.
break; //The light is shooting into the void, let's stop RT.
} }
} }
// RTX off if(++curr_ptr >= curr_top){
if(rtxoff) curr_top = next_top;
break; curr_ptr = 0;
if(next_top * 2 > max_stack)
final = true;
stackswap = !stackswap;
} }
if(rtxoff)
color = stack[0].color;
else
{
color = vec3(0,0,0);
float currks = 1.;
for(int i = 0; i < n_ref; ++i)
{
if(i >= cnt_ref)//same trick to use bounded non-const on indexes
{
color += currks * stack[i - 1].color; //if there're less than n_ref rays, e.g. ray go to the void.
break; break;
} }
color += currks *(1.-stack[i].ks) * stack[i].color;
currks *= stack[i].ks;
} }
if(n_ref == cnt_ref)
color += currks * stack[n_ref - 1].color;
} }
// APPLY GAMMA CORRECTION AND SET THE PIXEL COLOR. gl_FragColor=vec4(sqrt(scolor),1.);
gl_FragColor=vec4(sqrt(color),1.);
} }
Loading…
Cancel
Save