After Width: | Height: | Size: 11 MiB |
After Width: | Height: | Size: 336 KiB |
@ -0,0 +1,360 @@
|
||||
<script src=lib1.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/ext-language_tools.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<canvas id='canvas1' width=600 height=600></canvas>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
<script id='my_vertex_shader' type='x-shader/x-vertex'>
|
||||
attribute vec3 aPos;
|
||||
varying vec3 vPos;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.);
|
||||
vPos = aPos;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
|
||||
<script id='my_fragment_shader' type='x-shader/x-fragment'>
|
||||
|
||||
uniform float uTime; // TIME, IN SECONDS
|
||||
uniform int flags;
|
||||
varying vec3 vPos; // -1 < vPos.x < +1
|
||||
// -1 < vPos.y < +1
|
||||
// vPos.z == 0
|
||||
|
||||
uniform sampler2D uSampler;
|
||||
// YOU MUST DEFINE A main() FUNCTION.
|
||||
|
||||
void main() {
|
||||
|
||||
////////////////////////////////////////////////
|
||||
//
|
||||
// HERE, FOR YOUR HOMEWORK, YOU CAN WRITE ANY
|
||||
// CODE YOU LIKE
|
||||
|
||||
//DEFINE A COLOR FOR THIS FRAGMENT.
|
||||
|
||||
// LIGHT DIRECTION AND COLOR
|
||||
|
||||
vec3 LDir = vec3(1, 1, 1);
|
||||
vec3 LCol = vec3(1.,.98,.9);
|
||||
vec3 VDir = vec3(0., 0., 1.);
|
||||
|
||||
float R = 0.5 + 0.3*sin(uTime/10.);
|
||||
// vPos IS THE 3D LOCATION OF THIS FRAGMENT.
|
||||
|
||||
// SURFACE REFLECTANCE PROPERTIES
|
||||
|
||||
vec3 diffuse = vec3(.7,.7,.6);
|
||||
vec3 ambient = vec3(.1,.1,.09);
|
||||
vec3 specular = vec3(.35,.35,.3);
|
||||
// CREATE A WACKY BACKGROUND PATTERN.
|
||||
|
||||
vec3 color = vec3(.5 + .5 * sin(20. * vPos.x + sin(10. * vPos.y + 5. * uTime)),
|
||||
.5,
|
||||
.5 + .5 * sin(20. * vPos.y + sin(10. * vPos.x + 5. * uTime)));
|
||||
vec3 specularlight; //specular light should disregard object color.
|
||||
|
||||
float x = vPos.x;
|
||||
float y = vPos.y;
|
||||
|
||||
// CREATE BOUNCING ANIMATION.
|
||||
|
||||
y += .5 - 2. * abs(.5 * sin(uTime));
|
||||
|
||||
// FIGURE OUT WHETHER THIS PIXEL IS ON THE SPHERE.
|
||||
|
||||
float rr = x * x + y * y;
|
||||
|
||||
// IF SO, THEN SHADE THE SPHERE.
|
||||
|
||||
if (rr < R*R) {
|
||||
|
||||
// COMPUTE THE z AND NORMAL OF THE SPHERE.
|
||||
|
||||
float z = R * sqrt(1. - rr/(R*R));
|
||||
vec3 N = vec3(x,y,z) / R;
|
||||
|
||||
// APPLY SHADING AND LIGHTING.
|
||||
vec3 realVPos = vec3(vPos.x, vPos.y, z);
|
||||
//*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING
|
||||
LDir = normalize(LDir - realVPos);
|
||||
VDir = normalize(VDir - realVPos);
|
||||
|
||||
vec3 lcolor = ambient;
|
||||
lcolor += diffuse * max(0., dot(N, LDir)) * LCol;
|
||||
//*SPECULAR LIGHTS ARE ADDED
|
||||
specularlight = specular *
|
||||
pow(max(0.,dot(2.*dot(N, LDir) * N - LDir, VDir)),3.) * LCol;
|
||||
// ROTATE THE TEXTURE OVER TIME.
|
||||
|
||||
float angle = 0.4 * uTime;
|
||||
float px = cos(angle) * N.x + sin(angle) * N.z;
|
||||
float pz = -sin(angle) * N.x + cos(angle) * N.z;
|
||||
vec3 P = vec3(px, N.y, pz);
|
||||
|
||||
// APPLY PROCEDURAL NOISE TEXTURE.
|
||||
|
||||
float cloud = min(0.85, max(-0.05, 2. * noise(1.1 * P)));
|
||||
//const float rspeed = 10.;
|
||||
//*CALCULATING THE TEXTURE COORDINATE.
|
||||
const float pi = 3.14159265359;
|
||||
float tex_x = acos(abs(x)/sqrt(R*R-y*y));
|
||||
if(x > 0.)
|
||||
tex_x = pi - tex_x;
|
||||
tex_x = R * tex_x;
|
||||
tex_x *= 1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
|
||||
tex_x = tex_x + float(uTime)*R;
|
||||
float _2pir = 2. * pi * R;
|
||||
float quo = float(int(tex_x/_2pir));
|
||||
tex_x = (tex_x - quo * _2pir) / _2pir;
|
||||
//*TEXTURE MAPPING
|
||||
vec3 texture_color;
|
||||
if(flags /*& 0x1*/ == 0)
|
||||
texture_color = texture2D(uSampler, vec2(tex_x, ((R - y)/(2.*R)))).xyz;
|
||||
else
|
||||
texture_color = vec3(.0841, .5329, .9604);
|
||||
color = lcolor *
|
||||
(cloud + texture_color) + specularlight;
|
||||
|
||||
}
|
||||
//*CALCULATING LIGHTING AND SHADOWS FOR THE BACKGROUND
|
||||
else{
|
||||
vec3 realVPos = vec3(vPos.x, vPos.y, -1.);
|
||||
vec3 N = vec3(0., 0., 1.);
|
||||
vec3 C = vec3(0., vPos.y - y, 0.);
|
||||
// APPLY SHADING AND LIGHTING.
|
||||
|
||||
|
||||
vec3 lcolor = 6.*ambient;
|
||||
//*CALCULATING DISTANCE BETWEEN SPHERE CENTER TO THE RAY
|
||||
//BETWEEN THE POINT TO LIGHT SOURCE
|
||||
vec3 LV = realVPos - LDir;
|
||||
vec3 LC = C - LDir;
|
||||
vec3 VC = C - realVPos;
|
||||
float lLV = dot(LV, LV);
|
||||
float lLC = dot(LC, LC);
|
||||
float lVC = dot(VC, VC);
|
||||
float RR = R*R;
|
||||
float d_VCVL = -dot(VC, LV);
|
||||
float dist = min(lLC, lVC);
|
||||
if (d_VCVL > 0.)
|
||||
dist = min(dist, lVC - d_VCVL*d_VCVL/lLV);
|
||||
|
||||
//*CALCULATE DISTANCE BETWEEN SPHERE CENTER TO THE RAY BETWEEN POINT TO CAMERA
|
||||
vec3 EV = realVPos - VDir;
|
||||
vec3 EC = C - VDir;
|
||||
float lEV = dot(EV, EV);
|
||||
float lEC = dot(EC, EC);
|
||||
float d_VCVE = -dot(VC, EV);
|
||||
float dist2 = min(lEC, lVC);
|
||||
if (d_VCVE > 0.)
|
||||
dist2 = min(dist2, lVC - d_VCVE*d_VCVE/lEV);
|
||||
//*AMBIENT LIGHT WILL DECAY WHEN BACKGROUND POINT IS CLOSER TO THE SPHERE
|
||||
//*FIRST THE DISTANCE IS NORMALIZED, THEN I CURVED IT WITH LOGISTIC FUNCTION
|
||||
float aratio0 = 1./(1.+pow(2.71828,15.*(.2-(sqrt(lVC) - R - .4806)/1.381)));
|
||||
lcolor *= aratio0;
|
||||
diffuse *= pow(aratio0, 0.3);
|
||||
//*TEST IF CAMERA CAN SEE THE POINT
|
||||
if(dist2 < RR)
|
||||
{
|
||||
specular *= 0.;
|
||||
float d = sqrt(dist2);
|
||||
float ratio = pow(2., sqrt(dist2)/R) - 1.;
|
||||
diffuse *= ratio;
|
||||
}
|
||||
//*TEST IF THE LIGHT CAN REACH THE BACKGROUND POINT DIRECTLY
|
||||
if(dist < RR)
|
||||
{
|
||||
specular *= 0.;
|
||||
float ratio = pow(2.7, sqrt(dist)/R) - 1.7;
|
||||
float aratio = pow(2., sqrt(dist)/R) - 1.;
|
||||
|
||||
if(ratio < 0.)
|
||||
ratio = 0.;
|
||||
diffuse *= ratio;
|
||||
lcolor *= aratio;
|
||||
}
|
||||
LDir = normalize(LDir - realVPos);
|
||||
VDir = normalize(VDir - realVPos);
|
||||
|
||||
lcolor += 1.2*diffuse * max(0., dot(N, LDir)) * LCol;
|
||||
specularlight = 0.3*specular *
|
||||
pow(max(0., dot(2.*dot(N, LDir) * N - LDir, VDir)),32.) * LCol;
|
||||
|
||||
color = color * lcolor + specularlight;
|
||||
}
|
||||
// APPLY GAMMA CORRECTION AND SET THE PIXEL COLOR.
|
||||
|
||||
gl_FragColor = vec4(sqrt(color), 1.0);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW -------->
|
||||
|
||||
<script id='my_title' type='text/html'>
|
||||
The Bouncing Earth
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- HERE IS WHERE YOU CAN PROVIDE A LONGER TEXT DESCRIPTION -------->
|
||||
|
||||
<script id='my_instructions' type='text/html'><font color=#909090>
|
||||
<p style="font-size:30px; ">This is a description
|
||||
of my cool homework
|
||||
that you are seeing now.</p>
|
||||
<p>
|
||||
<i style="font-size:25px;">Here is how it works:</i>
|
||||
<ul>
|
||||
<li>First, I added <a href="http://planetpixelemporium.com/download/download.php?earthmap1k.jpg">texture</a> to the sphere. The code to load the texture is from
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL">here</a>. Please wait a sec for the texture to download.</li>
|
||||
<li>Then, I mapped the 3D sphere surface to the rectangular 2D texture picture. </li>
|
||||
<li>I also make it look like it's rotating by adding uTime to the offset of the texture and reset the offset to 0 whenever it exceeds
|
||||
the width of the texture.</li>
|
||||
<li>I used Perlin Noise to generate fake clouds.</li>
|
||||
<li> I modified the lighting so that the light source won't move with the sphere and
|
||||
the lighting will change when the sphere moves. I also added specular lights to make it shinier.</li>
|
||||
<li> I tried to add some 'soft shadow' to it. I used a mix of methods inspired by Ambient Occlusion and Ray Tracing.<br>
|
||||
<ul>
|
||||
<li>The Ambient lights and diffusion lights are reduced with respect to the distance between the background point and the sphere.</li>
|
||||
<li>The specular light of the background wall is eliminated and the diffusion factor is reduced when the ray shooting from the background point
|
||||
towards the light source or from the background point towards the camera position intersect with the sphere.</li>
|
||||
</ul>
|
||||
<li>I added basic interactions such as press ctrl + 't' key to hide/show texture, click on the above canvas to pause/unpause animations.
|
||||
Just a proof of concept.</li>
|
||||
<li>Finally, I made some small changes like changing R over time and refined the UI a little bit. I used
|
||||
<a href="https://ace.c9.io">Ace</a> for code highlighting and autocompletion on the edit panel.</li>
|
||||
<li>Comments begin with '//*' are added by me.</li>
|
||||
<li>Repo on <a href="https://github.com/sunyinqi0508/graphics_hw1">Github</a>.</li>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY DON'T WANT TO CHANGE ANYTHING BELOW FOR NOW -------->
|
||||
|
||||
<script>
|
||||
|
||||
// CREATE THE HTML DOCUMENT
|
||||
let flags = 0x0;
|
||||
let vs = my_vertex_shader.innerHTML,
|
||||
fs = my_fragment_shader.innerHTML;
|
||||
fs = fs.substring(1, fs.length);
|
||||
|
||||
document.body.innerHTML = [''
|
||||
,'<font size=7 color=#909090>' + my_title.innerHTML
|
||||
,'<TABLE cellspacing=0 cellpadding=0><TR>'
|
||||
,'<td><font color=red size=5><div id=errorMessage></div></font></td>'
|
||||
,'</TR><TR>'
|
||||
,'<table cellspacing=0>'
|
||||
,'<tr>'
|
||||
,'<td valign=top>'
|
||||
,'<div id="ace" style="width:800px;height:1780px;"></div>'
|
||||
,'</td><td valign=top>' + document.body.innerHTML + '<div style=\'font-size:25px\'>' + my_instructions.innerHTML + '</div>' + '</td>'
|
||||
,'</tr></table>'
|
||||
,'</TR></TABLE>'
|
||||
].join('');
|
||||
|
||||
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
|
||||
|
||||
let text = fs.split('\n'), cols = 0;
|
||||
for (let i = 0 ; i < text.length ; i++)
|
||||
cols = Math.max(cols, text[i].length);
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
editor.getSession().setValue(fs);
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
editor.session.on('change', function(delta) {
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
|
||||
});
|
||||
let lastTime = Date.now();
|
||||
let animating = true;
|
||||
let ctrl = false;
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
});
|
||||
document.addEventListener('keydown',(e)=>{
|
||||
|
||||
if(e.code.startsWith('Control'))
|
||||
{
|
||||
ctrl = true;
|
||||
}
|
||||
else if(ctrl && e.code == 'KeyT')
|
||||
{
|
||||
mask = 0x1;
|
||||
flags = flags&!mask | (!(flags&mask)*mask);
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keyup',(e)=>{
|
||||
if(e.code.startsWith('Control'))
|
||||
{
|
||||
ctrl = false;
|
||||
}
|
||||
});
|
||||
// SET THE CURRENT TIME IN SECONDS BEFORE RENDERING EACH FRAME.
|
||||
|
||||
let startTime = Date.now();
|
||||
|
||||
function animate(gl) {
|
||||
if(animating)
|
||||
setUniform('1f', 'uTime', (Date.now() - startTime) / 1000);
|
||||
else
|
||||
setUniform('1f', 'uTime', (lastTime - startTime) / 1000);
|
||||
|
||||
}
|
||||
|
||||
// START EVERYTHING.
|
||||
|
||||
gl_start(canvas1, vs, fs);
|
||||
</script>
|
||||
|
@ -0,0 +1,175 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
|
||||
let nfsh = fragmentShaderHeader.split('\n').length; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER?
|
||||
let errorMsg = '';
|
||||
//
|
||||
// Initialize a texture and load an image.
|
||||
// When the image finished loading copy it into the texture.
|
||||
//
|
||||
function loadTexture(gl, url) {
|
||||
const texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
|
||||
// Because images have to be downloaded over the internet
|
||||
// they might take a moment until they are ready.
|
||||
// Until then put a single pixel in the texture so we can
|
||||
// use it immediately. When the image has finished downloading
|
||||
// we'll update the texture with the contents of the image.
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
// WebGL1 has different requirements for power of 2 images
|
||||
// vs non power of 2 images so check if the image is a
|
||||
// power of 2 in both dimensions.
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
// Yes, it's a power of 2. Generate mips.
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
} else {
|
||||
// No, it's not a power of 2. Turn off mips and set
|
||||
// wrapping to clamp to edge
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
let gl = this.gl, program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = '<br>';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
|
||||
let texture = loadTexture(gl, './earthmap1k.jpg') //Texture loading.
|
||||
// Tell WebGL we want to affect texture unit 0
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
// Bind the texture to texture unit 0
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
// Tell the shader we bound the texture to texture unit 0
|
||||
gl.uniform1i(gl.getUniformLocation(program, 'uSampler'), 0);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
let gl;
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch index.html",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"file": "${workspaceFolder}/index.html",
|
||||
"runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
||||
"runtimeArgs": ["--args", "--allow-file-access-from-files"]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"file": "${workspaceFolder}/index.html",
|
||||
"runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe",
|
||||
"runtimeArgs": ["--args", "--allow-file-access-from-files"]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"svg.preview.background": "black"
|
||||
}
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,301 @@
|
||||
let lerp = (a,b,t) => a + t * (b - a); // LINEAR INTERPOLATION
|
||||
|
||||
let N = 0; // GREATER N INCREASES "NERVOUSNESS" OF BIG OBJECT
|
||||
|
||||
// TIMING TABLE FOR THE ANIMATION SEQUENCE
|
||||
|
||||
let timing = [
|
||||
[0, lerp(1,.5,N)],
|
||||
[1.8, lerp(2.3,2.1,N)],
|
||||
[lerp(2,2.5,N), 2.8],
|
||||
[2.0, 3.2],
|
||||
[3.4, 3.9],
|
||||
[4.3, 5.0],
|
||||
];
|
||||
|
||||
// EASE CURVE TO ACCELERATE FROM REST AND THEN DECELERATE TO REST
|
||||
|
||||
let sCurve = t => (3 - 2 * t) * t * t;
|
||||
let jCurve = t => t*t*t*t;
|
||||
let jCurve3 = t => t*t*t;
|
||||
// EVALUATE THE TIMING OF ONE ANIMATION PARAMETER FOR THIS FRAME
|
||||
|
||||
let evalTiming = n => {
|
||||
let t0 = timing[n][0];
|
||||
let t1 = timing[n][1];
|
||||
|
||||
if (animationTime < t0)
|
||||
return 0;
|
||||
if (animationTime > t1)
|
||||
return 1;
|
||||
return sCurve((animationTime - t0) / (t1 - t0));
|
||||
}
|
||||
|
||||
|
||||
let bounce = t => Math.sin(Math.PI * t);
|
||||
let wiggle = t => Math.sin(6 * Math.PI * t);
|
||||
|
||||
let trees = []
|
||||
|
||||
function add_tree(pos) {
|
||||
trees.append(new Float32Array(pos))
|
||||
createMesh(32,32,uvToCone,-1, 20);
|
||||
}
|
||||
|
||||
class State {
|
||||
constructor(idx = 0, step = 1) {
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
this.rh = this.lh = .5 * pi;
|
||||
this.lf = this.rf = 0;
|
||||
this.running = 0;
|
||||
this.direction_l = [0, 0];
|
||||
this.dir_sdl = 0;
|
||||
this.delta_l = [0, 0];
|
||||
this.wiggle_t = 0;
|
||||
this.punch_t = 1;
|
||||
this.idx = idx;
|
||||
if(rebuild[idx] === undefined)
|
||||
rebuild.push(true);
|
||||
states[this.idx] = this;
|
||||
this.fig_rot_t = 0;
|
||||
this.figure_rot = pi;
|
||||
this.old_figure_rot = pi;
|
||||
this.curr_figure_rot = pi;
|
||||
this.target_figure_rot = pi;
|
||||
this.delta_height = 0;
|
||||
this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]);
|
||||
this.stepSize = step;
|
||||
this.turnStep = .1;
|
||||
this.stepLength = this.stepSize*(1.8522);
|
||||
this.life = 3;
|
||||
this.damage = .05;
|
||||
this.dead = false;
|
||||
this.death_t = 0;
|
||||
this.hitID = 19;
|
||||
}
|
||||
reset(){
|
||||
this.running = 0;
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
this.delta_height = 0;
|
||||
this.dir_sdl = 0;
|
||||
this.rh = this.lh = .5 * pi;
|
||||
this.lf = this.rf = 0;
|
||||
this.dead = false;
|
||||
this.wiggle_t = 0;
|
||||
this.death_t = 0;
|
||||
}
|
||||
initialize() {
|
||||
this.figure_rot = pi;
|
||||
this.old_figure_rot = pi;
|
||||
this.curr_figure_rot = pi;
|
||||
this.target_figure_rot = pi;
|
||||
this.reset();
|
||||
this.wiggle_t = 0;
|
||||
this.punch_t = 1;
|
||||
this.fig_rot_t = 0;
|
||||
this.direction_l = [0, 0];
|
||||
this.delta_l = [0, 0];
|
||||
this.life = 3;
|
||||
this.delta_l = [0,0];
|
||||
this.direction_l = [0, 0];
|
||||
this.dir_sdl = 0;
|
||||
}
|
||||
set_stepSize(step){
|
||||
this.delta = const_multiply(step, [-.7*pi , 0.5 * pi, 0.44 * pi, 0.55 * pi]);
|
||||
this.stepSize = step;
|
||||
this.stepLength = this.stepSize*(1.8522);
|
||||
}
|
||||
hitTest(punchProg){
|
||||
states.forEach((st, i)=>{
|
||||
if(i != this.idx){
|
||||
//1.15=.5*.23*10; .92 = .4*.23*10 .23=figure_scale, 10=overall_scale
|
||||
const armlength = 1.15*cos(punchProg*pi/4)+.92*cos(-.53*pi*punchProg)
|
||||
let dir = normalize(this.direction_l);
|
||||
let punchpos = plus(plus(this.delta_l,
|
||||
const_multiply(.3, [dir[1],-dir[0]])),
|
||||
const_multiply(armlength, dir));
|
||||
if(vec_len(minus(punchpos, st.delta_l)) < .65){
|
||||
st.hit(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
start_punch(){
|
||||
if(this.punch_t >= 1){
|
||||
this.punch_t = 0;
|
||||
rebuild[this.idx] = true;
|
||||
}
|
||||
}
|
||||
punch(){
|
||||
if(this.punch_t < 1)
|
||||
{
|
||||
rebuild[this.idx] = true;
|
||||
this.punch_t+=.05;
|
||||
if(this.punch_t <= .05)
|
||||
return 1.2;
|
||||
else
|
||||
{
|
||||
let punchProg;
|
||||
if(this.punch_t <= .4)
|
||||
punchProg = 1.2*(1-jCurve((this.punch_t- .05)/.35));
|
||||
else
|
||||
punchProg = 1-jCurve(2-2*(this.punch_t+.1)/1.1);
|
||||
if(this.hitTest(punchProg))
|
||||
this.punch_t = 1;
|
||||
return punchProg;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
restoreID(st){
|
||||
objects[st.idx].forEach(
|
||||
(obj, i)=>{
|
||||
changeID(st.orig_objid[i], obj[0]);
|
||||
}
|
||||
)
|
||||
}
|
||||
defaultHit(){
|
||||
if(objects[this.idx][0][0][0] != this.hitID){
|
||||
this.orig_objid = [];
|
||||
objects[this.idx].forEach(
|
||||
(obj, i) => {
|
||||
this.orig_objid[i] = (obj[0])[0];
|
||||
}
|
||||
)
|
||||
objects[this.idx].forEach(
|
||||
(obj, i) => {
|
||||
changeID(this.hitID, obj[0]);
|
||||
}
|
||||
)
|
||||
setTimeout(this.restoreID, 100, this);
|
||||
}
|
||||
if(this.dead&&this.wiggle_t<=0){
|
||||
rebuild[this.idx] = true;
|
||||
this.wiggle_t = 2.5;
|
||||
}
|
||||
else if(this.life > 0)
|
||||
{
|
||||
this.life -= this.damage;
|
||||
}
|
||||
else {
|
||||
this.death();
|
||||
}
|
||||
}
|
||||
defaultDeath(){
|
||||
this.dead = true;
|
||||
this.death_t = 1;
|
||||
rebuild[this.idx] = true;
|
||||
}
|
||||
hit(hitter){this.defaultHit(hitter);}
|
||||
death() {if(!this.dead){this.defaultDeath();}}
|
||||
animated_turn(){
|
||||
if(this.target_figure_rot != this.figure_rot)
|
||||
{
|
||||
this.fig_rot_t = 1;
|
||||
this.figure_rot %= 2*pi;
|
||||
if(this.figure_rot < 0) this.figure_rot += 2*pi;
|
||||
this.curr_figure_rot %= 2*pi;
|
||||
if(this.curr_figure_rot < 0) this.curr_figure_rot += 2*pi;
|
||||
if((this.curr_figure_rot - this.figure_rot) > pi)
|
||||
this.curr_figure_rot -= 2*pi;
|
||||
else if (this.curr_figure_rot - this.figure_rot < -pi)
|
||||
this.figure_rot -= 2*pi;
|
||||
this.old_figure_rot = this.curr_figure_rot;
|
||||
this.target_figure_rot = this.figure_rot;
|
||||
}
|
||||
if(this.fig_rot_t > 0)
|
||||
{
|
||||
this.fig_rot_t-=this.turnStep;
|
||||
this.curr_figure_rot = lerp(
|
||||
this.old_figure_rot, this.figure_rot, sCurve(1-this.fig_rot_t));
|
||||
}
|
||||
}
|
||||
turn(pt, animated = true){
|
||||
this.direction_l = [pt[0] - this.delta_l[0]/10, pt[1] - this.delta_l[1]/10];
|
||||
if(this.direction_l[1] == 0)
|
||||
this.direction_l[1] = 0.0000000000000001;
|
||||
this.figure_rot = atan(this.direction_l[0]/this.direction_l[1]);
|
||||
if(this.direction_l[1] < 0)
|
||||
this.figure_rot = pi + this.figure_rot;
|
||||
if(!animated)
|
||||
{
|
||||
this.curr_figure_rot = this.target_figure_rot = this.figure_rot;
|
||||
this.fig_rot_t = 0;
|
||||
}
|
||||
}
|
||||
walk(){
|
||||
this.dir_sdl = vec_len(this.direction_l)*10;
|
||||
this.direction_l = normalize(this.direction_l);
|
||||
rebuild[this.idx] = true;
|
||||
this.running = 1;
|
||||
}
|
||||
next() {
|
||||
//return this.presentation();
|
||||
if (this.running <= 0)
|
||||
{
|
||||
this.reset();
|
||||
return {
|
||||
rh: .5 * pi,
|
||||
lh: .5 * pi,
|
||||
rf: 0,
|
||||
lf: 0,
|
||||
dh: 0,
|
||||
dl: 0
|
||||
}
|
||||
}
|
||||
this.running--;
|
||||
const steps = 28;
|
||||
let dl = 0;
|
||||
if (this.progress >= steps / 2) {
|
||||
this.progress = 0;
|
||||
this.leg = !this.leg;
|
||||
}
|
||||
let delta = deepcopy(this.delta);
|
||||
for (let i = 0; i < 4; ++i) delta[i] /= steps;
|
||||
if (this.leg) {
|
||||
if (this.progress < steps / 4) {
|
||||
this.lh += delta[0];
|
||||
this.rh += delta[3];
|
||||
this.lf += delta[1];
|
||||
this.rf += delta[2];
|
||||
} else {
|
||||
this.lh -= delta[0];
|
||||
this.rh -= delta[3];
|
||||
this.lf -= delta[1];
|
||||
this.rf -= delta[2];
|
||||
}
|
||||
} else {
|
||||
if (this.progress < steps / 4) {
|
||||
this.lh += delta[3];
|
||||
this.rh += delta[0];
|
||||
this.lf += delta[2];
|
||||
this.rf += delta[1];
|
||||
} else {
|
||||
this.lh -= delta[3];
|
||||
this.rh -= delta[0];
|
||||
this.lf -= delta[2];
|
||||
this.rf -= delta[1];
|
||||
}
|
||||
}
|
||||
let delta_h = Math.max((1 - cos(abs(this.lh - pi / 2))) * .5 + (1 - cos(abs(this.lf))) * .6, (1 - cos(abs(this.rh - pi / 2))) * .5 + (1 - cos(abs(this.rf))) * .6);
|
||||
this.progress++;
|
||||
return {
|
||||
lh: this.lh,
|
||||
lf: this.lf,
|
||||
rh: this.rh,
|
||||
rf: this.rf,
|
||||
dh: delta_h,
|
||||
dl: this.stepLength / steps
|
||||
};
|
||||
}
|
||||
presentation(){
|
||||
return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0, dl: 0};
|
||||
}
|
||||
};
|
||||
|
||||
function update_tree(idx){
|
||||
//scale, add leaves, add branch
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
|
||||
//------ CREATING MESH SHAPES
|
||||
|
||||
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||||
|
||||
let createMesh = (nu, nv, f, data) => {
|
||||
let tmp = [];
|
||||
for (let v = 0 ; v < 1 ; v += 1/nv) {
|
||||
for (let u = 0 ; u <= 1 ; u += 1/nu) {
|
||||
tmp = tmp.concat(f(u,v,data));
|
||||
tmp = tmp.concat(f(u,v+1/nv,data));
|
||||
}
|
||||
tmp = tmp.concat(f(1,v,data));
|
||||
tmp = tmp.concat(f(0,v+1/nv,data));
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
|
||||
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||||
|
||||
let glueMeshes = (a, b) => {
|
||||
let c = [];
|
||||
for (let i = 0 ; i < a.length ; i++)
|
||||
c.push(a[i]); // a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(b[i]); // + first vertex of b
|
||||
for (let i = 0 ; i < b.length ; i++)
|
||||
c.push(b[i]); // + b
|
||||
return new Float32Array(c);
|
||||
}
|
||||
|
||||
let createSquareMesh = (i, z) => {
|
||||
let m = [], n = 6, j = z < 0 ? (i + 2) % 3 : (i + 1) % 3,
|
||||
k = z < 0 ? (i + 1) % 3 : (i + 2) % 3;
|
||||
|
||||
m[i] = m[1*n+i] = m[2*n+i] = m[3*n+i] = z;
|
||||
m[j] = m[2*n+j] = m[2*n+k] = m[3*n+k] = -1;
|
||||
m[k] = m[1*n+j] = m[1*n+k] = m[3*n+j] = 1;
|
||||
|
||||
m[3+i] = m[1*n+3+i] = m[2*n+3+i] = m[3*n+3+i] = z < 0 ? -1 : 1;
|
||||
m[3+j] = m[1*n+3+j] = m[2*n+3+j] = m[3*n+3+j] = 0;
|
||||
m[3+k] = m[1*n+3+k] = m[2*n+3+k] = m[3*n+3+k] = 0;
|
||||
|
||||
return new Float32Array(m);
|
||||
}
|
||||
|
||||
let squareMesh = createSquareMesh(2, 0);
|
||||
|
||||
let cubeMesh = glueMeshes(
|
||||
glueMeshes(glueMeshes(createSquareMesh(0,-1),createSquareMesh(0,1)),
|
||||
glueMeshes(createSquareMesh(1,-1),createSquareMesh(1,1))),
|
||||
glueMeshes(createSquareMesh(2,-1),createSquareMesh(2,1)) );
|
||||
|
||||
let uvToTorus = (u,v,r) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = 2 * Math.PI * v;
|
||||
|
||||
let x = Math.cos(theta) * (1 + r * Math.cos(phi));
|
||||
let y = Math.sin(theta) * (1 + r * Math.cos(phi));
|
||||
let z = r * Math.sin(phi);
|
||||
|
||||
let nx = Math.cos(theta) * Math.cos(phi);
|
||||
let ny = Math.sin(theta) * Math.cos(phi);
|
||||
let nz = Math.sin(phi);
|
||||
|
||||
return [x,y,z, nx,ny,nz];
|
||||
}
|
||||
|
||||
let uvToSphere = (u,v) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = Math.PI * (v - .5);
|
||||
let x = Math.cos(theta) * Math.cos(phi);
|
||||
let y = Math.sin(theta) * Math.cos(phi);
|
||||
let z = Math.sin(phi);
|
||||
return [x,y,z, x,y,z];
|
||||
}
|
||||
|
||||
let uvToTube = (u,v) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta);
|
||||
let y = Math.sin(theta);
|
||||
let z = 2 * v - 1;
|
||||
return [x,y,z, x,y,0];
|
||||
}
|
||||
|
||||
let uvToDisk = (u,v,dz) => {
|
||||
if (dz === undefined)
|
||||
dz = 0;
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta) * v;
|
||||
let y = Math.sin(theta) * v;
|
||||
let z = dz;
|
||||
return [x,y,z, 0,0,dz ? Math.sign(dz) : 1];
|
||||
}
|
||||
|
||||
let torusMesh = createMesh(32, 16, uvToTorus, .5);
|
||||
let sphereMesh = createMesh(32, 16, uvToSphere);
|
||||
let tubeMesh = createMesh(32, 2, uvToTube);
|
||||
let diskMesh = createMesh(32, 2, uvToDisk);
|
||||
let diskNMesh = createMesh(32, 2, uvToDisk, -1);
|
||||
let diskPMesh = createMesh(32, 2, uvToDisk, 1);
|
||||
let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh);
|
||||
|
@ -0,0 +1,201 @@
|
||||
|
||||
function implicitSurfaceTriangleMesh(implicitFunction, n, args, id = 8) {
|
||||
|
||||
// HERE IS WHERE MOST OF THE WORK HAPPENS
|
||||
|
||||
let marchingTetrahedra = function(V, ni, nj) {
|
||||
|
||||
// CONVENIENCE FUNCTIONS TO COMPUTE (i,j,k) FROM VOLUME INDEX n
|
||||
|
||||
function n2i(n) { return n % ni; }
|
||||
function n2j(n) { return (n / dj >>> 0) % nj; }
|
||||
function n2k(n) { return n / dk >>> 0 ; }
|
||||
|
||||
// ADD A VERTEX, AND RETURN A UNIQUE ID FOR THAT VERTEX
|
||||
|
||||
function E(a, b) {
|
||||
if (a > b) { let tmp = a; a = b; b = tmp; }
|
||||
let ai = n2i(a), aj = n2j(a), ak = n2k(a),
|
||||
bi = n2i(b), bj = n2j(b), bk = n2k(b);
|
||||
let m = (n << 6) + (ai & bi ? 1 << 6 : ai | bi << 3)
|
||||
+ (aj & bj ? dj << 6 : aj << 1 | bj << 4)
|
||||
+ (ak & bk ? dk << 6 : ak << 2 | bk << 5);
|
||||
|
||||
// ADD TO VERTEX ARRAY ONLY THE FIRST TIME THE VERTEX IS ENCOUNTERED
|
||||
|
||||
if (vertexID[m] === undefined) {
|
||||
vertexID[m] = P.length / 3;
|
||||
let t = -V[n+a] / (V[n+b] - V[n+a]),
|
||||
c = function(i,a,b) { return (i + (1-t)*a + t*b) / ni * 2 - 1; };
|
||||
P.push( c(i,ai,bi), c(j,aj,bj), c(k,ak,bk) );
|
||||
}
|
||||
|
||||
return vertexID[m];
|
||||
}
|
||||
|
||||
// CASE WHERE WE ADD ONE TRIANGLE IN A TETRAHEDRON
|
||||
|
||||
function tri(a, b, c, d) {
|
||||
T.push(E(a,b), E(a,c), E(a,d));
|
||||
}
|
||||
|
||||
// CASE WHERE WE ADD TWO TRIANGLES IN A TETRAHEDRON
|
||||
|
||||
function quad(a, b, c, d) {
|
||||
let ac = E(a,c), bc = E(b,c), ad = E(a,d), bd = E(b,d);
|
||||
T.push(bc, ac, ad);
|
||||
T.push(ad, bd, bc);
|
||||
}
|
||||
|
||||
// DECLARE VARIABLES
|
||||
|
||||
let nk = V.length / (ni * nj), di = 1, dj = ni, dk = ni * nj;
|
||||
let dij = di + dj, dik = di + dk, djk = dj + dk, dijk = di + dj + dk;
|
||||
let P = [], T = [], vertexID = [], i, j, k, m = 0, n, S = [0,di,dij,dijk];
|
||||
let lo = new Array(nj * nk),
|
||||
hi = new Array(nj * nk);
|
||||
|
||||
// THE SIX POSSIBLE INTERMEDIATE PATHS THROUGH A TETRAHEDRON
|
||||
|
||||
let S1 = [di , dj , dk , di , dj , dk ];
|
||||
let S2 = [dij, djk, dik, dik, dij, djk];
|
||||
|
||||
// THERE ARE 16 CASES TO CONSIDER
|
||||
|
||||
let cases = [ [0 ], [1, 0,1,2,3], [1, 1,2,0,3], [2, 0,1,2,3],
|
||||
[1, 2,3,0,1], [2, 0,2,3,1], [2, 1,2,0,3], [1, 3,1,2,0],
|
||||
[1, 3,0,2,1], [2, 0,3,1,2], [2, 1,3,2,0], [1, 2,1,0,3],
|
||||
[2, 2,3,0,1], [1, 1,3,0,2], [1, 0,3,2,1], [0 ], ];
|
||||
|
||||
// FOR EACH (Y,Z), DON'T DO ANY WORK OUTSIDE OF X RANGE WHERE SURFACE MIGHT BE
|
||||
|
||||
for (k = 0 ; k < nk ; k++)
|
||||
for (j = 0 ; j < nj ; j++, m++) {
|
||||
let n0 = m * ni, n1 = n0 + ni - 1;
|
||||
for (n = n0 ; n <= n1 && V[n] > 0 ; n++) ;
|
||||
lo[m] = Math.max(0, n-1 - n0);
|
||||
for (n = n1 ; n >= n0 && V[n] > 0 ; --n) ;
|
||||
hi[m] = Math.min(ni-1, n+1 - n0);
|
||||
}
|
||||
|
||||
// FOR ALL Y AND Z IN THE VOLUME
|
||||
|
||||
for (k = 0 ; k < nk - 1 ; k++) {
|
||||
let i0, i1, m = k * nj, n1, s0, s1;
|
||||
for (j = 0 ; j < nj - 1 ; j++, m++) {
|
||||
i0 = Math.min(lo[m], lo[m+1], lo[m+ni], lo[m+1+ni]);
|
||||
i1 = Math.max(hi[m], hi[m+1], hi[m+ni], hi[m+1+ni]);
|
||||
|
||||
// GO THROUGH RANGE OF X WHERE THE SURFACE MIGHT BE (IE: WITH ANY POSITIVE VALUES)
|
||||
|
||||
if (i0 <= i1) {
|
||||
n = m * ni + i0;
|
||||
n1 = m * ni + i1;
|
||||
s0 = (V[n]>0) + (V[n+dj]>0) + (V[n+dk]>0) + (V[n+djk]>0);
|
||||
for (i = i0 ; n <= n1 ; i++, n++, s0 = s1) {
|
||||
|
||||
// FOR EACH CUBE
|
||||
|
||||
s1 = (V[n+di]>0) + (V[n+dij]>0) + (V[n+dik]>0) + (V[n+dijk]>0);
|
||||
if (s0 + s1 & 7) {
|
||||
let C14 = (V[n] > 0) | (V[n+dijk] > 0) << 3;
|
||||
|
||||
// CYCLE THROUGH THE SIX TETRAHEDRA THAT TILE THE CUBE
|
||||
|
||||
for (let p = 0 ; p < 6 ; p++) {
|
||||
let C = cases [ C14 | (V[n+S1[p]] > 0) << 1 | (V[n+S2[p]] > 0) << 2 ];
|
||||
|
||||
// FOR EACH TETRAHEDRON, OUTPUT EITHER ZERO, ONE OR TWO TRIANGLES
|
||||
|
||||
if (C[0]) { // C[0] == number of triangles to be created.
|
||||
S[1] = S1[p]; // assign 2nd and 3rd corners of simplex.
|
||||
S[2] = S2[p];
|
||||
(C[0]==1 ? tri : quad)(S[C[1]], S[C[2]], S[C[3]], S[C[4]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MAKE SURE ALL TRIANGLE VERTICES ARE LISTED IN COUNTERCLOCKWISE ORDER
|
||||
|
||||
for (let m = 0 ; m < T.length ; m += 3) {
|
||||
let a = 3 * T[m], b = 3 * T[m+1], c = 3 * T[m+2],
|
||||
n = Math.floor(ni*(P[a ]+1)/2) +
|
||||
Math.floor(ni*(P[a+1]+1)/2) * dj +
|
||||
Math.floor(ni*(P[a+2]+1)/2) * dk,
|
||||
u = cross([P[b] - P[a], P[b+1] - P[a+1], P[b+2] - P[a+2]],
|
||||
[P[c] - P[b], P[c+1] - P[b+1], P[c+2] - P[b+2]]),
|
||||
v = [ V[n+1] - V[n], V[n+dj] - V[n], V[n+dk] - V[n] ];
|
||||
if (dot(u, v) < 0) { let tmp = T[m]; T[m] = T[m + 2]; T[m + 2] = tmp; }
|
||||
}
|
||||
|
||||
// RETURN POINTS AND TRIANGLES
|
||||
|
||||
return [P, T];
|
||||
}
|
||||
|
||||
// SAMPLE THE VOLUME
|
||||
|
||||
let F = i => (i - n/2) / (n/2);
|
||||
let volume = [];
|
||||
|
||||
for (let k = 0 ; k < n ; k++)
|
||||
for (let j = 0 ; j < n ; j++)
|
||||
for (let i = 0 ; i < n ; i++)
|
||||
volume.push(implicitFunction(F(i), F(j), F(k), args));
|
||||
|
||||
// FIND ALL VERTICES AND TRIANGLES IN THE VOLUME
|
||||
|
||||
let VT = marchingTetrahedra(volume, n, n);
|
||||
let V = VT[0];
|
||||
let T = VT[1];
|
||||
|
||||
// COMPUTE SURFACE NORMALS
|
||||
|
||||
let N = new Array(V.length);
|
||||
for (let i = 0 ; i < V.length ; i += 3) {
|
||||
let x = V[i], y = V[i+1], z = V[i+2], e = .001,
|
||||
f0 = implicitFunction(x ,y ,z , args),
|
||||
fx = implicitFunction(x+e,y ,z , args),
|
||||
fy = implicitFunction(x ,y+e,z , args),
|
||||
fz = implicitFunction(x ,y ,z+e, args),
|
||||
normal = normalize([f0-fx,f0-fy,f0-fz]);
|
||||
for (let j = 0 ; j < 3 ; j++)
|
||||
N[i+j] = normal[j];
|
||||
}
|
||||
|
||||
// CONSTRUCT AND RETURN THE TRIANGLES MESH
|
||||
|
||||
let mesh = [];
|
||||
for (let i = 0; i < T.length; i += 3) {
|
||||
let a = 3 * T[i ],
|
||||
b = 3 * T[i + 1],
|
||||
c = 3 * T[i + 2];
|
||||
mesh.push( id, V[a],V[a+1],V[a+2] , N[a],N[a+1],N[a+2] ,
|
||||
id, V[b],V[b+1],V[b+2] , N[b],N[b+1],N[b+2] ,
|
||||
id, V[c],V[c+1],V[c+2] , N[c],N[c+1],N[c+2] );
|
||||
}
|
||||
return new Float32Array(mesh);
|
||||
}
|
||||
|
||||
let blob = (center, radius, x, y, z) => {
|
||||
x -= center[0];
|
||||
y -= center[1];
|
||||
z -= center[2];
|
||||
return Math.max(0, 1 - .16 * (x*x + y*y + z*z) / (radius * radius));
|
||||
}
|
||||
|
||||
var implicitFunction = (x,y,z,args) => {
|
||||
let ret = -.5;
|
||||
let x4 = _x => _x*_x*_x*_x;
|
||||
args.forEach((paras, _)=>{
|
||||
const center = paras[0],
|
||||
radius = paras[1];
|
||||
ret += x4(blob(center, radius, x, y, z));
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
<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=lib10.header.js></script>
|
||||
<script src=animation.js></script>
|
||||
<script src=implicitSurface.js></script>
|
||||
<script src=lib10.js></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
|
||||
<font size=7 color=#909090>
|
||||
|
||||
Pikachu
|
||||
|
||||
<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>
|
||||
<TABLE cellspacing=0 cellpadding=0 style="width:50%"><TR>
|
||||
<td><font color=red size=5><div id=errorMessage></div></font></td>
|
||||
</TR><TR>
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td valign=top>
|
||||
<div id="ace" style="opacity:90%;width:300px;height:700px;"hidden=true></div>
|
||||
<div id = 'usage' style="opacity:90%;width:300px;height:700px;">
|
||||
<font color=#000000>
|
||||
<i style="font-size:28px;">What's new: </i>
|
||||
<ul style="font-size:24px;">
|
||||
|
||||
</ul>
|
||||
<p style="font-size:24px;">
|
||||
</div>
|
||||
</td><td valign=top style="background-color:azure;opacity: 100%;">
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" background-color:#333333;opacity: 100%;overflow: hidden !important; width: 600px !important; height:600px !important;" width=1199 height=1199></canvas>
|
||||
</center>
|
||||
</body>
|
||||
<div id="controls">
|
||||
<input type="number" id="ins" style="display:none;margin-left:0px;font-size:24px;width:35px;height:45px" value="5" max="5" min = "1">
|
||||
<button id="bns" style="display:none;margin-left:0px;font-size:24px;width:105px;height:45px">Spheres</button>
|
||||
<input type="number" id="insamp" style="margin-left:2px;font-size:24px;width:60px;height:45px" value="2" max="10" min = "0.1" step="0.2">
|
||||
<button id="bnsamp" style="margin-left:0px;font-size:24px;width:190px;height:45px">Super Sampling</button>
|
||||
<button id="bnfs" style="margin-left:2px;font-size:24px;width:180px;height:45px">Fullscreen</button>
|
||||
<button id="clrsel" style="display:none;margin-left:0px;font-size:24px;width:180px;height:45px">Clear Selection</button>
|
||||
<button id="reset" style="margin-left:0px;font-size:24px;width:100px;height:45px">Reset</button>
|
||||
<button id="mov" style="margin-left:0px;font-size:24px;width:180px;height:45px">Move Lighting</button>
|
||||
<button id="pause" style="margin-left:0px;font-size:24px;width:100px;height:45px">Pause</button>
|
||||
<div style='font-size:25px;'>
|
||||
|
||||
<!-- <font color=#000000>
|
||||
|
||||
<i style="font-size:28px;">What's new: </i>
|
||||
<p style="font-size:24px;">
|
||||
</p> -->
|
||||
<div id="howitworks">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</TR></TABLE>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script src="lib10.ext.js"></script>
|
||||
|
||||
<script>
|
||||
setInterval(() => {
|
||||
if(window.vs != null && window.fs != null&& canvas1.setShaders === undefined)
|
||||
gl_start(canvas1, vs, fs);
|
||||
}, 200);
|
||||
</script>
|
@ -0,0 +1,809 @@
|
||||
//Header file, contains global variable definitions,
|
||||
// asynchronized shader loading and utility functions
|
||||
|
||||
var mousedx = 0,
|
||||
mousedy = 0,
|
||||
mousedz = 0;
|
||||
let seldx = 0,
|
||||
seldy = 0,
|
||||
seldz = 0;
|
||||
var enableSelection = false;
|
||||
var cx = 1,
|
||||
cy = 1,
|
||||
sx = 0,
|
||||
sy = 0,
|
||||
shaders = [];
|
||||
var mouselastX, mouselastY
|
||||
;
|
||||
var fl = 3;
|
||||
let start;
|
||||
var vs, fs;
|
||||
var bezierMat = [-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0],
|
||||
hermiteMat = [2, -3, 0, 1, -2, 3, 0, 0, 1, -2, 1, 0, 1, -1, 0, 0],
|
||||
catmullRomMat = [-.5, 1, -.5, 0, 1.5, -2.5, 0, 1, -1.5, 2, .5, 0, .5, -.5, 0, 0];
|
||||
var starColors = [0.9921, 0.5378, 0.7109,
|
||||
0.65, 0.56, 0.992,
|
||||
0.992, 0.7994, 0.2402,
|
||||
0.1760, 0.5094, 0.5378,
|
||||
.1164, .1274, .2289,
|
||||
.9784, .71, .4482,
|
||||
],
|
||||
n_shapes = starColors.length / 3;
|
||||
var editor = undefined
|
||||
var cos = Math.cos,
|
||||
sin = Math.sin,
|
||||
tan = Math.tan,
|
||||
acos = Math.acos,
|
||||
asin = Math.asin,
|
||||
atan = Math.atan,
|
||||
sqrt = Math.sqrt,
|
||||
pi = Math.PI,
|
||||
abs = Math.abs,
|
||||
pow = Math.pow,
|
||||
log = Math.log;
|
||||
var positionsupdated = true;
|
||||
var paths = [],
|
||||
origpath = [],
|
||||
path_misc = [];
|
||||
var canvas_controls = [];
|
||||
var states = [];
|
||||
function deepcopy(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
let vsfetch = new XMLHttpRequest();
|
||||
vsfetch.open('GET', './shader.vert');
|
||||
vsfetch.onloadend = function () {
|
||||
vs = vsfetch.responseText;
|
||||
};
|
||||
vsfetch.send();
|
||||
//* LOADING FRAGMENT SHADER
|
||||
let fsfetch = new XMLHttpRequest();
|
||||
fsfetch.open('GET', './shader.frag');
|
||||
fsfetch.onloadend = function () {
|
||||
fs = (fsfetch.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
if (editor != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
};
|
||||
fsfetch.send();
|
||||
let pathFetch = new XMLHttpRequest();
|
||||
pathFetch.open('GET', './paths.txt');
|
||||
pathFetch.onloadend = function () {
|
||||
let text = pathFetch.responseText;
|
||||
let currX = 0,
|
||||
currY = 0,
|
||||
maxX = -10000,
|
||||
maxY = -10000,
|
||||
minX = 10000,
|
||||
minY = 10000;
|
||||
var currShape = [],
|
||||
currCurve = [];
|
||||
let i = 0;
|
||||
let postProcess = () => {
|
||||
if (currShape.length) {
|
||||
let spanX = maxX - minX;
|
||||
let spanY = maxY - minY;
|
||||
let span = Math.max(spanX, spanY);
|
||||
let l_total = 0;
|
||||
for (var k = 0; k < currShape.length; ++k) {
|
||||
let funcs = [];
|
||||
const curve = currShape[k];
|
||||
for (let j = 0; j < curve.length; j += 2) {
|
||||
curve[j] = (curve[j] - minX) / span - spanX / (span * 2);
|
||||
curve[j + 1] = (curve[j + 1] - minY) / span - spanY / (span * 2);
|
||||
origpath.push(1, curve[j], curve[j + 1], 0, 0, 0, 1);
|
||||
if (j % 6 == 0 && j > 5) {
|
||||
let X = [],
|
||||
Y = [];
|
||||
for (let k = j - 6; k <= j + 1; k += 2) {
|
||||
X.push(curve[k]);
|
||||
Y.push(curve[k + 1]);
|
||||
}
|
||||
let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) +
|
||||
vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) +
|
||||
vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) +
|
||||
vec_len(minus([X[1], Y[1]], [X[0], Y[0]]))) / 2.;
|
||||
l_total += l;
|
||||
funcs.push([matrix_multiply(bezierMat, X),
|
||||
matrix_multiply(bezierMat, Y), l
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
paths.push(funcs);
|
||||
path_misc.push([l_total, spanX / (2 * span), spanY / (2 * span)]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
let read_num = () => {
|
||||
let num = 0,
|
||||
sign = 1,
|
||||
accepted = 0;
|
||||
|
||||
while (i < text.length && (text[i] < '0' || text[i] > '9') && text[i] != '-') ++i;
|
||||
if (text[i] == '-') {
|
||||
sign = -1;
|
||||
++i;
|
||||
}
|
||||
while (i < text.length && text[i] >= '0' && text[i] <= '9') {
|
||||
let n = text[i++] - '0';
|
||||
accepted *= 10;
|
||||
accepted += n;
|
||||
}
|
||||
|
||||
num += accepted;
|
||||
if (text[i] == '.') {
|
||||
i++;
|
||||
let multiplier = 0.1;
|
||||
accepted = 0;
|
||||
while (i < text.length && text[i] >= '0' && text[i] <= '9') {
|
||||
let n = text[i++] - '0';
|
||||
accepted += n * multiplier;
|
||||
multiplier /= 10;
|
||||
}
|
||||
num += accepted;
|
||||
}
|
||||
return num * sign;
|
||||
}
|
||||
let cRevs = [],
|
||||
c_idx = 0,
|
||||
prevX = 0,
|
||||
prevY = 0,
|
||||
getC = () => {
|
||||
return cRevs[c_idx--];
|
||||
}
|
||||
let get_next = (delta = false) => {
|
||||
if (delta) {
|
||||
currX = prevX + read_num();
|
||||
currY = prevY + read_num();
|
||||
} else {
|
||||
currX = read_num();
|
||||
currY = read_num();
|
||||
}
|
||||
maxX = currX > maxX ? currX : maxX;
|
||||
maxY = currY > maxY ? currY : maxY;
|
||||
minX = currX < minX ? currX : minX;
|
||||
minY = currY < minY ? currY : minY;
|
||||
|
||||
currCurve.push(currX);
|
||||
currCurve.push(currY);
|
||||
}
|
||||
while (i < text.length) {
|
||||
if (text[i] == 'z') {
|
||||
currCurve.length && currShape.push(currCurve);
|
||||
currCurve = [];
|
||||
++i
|
||||
} else if (text[i] == 'N') {
|
||||
postProcess();
|
||||
currShape = [];
|
||||
maxX = -1000, maxY = -1000, minX = 1000, minY = 1000;
|
||||
++i;
|
||||
} else if (text[i] == 'c') {
|
||||
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
|
||||
for (let j = 0; j < 3; ++j) {
|
||||
get_next(true);
|
||||
}
|
||||
} else if (text[i] == 'C') {
|
||||
for (let j = 0; j < 3; ++j) {
|
||||
get_next();
|
||||
}
|
||||
} else if (text[i] == 'M') {
|
||||
get_next();
|
||||
} else ++i;
|
||||
}
|
||||
};
|
||||
pathFetch.send();
|
||||
|
||||
let vec_len = v => {
|
||||
let len = 0;
|
||||
for (let i = 0; i < v.length; ++i)
|
||||
len += v[i] * v[i];
|
||||
return sqrt(len);
|
||||
}
|
||||
let matrix_inverse = src => {
|
||||
let dst = [],
|
||||
det = 0,
|
||||
cofactor = (c, r) => {
|
||||
let s = (i, j) => src[c + i & 3 | (r + j & 3) << 2];
|
||||
return (c + r & 1 ? -1 : 1) * ((s(1, 1) * (s(2, 2) * s(3, 3) - s(3, 2) * s(2, 3))) -
|
||||
(s(2, 1) * (s(1, 2) * s(3, 3) - s(3, 2) * s(1, 3))) +
|
||||
(s(3, 1) * (s(1, 2) * s(2, 3) - s(2, 2) * s(1, 3))));
|
||||
}
|
||||
for (let n = 0; n < 16; n++) dst.push(cofactor(n >> 2, n & 3));
|
||||
for (let n = 0; n < 4; n++) det += src[n] * dst[n << 2];
|
||||
for (let n = 0; n < 16; n++) dst[n] /= det;
|
||||
return dst;
|
||||
}
|
||||
|
||||
let scale = (v, s) => [ s * v[0] , s * v[1] , s * v[2] ];
|
||||
let norm = v => Math.sqrt(dot(v,v));
|
||||
|
||||
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||||
let matrix_identity = () => {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
}
|
||||
let matrix_translate = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[12] = x;
|
||||
m[13] = y;
|
||||
m[14] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_perspective = (m) => {
|
||||
let ret = []
|
||||
for (let i = 0; i < 16; i++)
|
||||
ret[i] = m[i];
|
||||
for (let i = 2; i < 15; i += 4) {
|
||||
ret[i] = -ret[i];
|
||||
ret[i + 1] += ret[i] / fl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||||
let matrix_rotateX = theta => {
|
||||
let m = matrix_identity();
|
||||
m[5] = cos(theta);
|
||||
m[6] = sin(theta);
|
||||
m[9] = -sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
|
||||
let matrix_rotateY = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[2] = -sin(theta);
|
||||
m[8] = sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_rotateZ = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[1] = sin(theta);
|
||||
m[4] = -sin(theta);
|
||||
m[5] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_scale = (x, y, z) => {
|
||||
if (y === undefined)
|
||||
y = z = x;
|
||||
let m = matrix_identity();
|
||||
m[0] = x;
|
||||
m[5] = y;
|
||||
m[10] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||||
let res = [];
|
||||
if (b.length < m * n) { //mat-vec multiply (i did this for my convenience)
|
||||
for (let i = 0; i < m; ++i) {
|
||||
res[i] = 0;
|
||||
for (let j = 0; j < n; ++j)
|
||||
res[i] += b[j] * a[m * j + i];
|
||||
}
|
||||
return res;
|
||||
} //otherwise mm multiply
|
||||
for (let i = 0; i < m; ++i)
|
||||
for (let j = 0; j < m; ++j) {
|
||||
var t = 0;
|
||||
for (let k = 0; k < n; ++k)
|
||||
t += a[k * m + j] * b[i * n + k];
|
||||
res.push(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
let const_multiply = (c, a, add = 0) => {
|
||||
let m = [];
|
||||
for (let i = 0; i < a.length; ++i)
|
||||
m[i] = (a[i] + add)* c;
|
||||
return m;
|
||||
}
|
||||
|
||||
function dot(a, b) {
|
||||
b=b?b:a;
|
||||
let m = 0;
|
||||
for (let i = 0; i < a.length; ++i)
|
||||
m += a[i] * b[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
function cross(a, b){
|
||||
let m = [];
|
||||
m[0] = a[1]*b[2] - a[2]*b[1];
|
||||
m[1] = a[2]*b[0] - a[0]*b[2];
|
||||
m[2] = a[0]*b[1] - a[1]*b[0];
|
||||
return m;
|
||||
}
|
||||
|
||||
function plus(a, b) {
|
||||
let m = [];
|
||||
for (let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] + b[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
function minus(a, b) {
|
||||
let m = [];
|
||||
for (let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] - b[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
function normalize(v) {
|
||||
let res = [];
|
||||
sum = 0;
|
||||
for (let i = 0; i < v.length; ++i)
|
||||
sum += v[i] * v[i];
|
||||
sum = sqrt(sum);
|
||||
for (let i = 0; i < v.length; ++i)
|
||||
res[i] = v[i] / sum;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
let Matrix = function () {
|
||||
let top = 0,
|
||||
m = [matrix_identity()];
|
||||
this.identity = () => m[top] = matrix_identity();
|
||||
this.translate = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_translate(x, y, z));
|
||||
this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta));
|
||||
this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta));
|
||||
this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta));
|
||||
this.scale = (x, y, z) => m[top] = matrix_multiply(m[top], matrix_scale(x, y, z));
|
||||
this.apply = (m1) => m[top] = matrix_multiply(m[top], m1);
|
||||
this.applyl = (m1) => m[top] = matrix_multiply(m1, m[top]);
|
||||
this.value = () => m[top];
|
||||
this.save = () => {
|
||||
m[top + 1] = m[top].slice();
|
||||
top++;
|
||||
}
|
||||
this.restore = () => --top;
|
||||
}
|
||||
function hitTest_sphere(ray, sphere, r) {
|
||||
let B = dot(ray, sphere);
|
||||
let C = dot(sphere, sphere) - r*r;
|
||||
let D = B * B - C;
|
||||
if (D > 0.) {
|
||||
//console.log(D);
|
||||
//let t = -B - sqrt(D);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function hitTest_square(pt, invMatrix, shape){
|
||||
pt.push(1);
|
||||
const V = dot_xyz(matrix_multiply(invMatrix, [0,0,fl,1])),
|
||||
pos = dot_xyz(matrix_multiply(invMatrix, pt)),
|
||||
W = minus(pos, V),
|
||||
A = shape.slice(1,4),
|
||||
B = shape.slice(8,11),
|
||||
C = shape.slice(15,18),
|
||||
AB = minus(B, A),
|
||||
AC = minus(C, A),
|
||||
AB_AC = cross(AB, AC),
|
||||
VA = minus(V, A),
|
||||
t = - dot(AB_AC, VA)/dot(W, AB_AC),
|
||||
P = plus(V, const_multiply(t, W)),
|
||||
AP = minus(P, A),
|
||||
d_AP_AC = dot(AP, AC),
|
||||
d_AP_AB = dot(AP, AB);
|
||||
if(0 <d_AP_AC && d_AP_AC < dot(AC, AC) && 0 < d_AP_AB && d_AP_AB< dot(AB, AB))
|
||||
return P;
|
||||
else return -1;
|
||||
}
|
||||
let dot_xyz = (v) => {
|
||||
let ret = [];
|
||||
if(v[3])
|
||||
for(let i = 0; i < 3; ++i)
|
||||
ret[i] = v[i]/v[3];
|
||||
else ret = v.slice(0,3);
|
||||
return ret;
|
||||
}
|
||||
let Button = function (onclick = () => {}, shape = [], outlineTy = 'sphere') {
|
||||
this.shape = shape;
|
||||
this.enabled = true;
|
||||
this.outlineTy = outlineTy;
|
||||
this.onClick = onclick;
|
||||
this.updateMatrix = (m) => {this.matrix = matrix_perspective(m); this.invMatrix = matrix_inverse(m);};
|
||||
this.updateMatrix(matrix_identity());
|
||||
this.hovering = false;
|
||||
this.activated = false;
|
||||
this.getShape = () => this.shape;
|
||||
this.draw = () => {setUniform('Matrix4fv', 'uMatrix', false, this.matrix);
|
||||
setUniform('Matrix4fv', 'invMatrix', false, this.invMatrix); drawMesh(this.shape);}
|
||||
this.resetShape = (_sh) => {
|
||||
if(this.shape) delete this.shape;
|
||||
this.shape = new Float32Array(_sh);
|
||||
let maxV = new Array(3).fill(-Infinity), minV = new Array(3).fill(Infinity);
|
||||
for(let i = 0; i < _sh.length; i += 7){
|
||||
const v = [_sh[i + 1], _sh[i + 2], _sh[i + 3]];
|
||||
v.forEach((c, j) => {
|
||||
maxV[j] = maxV[j] > c ? maxV[j] : c;
|
||||
minV[j] = minV[j] < c ? minV[j] : c;
|
||||
})
|
||||
}
|
||||
//build outline
|
||||
switch(this.outlineTy){
|
||||
case 'sphere':
|
||||
this.origin = const_multiply(.5, plus(maxV, minV));
|
||||
//this.origin.push(1);
|
||||
this.radius = 0.6*vec_len(minus(maxV, minV))/2.;
|
||||
break;
|
||||
case 'square':
|
||||
break;
|
||||
case 'circle':
|
||||
break;
|
||||
}
|
||||
switch(this.outlineTy){
|
||||
case 'sphere':
|
||||
this.hitTest = (pt) => {
|
||||
pt.push(1);
|
||||
const V = dot_xyz(matrix_multiply(this.invMatrix, [0,0,fl,1])),
|
||||
pos = dot_xyz(matrix_multiply(this.invMatrix, pt));
|
||||
let W = normalize((minus(pos, V)));
|
||||
let Vp = minus(V, this.origin);
|
||||
return hitTest_sphere(W, Vp, this.radius);
|
||||
}
|
||||
break;
|
||||
case 'square':
|
||||
this.hitTest = (pt) => {
|
||||
this.P = hitTest_square(pt, this.invMatrix, this.shape);
|
||||
return this.P;
|
||||
}
|
||||
break;
|
||||
case 'circle':
|
||||
break;
|
||||
}
|
||||
};
|
||||
if(!shape || shape.length != 0)
|
||||
this.resetShape(shape);
|
||||
canvas_controls.push(this);
|
||||
}
|
||||
let setM = (m) => {
|
||||
let mm = matrix_perspective(m);
|
||||
setUniform('Matrix4fv', 'uMatrix', false, mm);
|
||||
setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(mm));
|
||||
}
|
||||
//------ CREATING MESH SHAPES
|
||||
|
||||
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||||
|
||||
let createMesh = (nu, nv, f, data, oid = 0) => {
|
||||
let tmp = [];
|
||||
for (let v = 0; v < 1; v += 1 / nv) {
|
||||
for (let u = 0; u <= 1; u += 1 / nu) {
|
||||
tmp = tmp.concat(f(u, v, oid, data));
|
||||
tmp = tmp.concat(f(u, v + 1 / nv, oid, data));
|
||||
}
|
||||
tmp = tmp.concat(f(1, v, oid, data));
|
||||
tmp = tmp.concat(f(0, v + 1 / nv, oid, data));
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
//Create a Mesh from Splines
|
||||
let createMeshFromSpline = (idx,
|
||||
nu, nv, oid = 16, additional_offset = 0, f = () => {}) => {
|
||||
let S = paths[idx],
|
||||
meta = path_misc[idx];
|
||||
const n_min = 2;
|
||||
let ds = meta[0] / nv,
|
||||
curr_s = 0,
|
||||
curr_d = S[0][2] / Math.ceil(S[0][2] / ds),
|
||||
i = 0,
|
||||
s = 0;
|
||||
let tmp = [],
|
||||
ret = undefined;
|
||||
while (s < meta[0] - 1 / 100000) {
|
||||
|
||||
for (let u = 0; u <= 1; u += 1 / nu) {
|
||||
tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s, idx, oid, additional_offset));
|
||||
tmp = tmp.concat(getSurface(S[i][1], S[i][0], u, curr_s + curr_d, idx, oid, additional_offset));
|
||||
}
|
||||
tmp = tmp.concat(getSurface(S[i][1], S[i][0], 0, curr_s, idx, oid, additional_offset));
|
||||
tmp = tmp.concat(getSurface(S[i][1], S[i][0], 1, curr_s + curr_d, idx, oid, additional_offset));
|
||||
if (ret = f(i, tmp, oid)) {
|
||||
oid = ret;
|
||||
}
|
||||
curr_s += curr_d;
|
||||
if (curr_s >= 1) {
|
||||
s += S[i][2];
|
||||
++i;
|
||||
if (i >= S.length)
|
||||
break;
|
||||
let curr_n = Math.ceil(S[i][2] / ds);
|
||||
curr_n = curr_n < n_min ? n_min : curr_n;
|
||||
curr_d = 1 / curr_n;
|
||||
curr_s = 0;
|
||||
}
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||||
|
||||
let glueMeshes = (a, b) => {
|
||||
let c = [];
|
||||
for (let i = 0; i < a.length; i++)
|
||||
c.push(a[i]); // a
|
||||
for (let i = 0; i < VERTEX_SIZE; i++)
|
||||
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||||
for (let i = 0; i < VERTEX_SIZE; i++)
|
||||
c.push(b[i]); // + first vertex of b
|
||||
for (let i = 0; i < b.length; i++)
|
||||
c.push(b[i]); // + b
|
||||
return new Float32Array(c);
|
||||
}
|
||||
|
||||
let uvToCone = (u, v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta)*(1-v);
|
||||
let y = Math.sin(theta)*(1-v);
|
||||
let z = 2 * v - 1;
|
||||
return [i, x, y, z].concat(normalize([x, y, 0]));
|
||||
}
|
||||
|
||||
let uvToSphere = (u, v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = Math.PI * (v - .5);
|
||||
let x = Math.cos(theta) * Math.cos(phi);
|
||||
let y = Math.sin(theta) * Math.cos(phi);
|
||||
let z = Math.sin(phi);
|
||||
return [i, x, y, z].concat(normalize([x, y, z]));
|
||||
}
|
||||
|
||||
let uvToTube = (u, v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta);
|
||||
let y = Math.sin(theta);
|
||||
let z = 2 * v - 1;
|
||||
return [i, x, y, z].concat(normalize([x, y, 0]));
|
||||
}
|
||||
|
||||
let uvToDisk = (u, v, i, dz) => {
|
||||
if (dz === undefined)
|
||||
dz = 0;
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta) * v;
|
||||
let y = Math.sin(theta) * v;
|
||||
let z = dz;
|
||||
return [i, x, y, z].concat([0, 0, Math.sign(z)]);
|
||||
}
|
||||
let uvToTorus = (u, v, i, r) => {
|
||||
let theta = 2 * pi;
|
||||
let phi = theta * v;
|
||||
theta *= u;
|
||||
let x = 1 + r * cos(phi);
|
||||
let y = sin(theta) * x;
|
||||
x *= cos(theta);
|
||||
let z = r * sin(phi);
|
||||
let tx = -sin(theta),
|
||||
ty = cos(theta),
|
||||
tsx = sin(phi),
|
||||
tsy = tsx * tx,
|
||||
tsz = cos(phi);
|
||||
tsx *= -ty;
|
||||
return [i, x, y, z].concat(normalize([ty * tsz * 0.5, -tx * tsz, tx * tsy - ty * tsx]));
|
||||
}
|
||||
|
||||
let createCube = (w, h, l, id) => {
|
||||
let mesh = [];
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, -1, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, -1, 0]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, -1, 0]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 0, 1]);
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, 0, 0, 1]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]);
|
||||
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 0, 1]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]);
|
||||
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, -1, 0, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, l / 2, -1, 0, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, -1, 0, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]);
|
||||
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, -1, 0, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||||
|
||||
mesh = mesh.concat([id, -w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 0, 0, -1]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 0, -1]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 1, 0, 0]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 1, 0, 0]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, -l / 2, 1, 0, 0]);
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, -h / 2, l / 2, 1, 0, 0]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]);
|
||||
|
||||
mesh = mesh.concat([id, w / 2, h / 2, l / 2, 0, 1, 0]);
|
||||
mesh = mesh.concat([id, w / 2, h / 2, -l / 2, 0, 1, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, l / 2, 0, 1, 0]);
|
||||
mesh = mesh.concat([id, -w / 2, h / 2, -l / 2, 0, 1, 0]);
|
||||
return new Float32Array(mesh);
|
||||
}
|
||||
|
||||
function updatePositions() {
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||||
m = const_multiply((fl + 1 + mousedz) / (fl + 1), m);
|
||||
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||||
positionsupdated = false;
|
||||
}
|
||||
function controls_hitTest(pos, clicked = false){
|
||||
// let iMin = -1, tMin = 10000.;
|
||||
canvas_controls.forEach((c, i) => {
|
||||
if(c.enabled && c.hitTest && (c.hover||clicked) ){
|
||||
let t = c.hitTest(pos);
|
||||
if(t != -1){
|
||||
if(clicked)
|
||||
c.onClick();
|
||||
else
|
||||
c.hover(true);
|
||||
}
|
||||
else
|
||||
if(c.hovering)
|
||||
c.hover(false);
|
||||
}
|
||||
});
|
||||
//ground
|
||||
if(ground_m && ground){
|
||||
let invG = matrix_inverse(ground_m);
|
||||
let P = hitTest_square(pos, invG, ground);
|
||||
//let Vp = minus(matrix_multiply(invG,[0,0,fl, 1]).slice(0,3), this.origin);
|
||||
if(P!=-1)
|
||||
if(near_magician){
|
||||
if(slider.dragging === true){
|
||||
slider_dl += 10 * (P[0] - slider.lastPos);
|
||||
if(slider_dl < 0) slider_dl = 0;
|
||||
else if (slider_dl > 9) slider_dl = 9;
|
||||
slider.lastPos = P[0];
|
||||
// onUpdate
|
||||
let id = 1 + .45*slider_dl/9;
|
||||
changeID(id, cylinderMesh);
|
||||
} else if(clicked) {
|
||||
const PO = minus([slider_dl/10-.25, .7], [P[0], P[1]]);
|
||||
const rr = PO[0]*PO[0] + PO[1] * PO[1];
|
||||
if(rr < .003){
|
||||
slider.dragging = true;
|
||||
slider.lastPos = P[0];
|
||||
}
|
||||
}
|
||||
} else if (!state.dead){
|
||||
if(clicked || state.running<=0)
|
||||
{
|
||||
state.turn(P, clicked);
|
||||
}
|
||||
//if(state.figure_rot < 0) state.figure_rot += pi;
|
||||
//updateStatus([state.figure_rot, state.direction_l[0]/state.direction_l[1]]);
|
||||
if(clicked)
|
||||
{
|
||||
state.walk();
|
||||
}
|
||||
}
|
||||
}
|
||||
//return iMin;
|
||||
}
|
||||
function hitTest(pos) {
|
||||
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||||
m = const_multiply((fl + 1 + mousedz) / (fl + 1), m);
|
||||
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], [pos[0], -pos[1], -1], 3, 3);
|
||||
|
||||
let W = normalize(minus(trPos, V));
|
||||
let tMin = 10000.;
|
||||
let iMin = -1;
|
||||
for (let i = 0; i < cns; i++) {
|
||||
let Vp = minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||||
let B = dot(W, Vp);
|
||||
let C = dot(Vp, Vp) - Sph[i][4] * Sph[i][4];
|
||||
let D = B * B - C;
|
||||
if (D > 0.) {
|
||||
let t = -B - sqrt(D);
|
||||
if (t > 0.0 && t < tMin) {
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
return iMin;
|
||||
}
|
||||
let matrix_transform = (m, p) => {
|
||||
let x = p[0],
|
||||
y = p[1],
|
||||
z = p[2],
|
||||
w = p[3] === undefined ? 1 : p[3];
|
||||
let q = [m[0] * x + m[4] * y + m[8] * z + m[12] * w,
|
||||
m[1] * x + m[5] * y + m[9] * z + m[13] * w,
|
||||
m[2] * x + m[6] * y + m[10] * z + m[14] * w,
|
||||
m[3] * x + m[7] * y + m[11] * z + m[15] * w
|
||||
];
|
||||
return p[3] === undefined ? [q[0] / q[3], q[1] / q[3], q[2] / q[3]] : q;
|
||||
}
|
||||
let evalSpline = (h, t) => {
|
||||
// t *= (h.length - 2) / 2;
|
||||
// let n = 2 * Math.floor(t);
|
||||
// t = t % 1;
|
||||
// let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]);
|
||||
// return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3];
|
||||
return t * t * t * h[0] + t * t * h[1] + t * h[2] + h[3];
|
||||
}
|
||||
let getSurface = (S0, S1, u, v, offsetidx, id = 15, additional_offset = 0) => {
|
||||
const epsilon = .001;
|
||||
let z0 = evalSpline(S0, v),
|
||||
z1 = evalSpline(S0, v + epsilon),
|
||||
|
||||
r0 = evalSpline(S1, v) - path_misc[offsetidx][1] + additional_offset,
|
||||
r1 = evalSpline(S1, v + epsilon),
|
||||
|
||||
tilt = Math.atan2(r0 - r1, z1 - z0);
|
||||
|
||||
let xx = cos(2 * pi * u),
|
||||
yy = sin(2 * pi * u);
|
||||
let x = r0 * xx, // POSITION
|
||||
y = r0 * yy,
|
||||
z = z0,
|
||||
nx = cos(tilt), // NORMAL
|
||||
ny = nx * yy,
|
||||
nz = sin(tilt);
|
||||
nx *= xx;
|
||||
return [id, x, y, z, nx, ny, nz];
|
||||
}
|
||||
let concat = (a, b) => b.forEach(e => a.push(e));
|
||||
|
||||
let transform_mesh = (m,arr) => {
|
||||
for(let i = 0; i < arr.length; i+=7){
|
||||
const mod = dot_xyz(
|
||||
matrix_multiply(m, [arr[i+1], arr[i+2], arr[i+3], 1]));
|
||||
const mod_nor = normalize(dot_xyz(
|
||||
matrix_multiply(m, [arr[i+4], arr[i+5], arr[i+6], 0])));
|
||||
arr[i+1] = mod[0];
|
||||
arr[i+2] = mod[1];
|
||||
arr[i+3] = mod[2];
|
||||
arr[i+4] = mod_nor[0];
|
||||
arr[i+5] = mod_nor[1];
|
||||
arr[i+6] = mod_nor[2];
|
||||
}
|
||||
}
|
||||
|
||||
let thicken_contour = (thickness, contour) =>{
|
||||
let res = [];
|
||||
let inner_contour = contour.slice();
|
||||
transform_mesh(matrix_scale(thickness), inner_contour);
|
||||
for(let i = 0; i < contour.length - 7; i+=7){
|
||||
for (let j = 0; j < 7; ++ j)
|
||||
res.push(contour[i + j]);
|
||||
res.push(contour[0]);
|
||||
for (let j = 1; j < 7; ++ j)
|
||||
res.push((inner_contour[i + j]+inner_contour[i+7+j])/2);
|
||||
}
|
||||
for (let j = 0; j < 7; ++ j)
|
||||
res.push(contour[contour.length - 7 + j]);
|
||||
res.push(contour[0]);
|
||||
for (let j = 1; j < 7; ++ j)
|
||||
res.push((inner_contour[contour.length - 7 + j]+inner_contour[j])/2);
|
||||
for (let j = 0; j < 7; ++ j)
|
||||
res.push(contour[j]);
|
||||
return res;
|
||||
}
|
||||
|
||||
let sqlen = (a, b) => { return a*a + b*b; }
|
@ -0,0 +1,266 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;', '#define _NDEBUG\n','float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {', ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +', ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);', ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);', ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;', ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);', '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
var ns = 5,
|
||||
cns = 5;
|
||||
fragmentShaderHeader += 'const int ns = ' + ns + ';\n';
|
||||
var fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
var status = '';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let stack = function (){
|
||||
this.storage = ['Ready.'];
|
||||
this.len = 1;
|
||||
this.pop = ()=>{return this.storage[--this.len-1];}
|
||||
this.push = (o)=>{this.storage[this.len++] = o;}
|
||||
this.update = (o)=>{this.storage[this.len-1] = o;}
|
||||
this.top = () => {return this.storage[this.len - 1];}
|
||||
this.clear = () => this.len = 1;
|
||||
}
|
||||
let status_history = new stack();
|
||||
function updateStatus(val){
|
||||
status_history.update(status);
|
||||
status = val;
|
||||
errorMessage.innerHTML = val;
|
||||
}
|
||||
function pushStatus(val){
|
||||
status_history.push(status);
|
||||
status = val;
|
||||
errorMessage.innerHTML = val;
|
||||
}
|
||||
function restoreStatus(val){
|
||||
status = status_history.pop();
|
||||
errorMessage.innerHTML = status;
|
||||
}
|
||||
function resetStatus() {
|
||||
status_history.clear();
|
||||
updateStatus('Ready.');
|
||||
}
|
||||
var texture = [],
|
||||
gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null) {
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0 + i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0 + i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
function initTextures(gl, program){
|
||||
|
||||
}
|
||||
function initVideoTexture(gl, i) {
|
||||
|
||||
}
|
||||
function updateVideoTexture(gl, v_control, i){
|
||||
//return;
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, v_control);
|
||||
}
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function buildShaders(vertexShader, fragmentShader){
|
||||
let curr_program = canvas1.gl.createProgram();
|
||||
let compile_shaders = (type, src) => {
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
gl.attachShader(curr_program, shader);
|
||||
};
|
||||
compile_shaders(gl.VERTEX_SHADER, vertexShader);
|
||||
compile_shaders(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader);
|
||||
gl.linkProgram(curr_program);
|
||||
|
||||
return curr_program;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
console.log('glstart');
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = status;
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line + 1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
} else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader + fragmentShaderDefs + fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
if(!gl.shaders)
|
||||
gl.shaders = [program];
|
||||
else gl.shaders[0] = program;
|
||||
//initTextures(gl, program);
|
||||
positionsupdated = true;
|
||||
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., .3, 1., 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.,
|
||||
.0, .0, .0, .0, .0, .0, .0, .0, .0, 40., 0., .85, 1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for (let i = 0; i < ns; 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));
|
||||
}
|
||||
offset = 0;
|
||||
for (let i = 0; i < n_shapes; i++) {
|
||||
setUniform('3fv', 'starColors[' + i + ']', starColors.slice(offset, offset += 3));
|
||||
}
|
||||
|
||||
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);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL);
|
||||
gl.clearDepth(-1);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(oid);
|
||||
gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4 * 7, 0);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4 * 7, 4);
|
||||
|
||||
let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(normal);
|
||||
gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4 * 7, 4 * 4);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
//gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() {}
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if (gl) {
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
||||
//let VERTEX_SIZE = 3;
|
||||
|
||||
|
||||
let VERTEX_SIZE = 7;
|
||||
|
||||
|
||||
var drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => {
|
||||
gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW);
|
||||
gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE);
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04
|
||||
c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8
|
||||
c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11
|
||||
c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43
|
||||
c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83
|
||||
C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57
|
||||
c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04
|
||||
c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14
|
||||
c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54
|
||||
c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78
|
||||
c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1
|
||||
c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55
|
||||
c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09
|
||||
c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z
|
||||
N
|
||||
M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8
|
||||
c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69
|
||||
c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28
|
||||
c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1
|
||||
c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45
|
||||
c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73
|
||||
c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33
|
||||
c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48
|
||||
c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28
|
||||
c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78
|
||||
c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86
|
||||
c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4
|
||||
c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22
|
||||
C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87
|
||||
c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35
|
||||
c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81
|
||||
c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79
|
||||
c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12
|
||||
c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83
|
||||
c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57
|
||||
c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85
|
||||
c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74
|
||||
c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08
|
||||
c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59
|
||||
c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28
|
||||
c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22
|
||||
c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28
|
||||
c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56
|
||||
c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19
|
||||
c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83
|
||||
c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2
|
||||
c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27
|
||||
c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54
|
||||
c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55
|
||||
c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69
|
||||
c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23
|
||||
c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21
|
||||
C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49
|
||||
c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z
|
||||
N
|
||||
M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z
|
||||
N
|
||||
M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z
|
||||
N
|
||||
M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z
|
||||
N
|
||||
M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17
|
||||
c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64
|
||||
c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17
|
||||
c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42
|
||||
c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21
|
||||
c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84
|
||||
c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59
|
||||
c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88
|
||||
c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z
|
||||
N
|
||||
M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32
|
||||
c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96
|
||||
c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z
|
||||
N
|
||||
M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17
|
||||
c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560
|
||||
c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49
|
||||
c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z
|
||||
N
|
||||
M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38
|
||||
c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91
|
||||
c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76
|
||||
c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28
|
||||
c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85
|
||||
c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09
|
||||
c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96
|
||||
c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95
|
||||
c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32
|
||||
c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58
|
||||
c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z
|
||||
N
|
||||
M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88
|
||||
c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54
|
||||
c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15
|
||||
c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17
|
||||
c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z
|
||||
N
|
||||
M9.22,0C6.92,4.49,4,11.35,2.55,20.09
|
||||
c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95
|
||||
c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91
|
||||
c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77
|
||||
c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z
|
||||
N
|
||||
M
|
||||
M7.97,1.32c-1.2,2.4-2.58,5.53-3.81,9.3c-1.89,5.82-2.51,10.42-2.64,11.39c-0.28,2.2-0.69,6.44,0.73,18.39c0.57,4.79,1.47,11.13,2.94,18.58c0.94,0.01,1.87,0.03,2.81,0.04z
|
||||
N
|
@ -0,0 +1,64 @@
|
||||
|
||||
#ifndef _NDEBUG
|
||||
precision highp float;
|
||||
const int ns = 5;
|
||||
const int cns = 5;
|
||||
float noise(vec3 v){return 1.;}
|
||||
#endif
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
uniform vec3 starColors[10];
|
||||
uniform vec3 V0;
|
||||
varying vec3 norm;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
varying vec3 texPos;
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
void main(){
|
||||
vec3 color =foregroundColor.xyz;
|
||||
float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.;
|
||||
vec3 l = vec3(1,1,1);
|
||||
float alp = 2.*abs(.49999 - fract(id+.00001));
|
||||
if(id <-1.5) {color = vec3(.05,.05,.05);sp = 1.; df=.4; amb = .3, ex = 5.;}
|
||||
else if(id <-.5) {color = vec3(0.,1.,0.2034);}
|
||||
else if(id < 1.5) {color = vec3(1.0000, 0.659, 0.0384);sp = .2; df=.4; amb = .7, ex = 1.;l = color = vec3(1.0000, 0.7, 0.04);}
|
||||
else if (id < 2.5) color = vec3(1.,.16,.36);
|
||||
else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;}
|
||||
else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;}
|
||||
else if (id < 6.5) {}
|
||||
else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;}
|
||||
else if (id < 8.5) {color = starColors[1]; sp = 0.05, df = 0.1, amb = 0.8, ex=10.,l = color;alp*=1.1;}
|
||||
else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 14.5) {
|
||||
color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;
|
||||
if(texPos.y > .3)
|
||||
color = vec3(1.,1.,1.);
|
||||
}
|
||||
else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 16.5) {gl_FragColor=vec4(1.,1.,1., alp);return;}
|
||||
else if (id < 17.5) {color = vec3(.35,.35,.35);sp = .3; df=.6; amb = .1, ex = 1.;}
|
||||
else if (id < 18.5) {
|
||||
color = vec3(.6,.29,.12);sp = .1; df=.2; amb = .7, ex = 1.;
|
||||
vec3 P = vec3(sin(texPos.y*1.), sin(texPos.x*1.5+1.), cos(texPos.z*1.));
|
||||
// APPLY PROCEDURAL NOISE TEXTURE.
|
||||
float cloud = min(0.99, max(0., 1. * noise(.8 * P)));
|
||||
color = (1.-cloud)*color + starColors[5] * cloud*3.;
|
||||
}
|
||||
else if (id < 19.5) {color = vec3(.4,.4,.4);sp = .0; df=.0; amb = 1., ex = 1.,alp=1.;}
|
||||
|
||||
if(id < 0. &&id > -1.5){
|
||||
vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.));
|
||||
// APPLY PROCEDURAL NOISE TEXTURE.
|
||||
float cloud = min(0.99, max(0., 1. * noise(1. * P)));
|
||||
color = (1.-cloud)*color + starColors[5] * cloud*3.;
|
||||
}
|
||||
vec3 V = V0;
|
||||
vec3 W=normalize(glpos-V);
|
||||
vec3 realLDir=normalize(LDir - glpos);
|
||||
|
||||
color = color*(amb+ df*max(0.,dot(norm,realLDir)))
|
||||
+ sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l;
|
||||
gl_FragColor=vec4(sqrt(color), alp);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
uniform mat4 uMatrix;
|
||||
uniform mat4 invMatrix;
|
||||
uniform mat3 transformation;
|
||||
attribute float oid;
|
||||
attribute vec3 aPos;
|
||||
attribute vec3 normal;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
varying vec3 norm;
|
||||
varying vec3 texPos;
|
||||
void main() {
|
||||
vec4 pos = uMatrix * vec4(aPos, 1.);
|
||||
texPos = aPos;
|
||||
gl_Position = pos ;
|
||||
glpos = pos.xyz;
|
||||
id = oid;
|
||||
norm = normalize(vec4(normal,0.)*invMatrix).xyz;
|
||||
}
|
After Width: | Height: | Size: 11 MiB |
@ -0,0 +1,2 @@
|
||||
# graphics_hw2
|
||||
[link](https://billsun.dev/graphics/hw2)
|
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,350 @@
|
||||
<script src=lib2.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/ext-language_tools.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- 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>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
<script id='my_vertex_shader' type='x-shader/x-vertex'>
|
||||
attribute vec3 aPos;
|
||||
varying vec3 vPos;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.);
|
||||
vPos = aPos;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
<!--script src="shader.frag" id='my_fragment_shader' type='x-shader/x-fragment'> </script>
|
||||
|
||||
|
||||
<!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW -------->
|
||||
|
||||
<script id='my_title' type='text/html'>
|
||||
Solar RTX
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- HERE IS WHERE YOU CAN PROVIDE A LONGER TEXT DESCRIPTION -------->
|
||||
|
||||
<script id='my_instructions' type='text/html'><font color=#909090>
|
||||
<p style="font-size:30px; ">In this homework, I implemented Global illumination w/
|
||||
Realtime Recursive Ray Tracing!
|
||||
<p>
|
||||
<i style="font-size:25px;">Usage: </i>
|
||||
<ul>
|
||||
<li>Ctrl+Alt/Option+T: Toggle Texture.</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+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>Double Click on canvas (WITHOUT key modifiers): Toggle Pause/Resume.</li>
|
||||
<li>MOUSE DRAG, SCROLL/WHEEL ZOOM: Changing Viewing point.</li>
|
||||
<li>Use Chromium based browser for better performance.</li>
|
||||
</ul>
|
||||
<i style="font-size:25px;">How it works:</i>
|
||||
<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
|
||||
Specular light and much more (spherical texture mapping, simple interactions, improved UI/shader editor).
|
||||
</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>
|
||||
<li>It recursively bounces and/or refract(NOT IMPLEMENTED YET) itself spawning new rays.</li>
|
||||
<li>The color of this pixel equals to Ambient + Diffuse + ks*color[Reflected] + kt*color[Refracted].
|
||||
(<a href="https://www.cs.drexel.edu/~david/Classes/Papers/p343-whitted.pdf">Turner Whitted Model</a>)</li>
|
||||
<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>
|
||||
</ul>
|
||||
<li>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.
|
||||
</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>
|
||||
</ul>
|
||||
<p>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script>
|
||||
|
||||
// CREATE THE HTML DOCUMENT
|
||||
let flags = 0x0;
|
||||
let vs = my_vertex_shader.innerHTML;
|
||||
//* LOADING FRAGMENT SHADER
|
||||
var client = new XMLHttpRequest();
|
||||
client.open('GET', './shader.frag');
|
||||
client.onloadend = function() {
|
||||
fs = (client.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
gl_start(canvas1, vs, fs);
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
}
|
||||
client.send();
|
||||
document.body.innerHTML = [''
|
||||
,'<font size=7 color=#909090>' + my_title.innerHTML
|
||||
,'<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"'
|
||||
,' alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>'
|
||||
,'<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>'
|
||||
,'<TABLE cellspacing=0 cellpadding=0><TR>'
|
||||
,'<td><font color=red size=5><div id=errorMessage></div></font></td>'
|
||||
,'</TR><TR>'
|
||||
,'<table cellspacing=0>'
|
||||
,'<tr>'
|
||||
,'<td valign=top>'
|
||||
,'<div id="ace" style="width:800px;height:2200px;"></div>'
|
||||
,'</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">'
|
||||
,'<button id="bns" style="margin-left:5px;font-size:24px;width:150px;height:45px">Set Spheres</button>'
|
||||
,'<div style=\'font-size:25px\'>' + my_instructions.innerHTML + '</div>' + '</td>'
|
||||
,'</tr></table>'
|
||||
,'</TR></TABLE>'
|
||||
].join('');
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
let lastTime = Date.now();
|
||||
let animating = true;
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
let mouselastX, mouselastY;
|
||||
let lastClick = undefined;
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<400)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
//moving = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
const mask = 0x8;
|
||||
flags |= mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true
|
||||
mouselastX = mouselastY = undefined;
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
mousedx -= (mouselastX - e.offsetX)/60;
|
||||
mousedy -= (mouselastY - e.offsetY)/60;
|
||||
cx = Math.cos(mousedx);
|
||||
sx = Math.sin(mousedx);
|
||||
cy = Math.cos(mousedy);
|
||||
sy = Math.sin(mousedy);
|
||||
setUniform('4f', 'rot', cx, sx, cy, sy);
|
||||
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;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
// if(ctrl && alt && moving){
|
||||
// }
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
// if(ctrl && alt && moving){
|
||||
// }
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
mousedz += e.wheelDelta/600;
|
||||
setUniform('1f', 'dFL', mousedz);
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
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;
|
||||
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'){
|
||||
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;
|
||||
cx = Math.cos(mousedx);
|
||||
sx = Math.sin(mousedx);
|
||||
cy = Math.cos(mousedy);
|
||||
sy = Math.sin(mousedy);
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('4f', 'rot', cx, sx, cy, sy);
|
||||
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);
|
||||
</script>
|
||||
|
@ -0,0 +1,185 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
let ns = 4, cns = 4;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
let fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER?
|
||||
let errorMsg = '';
|
||||
//
|
||||
// Initialize a texture and load an image.
|
||||
// When the image finished loading copy it into the texture.
|
||||
//
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
// Because images have to be downloaded over the internet
|
||||
// they might take a moment until they are ready.
|
||||
// Until then put a single pixel in the texture so we can
|
||||
// use it immediately. When the image has finished downloading
|
||||
// we'll update the texture with the contents of the image.
|
||||
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
// WebGL1 has different requirements for power of 2 images
|
||||
// vs non power of 2 images so check if the image is a
|
||||
// power of 2 in both dimensions.
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
// Yes, it's a power of 2. Generate mips.
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
// No, it's not a power of 2. Turn off mips and set
|
||||
// wrapping to clamp to edge
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = '<br>';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
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);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
|
@ -0,0 +1,252 @@
|
||||
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform int flags;
|
||||
//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 vec3 fDir;//Flash light direction
|
||||
varying vec3 vPos;// -1 < vPos.x < +1
|
||||
// -1 < vPos.y < +1
|
||||
// vPos.z == 0
|
||||
|
||||
float fl=3.;//ORIGINAL FOCAL LENGTH
|
||||
const float pi=3.14159265359;
|
||||
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){
|
||||
float shifted = float(int(float(flag)/ pow(2.,float(bit))));
|
||||
return fract(shifted/2.)>0.;
|
||||
}
|
||||
float clampv(float val,float l,float h){
|
||||
return val<l?l:val>h?h:val;
|
||||
}
|
||||
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 LCol=vec3(1.,1.,1.);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
// COMPUTE THE RAY ORIGIN AND DIRECTION
|
||||
vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0;
|
||||
vec3 W=normalize(trPos-V);
|
||||
// RAY TRACE TO ALL OBJECTS IN THE SCENE
|
||||
bool rtxoff = getflag(flags, 1),
|
||||
showtexture = !getflag(flags,0),
|
||||
moved = getflag(flags,2)//,
|
||||
// flash = true;//getflag(flags, 3)
|
||||
;//get flags.
|
||||
// bool hit = false;
|
||||
int cnt_ref = n_ref;
|
||||
for(int j=0;j<n_ref;j++)
|
||||
{
|
||||
float tMin=10000.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
// SHIFT COORDINATES, SO THAT SPHERE IS AT (0,0,0)
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
// SOLVE FOR QUADRATIC EQUATION IN t
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t > 0. && t < tMin){
|
||||
tMin = t; //This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
// IF RAY HITS SPHERE
|
||||
if(iMin >= 0){
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin) //* Because GLSL doesn't support non-const index,
|
||||
{ //* 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;
|
||||
if(showtexture)
|
||||
texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz;
|
||||
else texture_color = foregroundColor;
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
//*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
color=(
|
||||
Ambient[i]
|
||||
+Diffuse[i]*max(0.,dot(N,realLDir))*LCol
|
||||
)*texture_color
|
||||
;
|
||||
// + SPECULAR COMPONENT GOES HERE
|
||||
if(rtxoff || j == n_ref - 1) //if it's the last ray
|
||||
color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0.,
|
||||
dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w);
|
||||
//*Pushing current color and ks into stack.
|
||||
//*suppose ks is 0.15 for all spheres, we can
|
||||
//*of course support different ks, kt for different object
|
||||
//*but I didn't have time to do that, just a proof of concept,
|
||||
//*I defined the new sphere structure that could be used in the future.
|
||||
stack[j] = RT(color, ks[i]);
|
||||
V = S; //*NEXT RAY SHOOTING FROM THE INTERSECTION POINT
|
||||
// if(flash && j == 0){
|
||||
// V0 = V - V0;
|
||||
// hit = true;
|
||||
// }
|
||||
W = -normalize(2. * dot(N, W) * N - W);//*W is the next direction of the next ray.
|
||||
|
||||
break;// this is only the innerloop, RT is still going!
|
||||
}
|
||||
}
|
||||
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 sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(
|
||||
0.5 //ambient for ground
|
||||
+0.5*max(0.,realLDir.y)*LCol //diffusion for ground
|
||||
)*groundColor
|
||||
;
|
||||
// + 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);
|
||||
stack[j] = RT(color, 0.15); //ks of ground is 0.1
|
||||
V = S; //Same as above, trace again from S, dir = reflect(W, N).
|
||||
// if(flash && j == 0){
|
||||
// V0 = W;
|
||||
// hit = true;
|
||||
// }
|
||||
W = vec3(W.x, -W.y, W.z);
|
||||
}
|
||||
else{
|
||||
if(j > 0)
|
||||
{
|
||||
// If the light bounces away! The color of it is calculated by
|
||||
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(rtxoff)
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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(color),1.);
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
old/
|
||||
new/
|
@ -0,0 +1,2 @@
|
||||
# graphics_hw3
|
||||
[link](https://billsun.dev/graphics/hw3)
|
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 658 KiB |
After Width: | Height: | Size: 75 KiB |
@ -0,0 +1,341 @@
|
||||
<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/ext-language_tools.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" overflow: hidden !important; width: 600px !important; height:600px !important;" width=600 height=600></canvas>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
<script id='my_vertex_shader' type='x-shader/x-vertex'>
|
||||
attribute vec3 aPos;
|
||||
varying vec3 vPos;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.);
|
||||
vPos = aPos;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
<!--script src="shader.frag" id='my_fragment_shader' type='x-shader/x-fragment'> </script>
|
||||
|
||||
|
||||
<!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW -------->
|
||||
|
||||
<script id='my_title' type='text/html'>
|
||||
RTX Extreme
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- HERE IS WHERE YOU CAN PROVIDE A LONGER TEXT DESCRIPTION -------->
|
||||
|
||||
<script id='my_instructions' type='text/html'><font color=#909090>
|
||||
<p style="font-size:30px; ">In this homework, I implemented Global illumination w/
|
||||
Realtime Recursive Ray Tracing!
|
||||
<p>
|
||||
<i style="font-size:25px;">Usage: </i>
|
||||
<ul>
|
||||
<li>Ctrl+Alt/Option+T: Toggle Texture.</li>
|
||||
<li>Ctrl+S: Download fragment shader.</li>
|
||||
<li>Ctrl+Alt/Option+N: Reset ViewPoint.</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>Double Click on canvas (WITHOUT key modifiers): Toggle Pause/Resume.</li>
|
||||
<li>DRAG, SCROLL on canvas: Changing Viewing point.</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 - 5): number of spheres, performance will suffer if adding too many spheres.</li>
|
||||
</ul>
|
||||
<i style="font-size:25px;">How it works:</i>
|
||||
<ul>
|
||||
<li>I added recursive ray tracing with fraction support.</li>
|
||||
<li><a style="color:red;">About the scene: </a>There're two spheres in the center, the bouncing one in the very center (the Earth) is not transparent nor is it reflective,
|
||||
the outer one is both transparent and reflective and has the texture of the Sun(barely distinguishable).
|
||||
The small sphere embedded running circle is reflective but not transparent. You can also add
|
||||
two additional spheres via the button above. (up to 5).
|
||||
</li>
|
||||
<li>If your scene is clipped, this is a bug from chromium, you may reset the viewport by adjust super sampling.</li>
|
||||
<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>
|
||||
<ul>
|
||||
<li>Pruning: If the weight of this ray is too small, dispose it.</li>
|
||||
<li>Smarter Stack frame utilization: Now the stack frame will only store last rays and next rays.
|
||||
By alternating 2 arrays storing last ray and next ray, I don't need to store other rays.
|
||||
</li>
|
||||
<li>By combining these methods I managed to significantly reduce RT depth and 'stack' size,
|
||||
While supporting nested object and objects both reflection and refraction rays on the same surface.
|
||||
</li>
|
||||
</ul>
|
||||
<img src="./img.jpg"></img>
|
||||
</ul>
|
||||
<p>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script>
|
||||
|
||||
// CREATE THE HTML DOCUMENT
|
||||
let flags = 0x0;
|
||||
let vs = my_vertex_shader.innerHTML;
|
||||
//* LOADING FRAGMENT SHADER
|
||||
var client = new XMLHttpRequest();
|
||||
client.open('GET', './shader.frag');
|
||||
client.onloadend = function() {
|
||||
fs = (client.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
gl_start(canvas1, vs, fs);
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
}
|
||||
client.send();
|
||||
document.body.innerHTML = [''
|
||||
,'<font size=7 color=#909090>' + my_title.innerHTML
|
||||
,'<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"'
|
||||
,' alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>'
|
||||
,'<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>'
|
||||
,'<TABLE cellspacing=0 cellpadding=0><TR>'
|
||||
,'<td><font color=red size=5><div id=errorMessage></div></font></td>'
|
||||
,'</TR><TR>'
|
||||
,'<table cellspacing=0>'
|
||||
,'<tr>'
|
||||
,'<td valign=top>'
|
||||
,'<div id="ace" style="width:800px;height:2200px;"></div>'
|
||||
,'</td><td valign=top>' + document.body.innerHTML
|
||||
,'<input type="number" id="ins" style="margin-left:3px;font-size:24px;width:100px;height:45px" value="3" max="5" min = "1">'
|
||||
,'<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" step="0.2">'
|
||||
,'<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>'
|
||||
,'</tr></table>'
|
||||
,'</TR></TABLE>'
|
||||
].join('');
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
}
|
||||
}
|
||||
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.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
|
||||
let lastTime = Date.now();
|
||||
let animating = true;
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
let mouselastX, mouselastY;
|
||||
let lastClick = undefined;
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<400)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
//moving = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
const mask = 0x8;
|
||||
flags |= mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true
|
||||
mouselastX = mouselastY = undefined;
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
mousedx -= (mouselastX - e.offsetX)/60;
|
||||
mousedy -= (mouselastY - e.offsetY)/60;
|
||||
cx = Math.cos(mousedx);
|
||||
sx = Math.sin(mousedx);
|
||||
cy = Math.cos(mousedy);
|
||||
sy = Math.sin(mousedy);
|
||||
setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]);
|
||||
}
|
||||
mouselastX = e.offsetX;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
mousedz += e.wheelDelta/600;
|
||||
setUniform('1f', 'dFL', mousedz);
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
rtx.style.cursor="pointer";
|
||||
let rtswitch = function(){
|
||||
alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.')
|
||||
rtx.src='./RTXon.svg';
|
||||
}
|
||||
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;
|
||||
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'){
|
||||
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;
|
||||
cx = Math.cos(mousedx);
|
||||
sx = Math.sin(mousedx);
|
||||
cy = Math.cos(mousedy);
|
||||
sy = Math.sin(mousedy);
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('Matrix3fv', 'transformation', false, [cx, sy*sx, sx*cy, 0, cy, -sy, -sx, cx*sy, cx*cy]);
|
||||
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;
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
let startTime = Date.now();
|
||||
let lastFrameTime = 0;
|
||||
function animate(gl) {
|
||||
let uTime;
|
||||
if(animating)
|
||||
{
|
||||
uTime = (Date.now() - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
uTime = (lastTime - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
setUniform('4f', 'Sph[4]', 0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),.12);
|
||||
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;
|
||||
requestAnimationFrame(fpscounter);
|
||||
</script>
|
||||
|
@ -0,0 +1,207 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
let ns = 5, cns = 3;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
let fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0; // IS THIS THE FIREFOX BROWSER?
|
||||
let errorMsg = '';
|
||||
//
|
||||
// Initialize a texture and load an image.
|
||||
// When the image finished loading copy it into the texture.
|
||||
//
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
// Because images have to be downloaded over the internet
|
||||
// they might take a moment until they are ready.
|
||||
// Until then put a single pixel in the texture so we can
|
||||
// use it immediately. When the image has finished downloading
|
||||
// we'll update the texture with the contents of the image.
|
||||
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
// WebGL1 has different requirements for power of 2 images
|
||||
// vs non power of 2 images so check if the image is a
|
||||
// power of 2 in both dimensions.
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
// Yes, it's a power of 2. Generate mips.
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
// No, it's not a power of 2. Turn off mips and set
|
||||
// wrapping to clamp to edge
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = '<br>';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
textures[i] = i;
|
||||
}
|
||||
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
|
||||
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., .3,1.,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.,
|
||||
.0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for(let i = 0; i < ns; 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.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);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if(gl)
|
||||
{
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 173 KiB |
@ -0,0 +1,264 @@
|
||||
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform int flags;
|
||||
//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 vec3 fDir;//Flash light direction
|
||||
varying vec3 vPos;// -1 < vPos.x < +1
|
||||
// -1 < vPos.y < +1
|
||||
// vPos.z == 0
|
||||
|
||||
float fl=3.;//ORIGINAL FOCAL LENGTH
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
const int n_ref=9; //2^(hits) + 1 because each hit now spawn 2 rays.
|
||||
//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];
|
||||
float kr[ns];
|
||||
float kf[ns], kf_air = 1.000293;
|
||||
|
||||
int type[ns];//Nested objects
|
||||
// 1-sphere, 2-compost sphere, 3-
|
||||
struct Object{ //UPDATED SPHERE STRUCTURE THAT SUPPORTS TRANSPARENCY.(UNUSED)
|
||||
vec4 Pos;
|
||||
vec3 Ambient;
|
||||
vec3 Diffuse;
|
||||
vec4 Specular;
|
||||
int textureid;
|
||||
float ks, kt, kr;
|
||||
};
|
||||
struct RT{ //STACK FOR RECURSIVE RAY TRACING.
|
||||
vec3 color;
|
||||
float ks;
|
||||
} ;
|
||||
|
||||
vec3 scolor = vec3(0,0,0); //Actually 2^n_ref
|
||||
struct Ray{
|
||||
vec3 V;
|
||||
vec3 W;
|
||||
float kf, cumulativeK;
|
||||
} lastRay[n_ref/2];
|
||||
bool modulo2(int n){
|
||||
return n-2*(n/2) == 1;
|
||||
}
|
||||
bool getflag(int flag,int bit){
|
||||
int shifted = flag / int(pow(2.,float(bit)));
|
||||
return modulo2(shifted);
|
||||
}
|
||||
float clampv(float val,float l,float h){
|
||||
return val<l?l:val>h?h:val;
|
||||
}
|
||||
void main(){
|
||||
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
|
||||
// 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;
|
||||
kr[0] = 0.25;
|
||||
kr[1] = 0.1;
|
||||
kr[2] = 0.3;
|
||||
kr[3] = 0.05;
|
||||
kf[0] = 1.3;
|
||||
kf[1] = 1.3; //Water
|
||||
kf[2] = 1.5; //Glass
|
||||
kf[3] = 1.; //Vacuum
|
||||
float currKf = kf_air;
|
||||
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 V0=transformation*vec3(0.,0.,fl+dFL), V = V0;
|
||||
vec3 W=normalize(trPos-V);
|
||||
bool rtxoff = getflag(flags, 1),
|
||||
showtexture = !getflag(flags,0),
|
||||
moved = getflag(flags,2);
|
||||
int cnt_ref = n_ref;
|
||||
float currentK = 1.;
|
||||
for(int j=0;j<n_ref;j++)
|
||||
{
|
||||
if(j > 0){
|
||||
Ray currR = lastRay[(j-1)/2];
|
||||
currKf = currR.kf;
|
||||
currentK = currR.cumulativeK;
|
||||
if(currKf <= 0.0000001 || currentK <= 0.0000001)
|
||||
continue; // We make it terminate w/ kf=0
|
||||
V = currR.V;
|
||||
W = currR.W;
|
||||
}
|
||||
float tMin=10000.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
// SHIFT COORDINATES, SO THAT SPHERE IS AT (0,0,0)
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
// SOLVE FOR QUADRATIC EQUATION IN t
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.00001 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
else if (t >= -0.00001){
|
||||
t = -(t + 2.*B);
|
||||
if(t < tMin){
|
||||
tMin = t;
|
||||
iMin = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// IF RAY HITS SPHERE
|
||||
if(iMin >= 0){
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
if(moved)
|
||||
tex_sph=invTr*tex_sph;
|
||||
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;
|
||||
if(showtexture)
|
||||
texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz;
|
||||
else texture_color = foregroundColor;
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
color=(
|
||||
Ambient[i]
|
||||
+Diffuse[i]*max(0.,dot(N,realLDir))*LCol
|
||||
)*texture_color
|
||||
;
|
||||
if(rtxoff || j >= n_ref/2) //if it's the last hit
|
||||
{
|
||||
color += sqrt(float(j+1)) * Specular[i].xyz*pow(max(0.,
|
||||
dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
}
|
||||
else{
|
||||
lastRay[2 * j + 1] = Ray(S, (-(2. * dot(N, W) * N - W)), currKf, currentK * ks[i]); //reflection
|
||||
float ita = currKf/kf[i];
|
||||
float c1 = dot(N, W);
|
||||
float c2 = sqrt(1.-ita*ita*(1.-c1*c1));
|
||||
lastRay[2 * j + 2] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf[i], currentK * kr[i]); //refraction
|
||||
scolor += currentK*(1. - ks[i] - kr[i]) * color;//stack[j] = RT(color, currentK*(1. - ks[i] - kr[i]) * color;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(
|
||||
0.5 //ambient for ground
|
||||
+0.5*max(0.,realLDir.y)*LCol //diffusion for ground
|
||||
)*groundColor
|
||||
;
|
||||
// + 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);
|
||||
//stack[j] = RT(color, currentK); //ks of ground is 0.15
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastRay[2 * j + 1] = Ray(S, vec3(W.x, -W.y, W.z), currKf, currentK * 0.15); //reflection
|
||||
//stack[j] = RT(color, currentK * (1.-0.15)); //ks of ground is 0.15
|
||||
scolor += (currentK*.85)*color;
|
||||
}
|
||||
//lastRay[2 * j + 2] = Ray(S, vec3(0,0,0), 0.); //refraction
|
||||
}
|
||||
else{
|
||||
if(j > 0)
|
||||
{
|
||||
// If the light bounces away! The color of it is calculated by
|
||||
//stack[j] = RT(sqrt(float(j+1))*vec3(4.,4.,4)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), currentK);
|
||||
scolor += currentK * sqrt(float(j+1)*pow(max(0.,dot(W, normalize(LDir - V))), 10.)) * vec3(4.,4.,4);
|
||||
//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.
|
||||
}
|
||||
}
|
||||
if(rtxoff)
|
||||
break;
|
||||
}
|
||||
if(!rtxoff)
|
||||
{
|
||||
color = scolor;
|
||||
// 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;
|
||||
// // }
|
||||
// if(stack[i].ks <0.0000001)
|
||||
// continue;
|
||||
// color += stack[i].ks * stack[i].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;
|
||||
}
|
||||
gl_FragColor=vec4(sqrt(color),1.);
|
||||
}
|
||||
|
@ -0,0 +1,268 @@
|
||||
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform int f_tex, f_rt, f_moved;
|
||||
uniform vec4 rot; //ROTATION VALUES USED TO CALCULATE TRANSFORMATION MATRIX
|
||||
uniform float dFL; //DELTA on FOCAL LENGTH
|
||||
uniform mat3 transformation, invTr;
|
||||
uniform vec3 Ambient[ns], Diffuse[ns];
|
||||
uniform vec4 Specular[ns];
|
||||
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
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
const int n_ref=15; //2^(hits) - 1 because each hit now spawn 2 rays.
|
||||
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;
|
||||
}
|
||||
vec3 getRefraction(vec3 N, vec3 W, float nextkr, float eta, float c1){
|
||||
float c2 = (1.-eta*eta*(1.-c1*c1));
|
||||
c2 = sqrt(abs(c2));
|
||||
return normalize(eta*W + (eta*c1 - c2)*N);
|
||||
}
|
||||
void main(){
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
float currKf = kf_air;
|
||||
vec3 color=vec3(.2, .3, .5);
|
||||
vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1);
|
||||
vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0;
|
||||
vec3 W=(trPos-V);
|
||||
bool rtxoff = false, showtexture = true, moved = false;
|
||||
float currentK = 1.;
|
||||
int curr_ptr = 0, curr_top = 0, next_top = 0;
|
||||
bool final = false, stackswap = false;
|
||||
for(int j=0;j<n_ref;j++)
|
||||
{
|
||||
for(int curr = 0; curr < max_stack; ++curr){
|
||||
if(curr == curr_ptr){
|
||||
bool outward = false;
|
||||
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.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.01 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
outward = false;
|
||||
}
|
||||
else if (t >= -0.01 && t <0.01){
|
||||
t = -(t + 2.*B);
|
||||
if(t > 0.01 && t < tMin){
|
||||
tMin = t;
|
||||
iMin = i;
|
||||
outward = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(iMin >= 0){
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 texture_color;
|
||||
if(showtexture)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
if(moved)
|
||||
;//tex_sph=invTr*tex_sph;
|
||||
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;
|
||||
texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz;
|
||||
}
|
||||
else texture_color = foregroundColor;
|
||||
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
if(outward){
|
||||
float c1 = dot(N, W);
|
||||
if(c1 > 0.)
|
||||
{
|
||||
c1 = -c1;
|
||||
N = -N;
|
||||
outward = true;
|
||||
}
|
||||
else outward = false;
|
||||
color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
|
||||
if(rtxoff || final) //if it's the last hit
|
||||
{
|
||||
color += Specular[i].xyz*pow(max(0.,dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
}
|
||||
else{
|
||||
float eta =kf[i]/currKf;
|
||||
if(outward) eta = 1./eta;
|
||||
float nextks = currentK * ks[i], nextkr = currentK * kr[i];
|
||||
bool refl = nextks > 0.001, refr = nextkr > 0.001;
|
||||
if(refl || refr)
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap){
|
||||
if(refl)
|
||||
{
|
||||
stack2[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack2[k+1] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction
|
||||
else
|
||||
stack2[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}else{
|
||||
if(refl)
|
||||
{
|
||||
stack1[k] = Ray(S, (-(2. * c1 * N - W)), currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack1[k+1] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction
|
||||
else
|
||||
stack1[k] = Ray(S, getRefraction(N, W, nextkr, eta, c1), kf[i], nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scolor += currentK * color;
|
||||
}
|
||||
}
|
||||
else{
|
||||
float c1 = (dot(N, W));
|
||||
float ita = kf_air/currKf;
|
||||
if(c1<0.)
|
||||
{
|
||||
c1 = -c1;
|
||||
N = - N;
|
||||
}
|
||||
float c2 = (1.-ita*ita*(1.-c1*c1));
|
||||
if(c2 >= 0.)
|
||||
c2 = sqrt(c2);
|
||||
else c2 = -sqrt(-c2);
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap)
|
||||
{
|
||||
//stack2[k] = Ray(S, ((2. * c1 * N - W)), currKf, currentK*ks[i]); //reflection
|
||||
//if(c2>=0.)
|
||||
{
|
||||
stack2[k] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf_air, currentK*kr[i]); //refraction
|
||||
next_top ++;
|
||||
}
|
||||
}else{
|
||||
//stack1[k] = Ray(S, ((2. * c1 * N - W)), currKf, currentK*ks[i]); //reflection
|
||||
//if(c2 >= 0.)
|
||||
{
|
||||
stack1[k] = Ray(S, normalize(ita*W + (ita*c1 - c2)*N), kf_air, currentK*kr[i]); //refraction
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
// next_top ++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+2.)/3., (sz+1.)/6.)).xyz;
|
||||
if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
color += groundSpecular.xyz* //specular for ground.
|
||||
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
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{
|
||||
if(j > 0)
|
||||
scolor += currentK * pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(1.,1.,1.);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++curr_ptr >= curr_top){
|
||||
curr_top = next_top;
|
||||
curr_ptr = 0;
|
||||
if(next_top * 2 > max_stack)
|
||||
final = true;
|
||||
stackswap = !stackswap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
gl_FragColor=vec4(sqrt(scolor),1.);
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
|
||||
#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;}
|
||||
#define REFRACTION normalize(c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1)))
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform int f_tex, f_rt, f_moved;
|
||||
uniform float dFL; //DELTA on FOCAL LENGTH
|
||||
uniform mat3 transformation, invTr;
|
||||
uniform vec3 Ambient[ns], Diffuse[ns];
|
||||
uniform vec4 Specular[ns];
|
||||
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
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
|
||||
/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/
|
||||
/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays.
|
||||
/**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;
|
||||
}
|
||||
vec2 getTextCoord(vec3 tex_sph, float R){
|
||||
float tex_x=atan(tex_sph.x,tex_sph.z)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
|
||||
tex_x=fract(tex_x+uTime/20.);
|
||||
return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5);
|
||||
}
|
||||
void main(){
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
float currKf = kf_air;
|
||||
vec3 color=vec3(.2, .3, .5);
|
||||
vec3 trPos = transformation*((dFL+fl+1.)/(fl+1.))*vec3(vPos.xy, -1);
|
||||
vec3 V0=transformation*vec3(0.,0.,fl+dFL), V = V0;
|
||||
vec3 W=(trPos-V);
|
||||
bool rtxoff = false, showtexture = true, moved = false;
|
||||
float currentK = 1.;
|
||||
int curr_ptr = 0, curr_top = 0, next_top = 0;
|
||||
bool final = false, stackswap = false, stop = false;
|
||||
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.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.01 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
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(iMin >= 0){
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 texture_color;
|
||||
if(showtexture)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz;
|
||||
}
|
||||
else texture_color = foregroundColor;
|
||||
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
float c1 =dot(N, W);
|
||||
float eta, nextkf;
|
||||
if(c1<0.){
|
||||
color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
|
||||
if(rtxoff || final) //if it's the last hit
|
||||
{
|
||||
color += Specular[i].xyz*pow(max(0.,
|
||||
dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
c1 = -c1;
|
||||
eta = currKf/kf[i];
|
||||
nextkf = kf[i];
|
||||
}
|
||||
}
|
||||
else{
|
||||
N = -N;
|
||||
eta = currKf/kf_air;
|
||||
nextkf = kf_air;
|
||||
color = Ambient[i];
|
||||
}
|
||||
float c2 = (1.-eta*eta*(1.-c1*c1));
|
||||
float nextks = currentK * ks[i], nextkr = currentK * kr[i];
|
||||
bool refl = nextks > 0.01, refr = nextkr > 0.01;
|
||||
if(refl || refr)
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap){
|
||||
if(refl)
|
||||
{
|
||||
stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack2[k] = Ray(S, REFRACTION, nextkf, 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, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz;
|
||||
if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
color += groundSpecular.xyz* //specular for ground.
|
||||
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
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{
|
||||
if(j > 0)
|
||||
scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1);
|
||||
else scolor = foregroundColor*0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++curr_ptr >= curr_top){
|
||||
if(next_top <= 0)
|
||||
stop = true;
|
||||
if(next_top * 2 > max_stack)
|
||||
final = true;
|
||||
curr_top = next_top;
|
||||
next_top = 0;
|
||||
curr_ptr = 0;
|
||||
|
||||
stackswap = !stackswap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
}
|
||||
gl_FragColor=vec4(sqrt(scolor),1.);
|
||||
}
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,112 @@
|
||||
<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=lib4.header.js></script>
|
||||
<script src=lib4.js></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
|
||||
<font size=7 color=#909090>
|
||||
|
||||
StarCraft RTX
|
||||
|
||||
<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"
|
||||
alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>
|
||||
<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>
|
||||
<TABLE cellspacing=0 cellpadding=0><TR>
|
||||
<td><font color=red size=5><div id=errorMessage></div></font></td>
|
||||
</TR><TR>
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td valign=top>
|
||||
<div id="ace" style="width:800px;height:2200px;"></div>
|
||||
</td><td valign=top>
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" overflow: hidden !important; width: 600px !important; height:600px !important;" width=599 height=599></canvas>
|
||||
</center>
|
||||
</body>
|
||||
<div id="controls">
|
||||
<input type="number" id="ins" style="margin-left:0px;font-size:24px;width:35px;height:45px" value="5" max="5" min = "1">
|
||||
<button id="bns" style="margin-left:0px;font-size:24px;width:105px;height:45px">Spheres</button>
|
||||
<input type="number" id="insamp" style="margin-left:2px;font-size:24px;width:60px;height:45px" value="1" max="4" min = "0.25" step="0.2">
|
||||
<button id="bnsamp" style="margin-left:0px;font-size:24px;width:190px;height:45px">Super Sampling</button>
|
||||
<button id="bnfs" style="margin-left:2px;font-size:24px;width:180px;height:45px">Fullscreen</button>
|
||||
<button id="clrsel" style="margin-left:0px;font-size:24px;width:180px;height:45px">Clear Selection</button>
|
||||
<button id="reset" style="margin-left:0px;font-size:24px;width:100px;height:45px">Reset</button>
|
||||
<div style=\'font-size:25px\'>
|
||||
|
||||
<font color=#909090>
|
||||
|
||||
<i style="font-size:25px;">What's new: </i>
|
||||
<p style="font-size:20px;">
|
||||
As before, drag on the canvas to rotate the scene, scroll on it to change focal point (perspective projection).
|
||||
Double click it to pause/resume.<br>
|
||||
I now added Fullscreen mode so that we can scroll on the canvas without scrolling the whole page and use single-key
|
||||
hotkeys without worrying about conflicts with the editor. <br>
|
||||
<a style="color:red;">Now, you can select a sphere by clicking on it (literally, click the sphere on the canvas!). And drag it around
|
||||
to move it.</a><br>
|
||||
When a sphere is selected, you can also scroll whe mouse wheel to change its radius, and <strong>in fullscreen mode</strong>, press B and F key to send
|
||||
it backward or forward <strong>according to your view point!</strong><br>
|
||||
I also optimized the code a little bit, so that the transformation matrix is completely computed in CPU,
|
||||
and applied (multiplied) inside the <a href="./shader.vert">vertex shader</a>.<br>
|
||||
I suggest moving spheres when pausing the scene by double clicking the canvas, otherwise the spheres are already moving.<br>
|
||||
<strong>If you found the scene somehow clipped, it seems to be a bug of chromium, please simply click on 'Super Sampling' button
|
||||
or refresh the page. Otherwise the interactions won't work because the coordinates are wrong.</strong>
|
||||
</p>
|
||||
<div id="howitworks">
|
||||
<br>
|
||||
<p style="font-size:20px;">
|
||||
<i style="font-size:25px;">How it works:</i><br>
|
||||
I used similar method in raytracing to find the sphere it hits.<br>
|
||||
The transformation methods are implemented in <a href="./lib4.header.js">lib4.header.js</a><br>
|
||||
Interaction parts are in <a href="./lib4.ext.js">lib4.ext.js</a><br>
|
||||
<a href="./lib4.js">lib4.js</a> most contains initialization methods for the renderer.<br>
|
||||
<a href="./index.html">index.html</a> is almost pure html.<br>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</TR></TABLE>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script src="lib4.ext.js"></script>
|
||||
|
||||
<script>
|
||||
setInterval(() => {
|
||||
if(window.vs != null && window.fs != null&& canvas1.setShaders === undefined)
|
||||
gl_start(canvas1, vs, fs);
|
||||
}, 200);
|
||||
</script>
|
@ -0,0 +1,359 @@
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let lastClick = undefined;
|
||||
let animating = true;
|
||||
let flags = 0x0;
|
||||
var startTime = Date.now();
|
||||
let lastTime = Date.now();
|
||||
var lastFrameTime = 0;
|
||||
let oldDocument;
|
||||
let fullscreen = false;
|
||||
let oldparents = {};
|
||||
var tr, div;
|
||||
let canvas_originalsize;
|
||||
let Sph = [];
|
||||
let SphTr = [];
|
||||
let SphDletaR = []
|
||||
let selected = false, selection = -1, dragging = false;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
function toggleFullscreen(element){
|
||||
if(fullscreen)
|
||||
{
|
||||
if (document.exitFullscreen)
|
||||
document.exitFullscreen();
|
||||
else if (document.webkitExitFullscreen)
|
||||
document.webkitExitFullscreen();
|
||||
else if (document.mozCancelFullScreen)
|
||||
document.mozCancelFullScreen();
|
||||
else if (document.msExitFullscreen)
|
||||
document.msExitFullscreen();
|
||||
fullscreen = false;
|
||||
bnfs.innerText = "Fullscreen";
|
||||
}
|
||||
else{
|
||||
if(element.requestFullscreen)
|
||||
element.requestFullscreen();
|
||||
else if (element.webkitRequestFullscreen)
|
||||
element.webkitRequestFullscreen();
|
||||
else if(element.msRequestFullscreen)
|
||||
element.msRequestFullscreen();
|
||||
fullscreen = true;
|
||||
bnfs.innerText = "Exit Fullscreen";
|
||||
}
|
||||
}
|
||||
bnfs.onclick = function(_){
|
||||
if(fullscreen){
|
||||
oldparents[controls].appendChild(controls);
|
||||
oldparents[canvas1].appendChild(canvas1);
|
||||
canvas1.style.width = canvas_originalsize[0];
|
||||
canvas1.style.height = canvas_originalsize[1];
|
||||
howitworks.hidden = false;
|
||||
}else{
|
||||
div = document.createElement("div");
|
||||
tr = document.createElement("table").insertRow();
|
||||
tr.style.backgroundColor="white";
|
||||
let size = Math.min(screen.availHeight, screen.availWidth);
|
||||
canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height];
|
||||
canvas1.style.height = canvas1.style.width = size;
|
||||
howitworks.hidden=true;
|
||||
oldparents[controls] = controls.parentNode;
|
||||
oldparents[canvas1] = canvas1.parentNode;
|
||||
|
||||
let td1 = tr.insertCell();
|
||||
td1.appendChild(canvas1);
|
||||
let td2;
|
||||
td2 = tr.insertCell();
|
||||
td2.style.verticalAlign="top";
|
||||
td2.appendChild(controls);
|
||||
|
||||
div.appendChild(tr);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
toggleFullscreen(div);
|
||||
}
|
||||
clrsel.onclick=function(_){
|
||||
setUniform("1i", "sel", -1);
|
||||
selected = false;
|
||||
selection = -1;
|
||||
}
|
||||
reset.onclick = function(_){
|
||||
clrsel.onclick();
|
||||
if(!animating)
|
||||
pause_resume();
|
||||
flags = 0;
|
||||
moving = false;
|
||||
mousedx = mousedy = mousedz = 0;
|
||||
positionsupdated = true;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
}
|
||||
}
|
||||
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.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
if(fs != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
|
||||
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<400)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
|
||||
});
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
const mask = 0x8;
|
||||
flags |= mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true
|
||||
mouselastX = mouselastY = undefined;
|
||||
let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1]);
|
||||
if(i >= 0)
|
||||
{
|
||||
dragging = true;
|
||||
selected = true;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
else if(selected = true){
|
||||
dragging = false;
|
||||
selected = false;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
let dx = (mouselastX - e.offsetX),
|
||||
dy = (mouselastY - e.offsetY);
|
||||
if(!selected)
|
||||
{
|
||||
mousedx -= dx/60;
|
||||
mousedy -= dy/60;
|
||||
positionsupdated = true;
|
||||
}else if(dragging){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width),
|
||||
2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3);
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2]));
|
||||
}
|
||||
}
|
||||
mouselastX = e.offsetX;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
moving = false;
|
||||
dragging = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
if(!selected){
|
||||
mousedz += e.wheelDelta/600;
|
||||
positionsupdated = true;
|
||||
}
|
||||
else{
|
||||
SphDletaR[selection] += e.wheelDelta / 800;
|
||||
}
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
rtx.style.cursor="pointer";
|
||||
let rtswitch = function(){
|
||||
alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.')
|
||||
rtx.src='./RTXon.svg';
|
||||
}
|
||||
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;
|
||||
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'){
|
||||
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')
|
||||
{
|
||||
reset.onclick();
|
||||
}
|
||||
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;
|
||||
|
||||
if(fullscreen && selected ){
|
||||
if(/*e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft'
|
||||
||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS'||*/e.code =='KeyF'||e.code =='KeyB'){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
|
||||
switch(e.code){
|
||||
// case 'ArrowUp':
|
||||
// m = matrix_multiply(m, matrix_rotateX(0.1));
|
||||
// break;
|
||||
// case 'ArrowDown':
|
||||
// m = matrix_multiply(m, matrix_rotateX(-0.1));
|
||||
// break;
|
||||
// case 'ArrowLeft':
|
||||
// m = matrix_multiply(m, matrix_rotateY(-0.1));
|
||||
// break;
|
||||
// case 'ArrowRight':
|
||||
// m = matrix_multiply(m, matrix_rotateY(0.1));
|
||||
// break;
|
||||
// case 'KeyW':
|
||||
// m = matrix_multiply(m, matrix_rotateZ(0.1));
|
||||
// break;
|
||||
// case 'KeyS':
|
||||
// m = matrix_multiply(m, matrix_rotateZ(-0.1));
|
||||
// break;
|
||||
case 'KeyB':
|
||||
var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
case 'KeyF':
|
||||
var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
}
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], m);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
function animate(gl) {
|
||||
let uTime;
|
||||
if(animating)
|
||||
{
|
||||
uTime = (Date.now() - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
uTime = (lastTime - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15];
|
||||
Sph[1] = [0,0,0,1,.25];
|
||||
Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05];
|
||||
Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25];
|
||||
Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12];
|
||||
|
||||
for(let i = 0; i < ns; ++ i)
|
||||
{
|
||||
let trsph = matrix_multiply(SphTr[i], Sph[i]);
|
||||
trsph[3] = Sph[i][4];
|
||||
setUniform('4fv', 'Sph['+ i + ']', trsph);
|
||||
}
|
||||
|
||||
if(positionsupdated)
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
requestAnimationFrame(fpscounter);
|
@ -0,0 +1,165 @@
|
||||
//Header file, contains global variable definitions,
|
||||
// asynchronized shader loading and utility functions
|
||||
|
||||
let mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let seldx = 0, seldy = 0, seldz = 0;
|
||||
|
||||
var cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
let mouselastX, mouselastY;
|
||||
const fl = 3;
|
||||
let start;
|
||||
var vs, fs;
|
||||
var vsfetch = new XMLHttpRequest();
|
||||
var editor = undefined
|
||||
let cos = Math.cos, sin = Math.sin, tan = Math.tan,
|
||||
acos = Math.acos, asin = Math.asin, atan = Math.atan,
|
||||
sqrt = Math.sqrt;
|
||||
let positionsupdated = true;
|
||||
vsfetch.open('GET', './shader.vert');
|
||||
vsfetch.onloadend = function () {
|
||||
vs = vsfetch.responseText;
|
||||
};
|
||||
vsfetch.send();
|
||||
//* LOADING FRAGMENT SHADER
|
||||
var client = new XMLHttpRequest();
|
||||
client.open('GET', './shader.frag');
|
||||
client.onloadend = function () {
|
||||
fs = (client.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
if (editor != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
};
|
||||
client.send();
|
||||
|
||||
|
||||
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||||
let matrix_identity = () => {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
}
|
||||
let matrix_translate = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[12] = x;
|
||||
m[13] = y;
|
||||
m[14] = z;
|
||||
return m;
|
||||
}
|
||||
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||||
let matrix_rotateX = theta => {
|
||||
let m = matrix_identity();
|
||||
m[5] = cos(theta);
|
||||
m[6] = sin(theta);
|
||||
m[9] = -sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
let matrix_rotateY = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[2] = -sin(theta);
|
||||
m[8] = sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_rotateZ= theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[1] = sin(theta);
|
||||
m[4] = -sin(theta);
|
||||
m[5] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_scale = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[0] = x;
|
||||
m[5] = y;
|
||||
m[10] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||||
let res = [];
|
||||
if (b.length < m*n) { //mat-vec multiply (i did this for my convenience)
|
||||
for (let i = 0; i < m; ++i) {
|
||||
res[i] = 0;
|
||||
for (let j = 0; j < n; ++j)
|
||||
res[i] += b[j] * a[m * j + i];
|
||||
}
|
||||
return res;
|
||||
} //otherwise mm multiply
|
||||
for (let i = 0; i < m; ++i)
|
||||
for (let j = 0; j < m; ++j) {
|
||||
var t = 0;
|
||||
for (let k = 0; k < n; ++k)
|
||||
t += a[k * m + j] * b[i * n + k];
|
||||
res.push(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
let const_multiply = (c, a) => {
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++ i)
|
||||
m[i] = a[i] * c;
|
||||
return m;
|
||||
}
|
||||
function dot(a, b){
|
||||
let m = 0;
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m += a[i] * b[i];
|
||||
return m;
|
||||
}
|
||||
function plus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] + b[i];
|
||||
return m;
|
||||
}
|
||||
function minus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] - b[i];
|
||||
return m;
|
||||
}
|
||||
function normalize(v){
|
||||
let res = [];
|
||||
sum = 0;
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
sum += v[i] * v[i];
|
||||
sum = sqrt(sum);
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
res[i] = v[i] / sum;
|
||||
return res;
|
||||
}
|
||||
function updatePositions() {
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||||
positionsupdated = false;
|
||||
}
|
||||
function hitTest(pos){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3);
|
||||
|
||||
let W=normalize(minus(trPos, V));
|
||||
let tMin=10000.;
|
||||
let iMin = -1;
|
||||
for(let i=0;i<cns;i++){
|
||||
let Vp=minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||||
let B=dot(W,Vp);
|
||||
let C=dot(Vp,Vp)-Sph[i][4]*Sph[i][4];
|
||||
let D=B*B-C;
|
||||
if(D>0.){
|
||||
let t=-B-sqrt(D);
|
||||
if(t > 0.0 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
return iMin;
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
let ns = 5, cns = 5;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
let fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
console.log('glstart');
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = 'Build Your Own Universe!';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
textures[i] = i;
|
||||
}
|
||||
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
|
||||
positionsupdated = true;
|
||||
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., .3,1.,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.,
|
||||
.0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for(let i = 0; i < ns; 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.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);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if(gl)
|
||||
{
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,235 @@
|
||||
|
||||
#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;}
|
||||
#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1)))
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform vec3 Ambient[ns], Diffuse[ns];
|
||||
uniform vec4 Specular[ns];
|
||||
uniform float ks[ns], kr[ns], kf[ns];
|
||||
uniform vec4 Sph[ns];
|
||||
uniform sampler2D uSampler[ns];
|
||||
uniform vec3 V0;
|
||||
uniform int sel;
|
||||
const float kf_air = 1.000293;
|
||||
varying vec3 trPos;
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
|
||||
/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/
|
||||
/*---->*/const int n_ref=31; //2^n-1 because each hit now spawn at most 2 rays.
|
||||
/**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);
|
||||
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;
|
||||
}
|
||||
vec2 getTextCoord(vec3 tex_sph, float R){
|
||||
float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
|
||||
tex_x=fract(tex_x+uTime/20.);
|
||||
return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5);
|
||||
}
|
||||
void main(){
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
float currKf = kf_air;
|
||||
vec3 color=vec3(.2, .3, .5);
|
||||
vec3 V = V0;
|
||||
vec3 W=(trPos-V);
|
||||
bool rtxoff = false, showtexture = true, selected = false;
|
||||
float currentK = 1.;
|
||||
int curr_ptr = 0, curr_top = 0, next_top = 0;
|
||||
bool final = false, stackswap = false, stop = false;
|
||||
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.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.01 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
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(iMin >= 0){
|
||||
if(j == 0 && iMin == sel)
|
||||
selected = true;
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 texture_color;
|
||||
if(showtexture)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz;
|
||||
}
|
||||
else texture_color = foregroundColor;
|
||||
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
float c1 =dot(N, W);
|
||||
float eta, nextkf;
|
||||
if(c1<0.){
|
||||
color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
|
||||
if(rtxoff || final) //if it's the last hit
|
||||
{
|
||||
color += Specular[i].xyz*pow(max(0.,
|
||||
dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
c1 = -c1;
|
||||
eta = currKf/kf[i];
|
||||
nextkf = kf[i];
|
||||
}
|
||||
}
|
||||
else{
|
||||
N = -N;
|
||||
eta = currKf/kf_air;
|
||||
nextkf = kf_air;
|
||||
color = Ambient[i];
|
||||
}
|
||||
float c2 = (1.-eta*eta*(1.-c1*c1));
|
||||
float nextks = currentK * ks[i], nextkr = currentK * kr[i];
|
||||
bool refl = nextks > 0.01, refr = nextkr > 0.01;
|
||||
if(refl || refr)
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap){
|
||||
if(refl)
|
||||
{
|
||||
stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack2[k] = Ray(S, REFRACTION, nextkf, 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, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz;
|
||||
if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
color += groundSpecular.xyz* //specular for ground.
|
||||
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
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{
|
||||
if(j > 0)
|
||||
scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1);
|
||||
else scolor = foregroundColor*0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++curr_ptr >= curr_top){
|
||||
if(next_top <= 0)
|
||||
stop = true;
|
||||
if(next_top * 2 > max_stack)
|
||||
final = true;
|
||||
curr_top = next_top;
|
||||
next_top = 0;
|
||||
curr_ptr = 0;
|
||||
stackswap = !stackswap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
}
|
||||
if(selected)
|
||||
scolor.x += 0.5;
|
||||
gl_FragColor=vec4(sqrt(scolor),1.);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
attribute vec3 aPos;
|
||||
varying vec3 trPos;
|
||||
uniform mat3 transformation;
|
||||
//I used mat3 instead of the augmented mat4 matrix because we
|
||||
//are not doing complex projections yet. I implemented simple
|
||||
//perspective projection back in hw2 by changing focal length
|
||||
//and adjusting the size of the projected surface accrodingly.
|
||||
//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface
|
||||
// = (deltaFl + fl + 1)/(fl + 1) * old surface
|
||||
//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos;
|
||||
//Because we will multiply the resulting vPos by the transformation matrix
|
||||
//anyway, I smashed the ratio into the matrix into avoid doing this in shaders.
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.);
|
||||
trPos = transformation *vec3(aPos.xy, -1);
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
|
||||
#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;}
|
||||
#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1)))
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform vec3 Ambient[ns], Diffuse[ns];
|
||||
uniform vec4 Specular[ns];
|
||||
uniform float ks[ns], kr[ns], kf[ns];
|
||||
uniform vec4 Sph[ns];
|
||||
uniform sampler2D uSampler[ns];
|
||||
uniform vec3 V0;
|
||||
uniform int sel;
|
||||
const float kf_air = 1.000293;
|
||||
varying vec3 trPos;
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
|
||||
/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/
|
||||
/*---->*/const int n_ref=7; //2^n-1 because each hit now spawn at most 2 rays.
|
||||
/**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);
|
||||
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;
|
||||
}
|
||||
vec2 getTextCoord(vec3 tex_sph, float R){
|
||||
float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
|
||||
tex_x=fract(tex_x+uTime/20.);
|
||||
return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5);
|
||||
}
|
||||
void main(){
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
float currKf = kf_air;
|
||||
vec3 color=vec3(.2, .3, .5);
|
||||
vec3 V = V0;
|
||||
vec3 W=(trPos-V);
|
||||
bool rtxoff = false, showtexture = true, selected = false;
|
||||
float currentK = 1.;
|
||||
int curr_ptr = 0, curr_top = 0, next_top = 0;
|
||||
bool final = false, stackswap = false, stop = false;
|
||||
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.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.01 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
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(iMin >= 0){
|
||||
if(j == 0 && iMin == sel)
|
||||
selected = true;
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 texture_color;
|
||||
if(showtexture)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz;
|
||||
}
|
||||
else texture_color = foregroundColor;
|
||||
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
float c1 =dot(N, W);
|
||||
float eta, nextkf;
|
||||
if(c1<0.){
|
||||
color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
|
||||
if(rtxoff || final) //if it's the last hit
|
||||
{
|
||||
color += Specular[i].xyz*pow(max(0.,
|
||||
dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
c1 = -c1;
|
||||
eta = currKf/kf[i];
|
||||
nextkf = kf[i];
|
||||
}
|
||||
}
|
||||
else{
|
||||
N = -N;
|
||||
eta = currKf/kf_air;
|
||||
nextkf = kf_air;
|
||||
color = Ambient[i];
|
||||
}
|
||||
float c2 = (1.-eta*eta*(1.-c1*c1));
|
||||
float nextks = currentK * ks[i], nextkr = currentK * kr[i];
|
||||
bool refl = nextks > 0.01, refr = nextkr > 0.01;
|
||||
if(refl || refr)
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap){
|
||||
if(refl)
|
||||
{
|
||||
stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack2[k] = Ray(S, REFRACTION, nextkf, 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, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz;
|
||||
if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
color += groundSpecular.xyz* //specular for ground.
|
||||
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
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{
|
||||
if(j > 0)
|
||||
scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1);
|
||||
else scolor = foregroundColor*0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++curr_ptr >= curr_top){
|
||||
if(next_top <= 0)
|
||||
stop = true;
|
||||
if(next_top * 2 > max_stack)
|
||||
final = true;
|
||||
curr_top = next_top;
|
||||
next_top = 0;
|
||||
curr_ptr = 0;
|
||||
stackswap = !stackswap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
}
|
||||
if(selected)
|
||||
scolor.x += 0.5;
|
||||
gl_FragColor=vec4(sqrt(scolor),1.);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
attribute vec3 aPos;
|
||||
varying vec3 trPos;
|
||||
uniform mat3 transformation;
|
||||
//I used mat3 instead of the augmented mat4 matrix because we
|
||||
//are not doing complex projections yet. I implemented simple
|
||||
//perspective projection back in hw2 by changing focal length
|
||||
//and adjusting the size of the projected surface accrodingly.
|
||||
//New surface = distance(surface, old viewpoint)/ distance(surface, new viewpoint) * old surface
|
||||
// = (deltaFl + fl + 1)/(fl + 1) * old surface
|
||||
//This is implemented by vPos = (dFl + fl + 1)/(fl + 1) * vPos;
|
||||
//Because we will multiply the resulting vPos by the transformation matrix
|
||||
//anyway, I smashed the ratio into the matrix into avoid doing this in shaders.
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.);
|
||||
trPos = transformation *vec3(aPos.xy, -1);
|
||||
}
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,111 @@
|
||||
<!--<video src="pjsk.mp4" id="pjsk" muted="muted" loop="true" style="position:fixed; left:0; top:0;max-width:100%;min-width:100%;min-height: 100%;z-index: -100;"></video>!-->
|
||||
<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=lib4.header.js></script>
|
||||
<script src=lib4.js></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
|
||||
<font size=7 color=#909090>
|
||||
|
||||
Space Walk
|
||||
|
||||
<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"
|
||||
alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>
|
||||
<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>
|
||||
<TABLE cellspacing=0 cellpadding=0><TR>
|
||||
<td><font color=red size=5><div id=errorMessage></div></font></td>
|
||||
</TR><TR>
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td valign=top>
|
||||
<div id="ace" style="opacity:90%;width:800px;height:2200px;"></div>
|
||||
</td><td valign=top style="background-color:azure;opacity: 95%;">
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" overflow: hidden !important; width: 600px !important; height:600px !important;" width=599 height=599></canvas>
|
||||
</center>
|
||||
</body>
|
||||
<div id="controls">
|
||||
<input type="number" id="ins" style="margin-left:0px;font-size:24px;width:35px;height:45px" value="5" max="5" min = "1">
|
||||
<button id="bns" style="margin-left:0px;font-size:24px;width:105px;height:45px">Spheres</button>
|
||||
<input type="number" id="insamp" style="margin-left:2px;font-size:24px;width:60px;height:45px" value="1" max="4" min = "0.25" step="0.2">
|
||||
<button id="bnsamp" style="margin-left:0px;font-size:24px;width:190px;height:45px">Super Sampling</button>
|
||||
<button id="bnfs" style="margin-left:2px;font-size:24px;width:180px;height:45px">Fullscreen</button>
|
||||
<button id="clrsel" style="margin-left:0px;font-size:24px;width:180px;height:45px">Clear Selection</button>
|
||||
<button id="reset" style="margin-left:0px;font-size:24px;width:100px;height:45px">Reset</button>
|
||||
<button id="mov" style="margin-left:0px;font-size:24px;width:280px;height:45px">Move Lighting & Texture</button>
|
||||
<div style='font-size:25px;'>
|
||||
|
||||
<font color=#909090>
|
||||
|
||||
<i style="font-size:25px;">What's new: </i>
|
||||
<p style="font-size:20px;">
|
||||
I created a little figure that can walk. First, press "Fullscreen" button. In fullscreen mode, <a style="font-size:25px;color:red;"><strong>the figure is able to walk
|
||||
in four directions when pressing corresponding arrow keys.</strong></a><br>
|
||||
I added normal calculation to every shape I created and then I added phong shading to the scene. <br>
|
||||
As before, you can rotate the scene by dragging on the canvas.<br>
|
||||
I added new shapes such as torus and cubes.</strong><br>
|
||||
<strong>If you found the scene somehow clipped, it seems to be a bug of chromium, please simply click on 'Super Sampling' button
|
||||
or refresh the page. .</strong>
|
||||
</p>
|
||||
<div id="howitworks">
|
||||
<br>
|
||||
|
||||
<i style="font-size:25px;">How it works:</i><br>
|
||||
<p style="font-size:20px;">
|
||||
I used the matrix stack to push and pop transformations to create the figure and move it around. The entire scene is then transformed to apply movements and global rotations.<br>
|
||||
The mesh creation functions and transformation methods are implemented in <a href="./lib4.header.js">lib4.header.js</a><br>
|
||||
Animating function and are in <a href="./lib4.ext.js">lib4.ext.js</a><br>
|
||||
<a href="./shader.vert">shader.vert</a> is the vertex shader.<br>
|
||||
<a href="./lib4.js">lib4.js</a> most contains initialization methods for the renderer. You can also see how I passes more attributes like surface normal and
|
||||
object id to the shaders.<br>
|
||||
<a href="./index.html">index.html</a> is almost pure html.<br>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</TR></TABLE>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script src="lib4.ext.js"></script>
|
||||
|
||||
<script>
|
||||
setInterval(() => {
|
||||
if(window.vs != null && window.fs != null&& canvas1.setShaders === undefined)
|
||||
gl_start(canvas1, vs, fs);
|
||||
}, 200);
|
||||
</script>
|
@ -0,0 +1,593 @@
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let lastClick = undefined;
|
||||
let animating = true;
|
||||
let flags = 0x0;
|
||||
var startTime = Date.now();
|
||||
let lastTime = Date.now();
|
||||
var lastFrameTime = 0;
|
||||
let oldDocument;
|
||||
let fullscreen = false, btntoggled = false;
|
||||
let movescene = true;
|
||||
let oldparents = {};
|
||||
var tr, div;
|
||||
let canvas_originalsize;
|
||||
let Sph = [];
|
||||
let SphTr = [];
|
||||
let SphDletaR = []
|
||||
let selected = false, selection = -1, dragging = false;
|
||||
let overall_trans = matrix_identity();
|
||||
let rebuild = true, presentation = true, sRotation = matrix_identity();
|
||||
let facing = 1, running = 0;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
function toggleFullscreen(element){
|
||||
if(fullscreen)
|
||||
{
|
||||
if (document.exitFullscreen)
|
||||
document.exitFullscreen();
|
||||
else if (document.webkitExitFullscreen)
|
||||
document.webkitExitFullscreen();
|
||||
else if (document.mozCancelFullScreen)
|
||||
document.mozCancelFullScreen();
|
||||
else if (document.msExitFullscreen)
|
||||
document.msExitFullscreen();
|
||||
fullscreen = false;
|
||||
bnfs.innerText = "Fullscreen";
|
||||
}
|
||||
else{
|
||||
if(element.requestFullscreen)
|
||||
element.requestFullscreen();
|
||||
else if (element.webkitRequestFullscreen)
|
||||
element.webkitRequestFullscreen();
|
||||
else if(element.msRequestFullscreen)
|
||||
element.msRequestFullscreen();
|
||||
fullscreen = true;
|
||||
bnfs.innerText = "Exit Fullscreen";
|
||||
}
|
||||
}
|
||||
bnfs.onclick = function(e){
|
||||
if(e === "no")
|
||||
;
|
||||
else
|
||||
btntoggled = true;
|
||||
if(fullscreen){
|
||||
oldparents[controls].appendChild(controls);
|
||||
oldparents[canvas1].appendChild(canvas1);
|
||||
canvas1.style.width = canvas_originalsize[0];
|
||||
canvas1.style.height = canvas_originalsize[1];
|
||||
howitworks.hidden = false;
|
||||
}else{
|
||||
div = document.createElement("div");
|
||||
tr = document.createElement("table").insertRow();
|
||||
tr.style.backgroundColor="white";
|
||||
let size = Math.min(screen.availHeight, screen.availWidth);
|
||||
canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height];
|
||||
canvas1.style.height = canvas1.style.width = size;
|
||||
howitworks.hidden=true;
|
||||
oldparents[controls] = controls.parentNode;
|
||||
oldparents[canvas1] = canvas1.parentNode;
|
||||
|
||||
let td1 = tr.insertCell();
|
||||
td1.appendChild(canvas1);
|
||||
let td2;
|
||||
td2 = tr.insertCell();
|
||||
td2.style.verticalAlign="top";
|
||||
td2.appendChild(controls);
|
||||
|
||||
div.appendChild(tr);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
toggleFullscreen(div);
|
||||
}
|
||||
mov.onclick=function(_){
|
||||
movescene = !movescene;
|
||||
if(!movescene)
|
||||
{
|
||||
mov.innerText= "Move Scene";
|
||||
mov.style.width = "170px";
|
||||
}
|
||||
else
|
||||
{
|
||||
mov.innerText = "Move Lighting&Texture";
|
||||
mov.style.width = "280px";
|
||||
}
|
||||
}
|
||||
document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
clrsel.onclick=function(_){
|
||||
setUniform("1i", "sel", -1);
|
||||
selected = false;
|
||||
selection = -1;
|
||||
}
|
||||
reset.onclick = function(_){
|
||||
clrsel.onclick();
|
||||
if(!animating)
|
||||
pause_resume();
|
||||
flags = 0;
|
||||
moving = false;
|
||||
mousedx = mousedy = mousedz = 0;
|
||||
positionsupdated = true;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
}
|
||||
}
|
||||
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.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
if(fs != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
|
||||
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<400)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
});
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
const mask = 0x8;
|
||||
flags |= mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true
|
||||
mouselastX = mouselastY = undefined;
|
||||
let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1]);
|
||||
if(i >= 0)
|
||||
{
|
||||
dragging = true;
|
||||
selected = true;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
else if(selected = true){
|
||||
dragging = false;
|
||||
selected = false;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
let dx = (mouselastX - e.offsetX),
|
||||
dy = (mouselastY - e.offsetY);
|
||||
if(movescene){
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60));
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60));
|
||||
|
||||
}
|
||||
else if(!selected)
|
||||
{
|
||||
mousedx -= dx/60;
|
||||
mousedy -= dy/60;
|
||||
positionsupdated = true;
|
||||
}else if(dragging){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width),
|
||||
2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3);
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2]));
|
||||
}
|
||||
|
||||
}
|
||||
mouselastX = e.offsetX;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
moving = false;
|
||||
dragging = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
if(!selected){
|
||||
mousedz += e.wheelDelta/600;
|
||||
positionsupdated = true;
|
||||
}
|
||||
else{
|
||||
SphDletaR[selection] += e.wheelDelta / 800;
|
||||
}
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
rtx.style.cursor="pointer";
|
||||
let rtswitch = function(){
|
||||
alert('Ray Tracing is always on. See hw2 where rt can be toggled on/off.')
|
||||
rtx.src='./RTXon.svg';
|
||||
}
|
||||
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;
|
||||
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'){
|
||||
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')
|
||||
{
|
||||
reset.onclick();
|
||||
}
|
||||
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;
|
||||
|
||||
if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft'
|
||||
||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS')
|
||||
{
|
||||
switch(e.code){
|
||||
case 'ArrowUp':
|
||||
facing = -2;
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
facing = 2;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
facing = -1;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
facing = 1;
|
||||
break;
|
||||
case 'KeyW':
|
||||
break;
|
||||
case 'KeyS':
|
||||
break;
|
||||
}
|
||||
running = 20;
|
||||
rebuild = true;
|
||||
}
|
||||
if(fullscreen && selected ){
|
||||
if(e.code =='KeyF'||e.code =='KeyB'){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
|
||||
switch(e.code){
|
||||
case 'KeyB':
|
||||
var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
case 'KeyF':
|
||||
var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
}
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], m);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
let squareMesh = new Float32Array([ -1,-1,1,0,0,0,0,0,-1, 1,1,0,0,0,0,0,-1, -1,-1,0,0,0,0,0, -1,1,-1,0 ,0,0,0,0]);
|
||||
let sphereMesh = createMesh(32, 32, uvToSphere);
|
||||
let tubeMesh = createMesh(32, 2, uvToTube,0,1);
|
||||
let diskMesh = createMesh(32, 2, uvToDisk,0,1);
|
||||
let tubeMesh2 = createMesh(32, 2, uvToTube,0,2);
|
||||
let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2);
|
||||
let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2);
|
||||
let tubeMesh3 = createMesh(32, 2, uvToTube,0,3);
|
||||
let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3);
|
||||
let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3);
|
||||
let diskNMesh = createMesh(32, 2, uvToDisk, -1,1);
|
||||
let diskPMesh = createMesh(32, 2, uvToDisk, 1,1);
|
||||
let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh);
|
||||
let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2);
|
||||
let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3);
|
||||
let torusMash = createMesh(32, 32, uvToTorus, 1, 5);
|
||||
let head = createCube(1.5,1,1, 4);
|
||||
|
||||
let objects = [];
|
||||
let addObject = (obj, mat) => {
|
||||
objects.push([obj, mat]);
|
||||
};
|
||||
let clearObject = () => {delete objects; objects = [];};
|
||||
|
||||
let delta_height = 0, delta_l = [0,0];
|
||||
class State{
|
||||
constructor() {
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
this.rh = this.lh = .5*pi;
|
||||
this.lf = this.rf = 0;
|
||||
}
|
||||
initialize(){
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
}
|
||||
next(){
|
||||
//return this.presentation();
|
||||
if(running <= 0)
|
||||
return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0}
|
||||
running --;
|
||||
const steps = 100;
|
||||
let dl = 0;
|
||||
if(this.progress >= steps/2)
|
||||
{
|
||||
this.progress = 0;
|
||||
this.leg = !this.leg;
|
||||
}
|
||||
let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi];
|
||||
for (let i = 0; i < 4; ++i) delta[i] /= steps;
|
||||
if(this.leg)
|
||||
{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[0];
|
||||
this.rh += delta[3];
|
||||
this.lf += delta[1];
|
||||
this.rf += delta[2];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[0];
|
||||
this.rh -= delta[3];
|
||||
this.lf -= delta[1];
|
||||
this.rf-= delta[2];
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[3];
|
||||
this.rh += delta[0];
|
||||
this.lf += delta[2];
|
||||
this.rf += delta[1];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[3];
|
||||
this.rh -= delta[0];
|
||||
this.lf -= delta[2];
|
||||
this.rf-= delta[1];
|
||||
}
|
||||
}
|
||||
let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6);
|
||||
this.progress++;
|
||||
return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps};
|
||||
}
|
||||
// presentation(){
|
||||
// return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0};
|
||||
// }
|
||||
};
|
||||
let build_objects = (state)=>{
|
||||
if(running === 0)
|
||||
rebuild = false;
|
||||
let {lh, lf, rh, rf, dh, dl} = state.next();
|
||||
delta_l[abs(facing)-1] += 0.3* Math.sign(facing) * dl;
|
||||
delta_height = dh;
|
||||
clearObject();
|
||||
M.save();
|
||||
M.save();
|
||||
M.rotateX(pi/2);
|
||||
M.scale(0.5, 0.5, 1);
|
||||
addObject(cylinderMesh, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0,1,0);
|
||||
addObject(head, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.53*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.55*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.3, -1, 0.);
|
||||
M.rotateX(lh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(lf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.3, -1, 0.);
|
||||
M.rotateX(rh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(rf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.restore();
|
||||
//rebuild = false;
|
||||
};
|
||||
let state = new State();
|
||||
var M = new Matrix();
|
||||
|
||||
function animate(gl) {
|
||||
let uTime;
|
||||
if(animating)
|
||||
{
|
||||
uTime = (Date.now() - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
uTime = (lastTime - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15];
|
||||
Sph[1] = [0,0,0,1,.25];
|
||||
Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05];
|
||||
Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25];
|
||||
Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12];
|
||||
|
||||
for(let i = 0; i < ns; ++ i)
|
||||
{
|
||||
let trsph = matrix_multiply(SphTr[i], Sph[i]);
|
||||
trsph[3] = Sph[i][4];
|
||||
setUniform('4fv', 'Sph['+ i + ']', trsph);
|
||||
}
|
||||
if(presentation){
|
||||
M.save();
|
||||
M.scale(0.3);
|
||||
M.rotateY(uTime/5);
|
||||
M.rotateX(1);
|
||||
M.translate(delta_l[0], -delta_height, delta_l[1]);
|
||||
if(facing !=2)
|
||||
M.rotateY(pi*(facing/2));
|
||||
|
||||
//M.translate(0, -delta_height, 0);
|
||||
overall_trans = M.value();
|
||||
M.restore();
|
||||
}else{
|
||||
M.save();
|
||||
// M.rotateY(2);
|
||||
M.rotateX(1);
|
||||
M.scale(0.3);
|
||||
M.translate(delta_l[0], -delta_height, delta_l[1]);
|
||||
overall_trans = M.value();
|
||||
M.restore();
|
||||
}
|
||||
if(rebuild)
|
||||
build_objects(state);
|
||||
for(const [obj, mat] of objects){
|
||||
drawMesh(obj, matrix_multiply(sRotation,matrix_multiply(overall_trans, mat)));
|
||||
}
|
||||
M.save();
|
||||
M.scale(0.1);
|
||||
M.translate(6,0,1);
|
||||
M.rotateX(1);
|
||||
M.rotateY(uTime/4);
|
||||
drawMesh(torusMash, M.value());
|
||||
M.restore();
|
||||
if(positionsupdated)
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
requestAnimationFrame(fpscounter);
|
||||
//pjsk.play();
|
@ -0,0 +1,332 @@
|
||||
//Header file, contains global variable definitions,
|
||||
// asynchronized shader loading and utility functions
|
||||
|
||||
var mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let seldx = 0, seldy = 0, seldz = 0;
|
||||
var enableSelection = false;
|
||||
var cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
var mouselastX, mouselastY;
|
||||
const fl = 3;
|
||||
let start;
|
||||
var vs, fs;
|
||||
var vsfetch = new XMLHttpRequest();
|
||||
var editor = undefined
|
||||
var cos = Math.cos, sin = Math.sin, tan = Math.tan,
|
||||
acos = Math.acos, asin = Math.asin, atan = Math.atan,
|
||||
sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs;
|
||||
var positionsupdated = true;
|
||||
vsfetch.open('GET', './shader.vert');
|
||||
vsfetch.onloadend = function () {
|
||||
vs = vsfetch.responseText;
|
||||
};
|
||||
vsfetch.send();
|
||||
//* LOADING FRAGMENT SHADER
|
||||
var client = new XMLHttpRequest();
|
||||
client.open('GET', './shader.frag');
|
||||
client.onloadend = function () {
|
||||
fs = (client.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
if (editor != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
};
|
||||
client.send();
|
||||
|
||||
|
||||
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||||
let matrix_identity = () => {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
}
|
||||
let matrix_translate = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[12] = x;
|
||||
m[13] = y;
|
||||
m[14] = z;
|
||||
return m;
|
||||
}
|
||||
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||||
let matrix_rotateX = theta => {
|
||||
let m = matrix_identity();
|
||||
m[5] = cos(theta);
|
||||
m[6] = sin(theta);
|
||||
m[9] = -sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
let matrix_rotateY = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[2] = -sin(theta);
|
||||
m[8] = sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_rotateZ= theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[1] = sin(theta);
|
||||
m[4] = -sin(theta);
|
||||
m[5] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_scale = (x, y, z) => {
|
||||
if (y === undefined)
|
||||
y = z = x;
|
||||
let m = matrix_identity();
|
||||
m[0] = x;
|
||||
m[5] = y;
|
||||
m[10] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||||
let res = [];
|
||||
if (b.length < m*n) { //mat-vec multiply (i did this for my convenience)
|
||||
for (let i = 0; i < m; ++i) {
|
||||
res[i] = 0;
|
||||
for (let j = 0; j < n; ++j)
|
||||
res[i] += b[j] * a[m * j + i];
|
||||
}
|
||||
return res;
|
||||
} //otherwise mm multiply
|
||||
for (let i = 0; i < m; ++i)
|
||||
for (let j = 0; j < m; ++j) {
|
||||
var t = 0;
|
||||
for (let k = 0; k < n; ++k)
|
||||
t += a[k * m + j] * b[i * n + k];
|
||||
res.push(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
let const_multiply = (c, a) => {
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++ i)
|
||||
m[i] = a[i] * c;
|
||||
return m;
|
||||
}
|
||||
function dot(a, b){
|
||||
let m = 0;
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m += a[i] * b[i];
|
||||
return m;
|
||||
}
|
||||
function plus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] + b[i];
|
||||
return m;
|
||||
}
|
||||
function minus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] - b[i];
|
||||
return m;
|
||||
}
|
||||
function normalize(v){
|
||||
let res = [];
|
||||
sum = 0;
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
sum += v[i] * v[i];
|
||||
sum = sqrt(sum);
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
res[i] = v[i] / sum;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
let Matrix = function() {
|
||||
let top = 0, m = [ matrix_identity() ];
|
||||
this.identity = () => m[top] = matrix_identity();
|
||||
this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z));
|
||||
this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta));
|
||||
this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta));
|
||||
this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta));
|
||||
this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z));
|
||||
this.value = () => m[top];
|
||||
this.save = () => { m[top+1] = m[top].slice(); top++; }
|
||||
this.restore = () => --top;
|
||||
}
|
||||
|
||||
|
||||
//------ CREATING MESH SHAPES
|
||||
|
||||
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||||
|
||||
let createMesh = (nu, nv, f, data, oid = 0) => {
|
||||
let tmp = [];
|
||||
for (let v = 0 ; v < 1 ; v += 1/nv) {
|
||||
for (let u = 0 ; u <= 1 ; u += 1/nu) {
|
||||
tmp = tmp.concat(f(u,v,oid,data));
|
||||
tmp = tmp.concat(f(u,v+1/nv,oid,data));
|
||||
}
|
||||
tmp = tmp.concat(f(1,v,oid,data));
|
||||
tmp = tmp.concat(f(0,v+1/nv,oid,data));
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
|
||||
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||||
|
||||
let glueMeshes = (a, b) => {
|
||||
let c = [];
|
||||
for (let i = 0 ; i < a.length ; i++)
|
||||
c.push(a[i]); // a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(b[i]); // + first vertex of b
|
||||
for (let i = 0 ; i < b.length ; i++)
|
||||
c.push(b[i]); // + b
|
||||
return new Float32Array(c);
|
||||
}
|
||||
|
||||
|
||||
let uvToSphere = (u,v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = Math.PI * (v - .5);
|
||||
let x = Math.cos(theta) * Math.cos(phi);
|
||||
let y = Math.sin(theta) * Math.cos(phi);
|
||||
let z = Math.sin(phi);
|
||||
return [i, x,y,z].concat(normalize([x, y, z]));
|
||||
}
|
||||
|
||||
let uvToTube = (u,v,i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta);
|
||||
let y = Math.sin(theta);
|
||||
let z = 2 * v - 1;
|
||||
return [i,x,y,z].concat(normalize([x,y,0]));
|
||||
}
|
||||
|
||||
let uvToDisk = (u,v,i,dz) => {
|
||||
if (dz === undefined)
|
||||
dz = 0;
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta) * v;
|
||||
let y = Math.sin(theta) * v;
|
||||
let z = dz;
|
||||
return [i,x,y,z].concat([0,0,Math.sign(z)]);
|
||||
}
|
||||
let uvToTorus = (u,v,i,r) => {
|
||||
let theta = 2 * pi;
|
||||
let phi = theta * v;
|
||||
theta *= u;
|
||||
let x = 1 + r * cos(phi);
|
||||
let y = sin(theta)*x;
|
||||
x *=cos(theta);
|
||||
let z = r * sin(phi);
|
||||
let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi);
|
||||
tsx*=-ty;
|
||||
return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx]));
|
||||
}
|
||||
|
||||
let createCube = (w, h, l,id) => {
|
||||
let mesh = [];
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]);
|
||||
return new Float32Array(mesh);
|
||||
|
||||
// let verts = [];
|
||||
// for(let i = -w/2; i < w; i += w)
|
||||
// for(let j = -h/2; j < h; j += h)
|
||||
// for (let k = -l/2; k < l; k += l)
|
||||
// verts.push([id, i, j, k]);
|
||||
// let mesh = [];
|
||||
// let n = 0;
|
||||
// for(let j = 0; j < 4; ++ j)
|
||||
// mesh = mesh.concat(verts[j]);
|
||||
// for(let i = 0; i < 2; ++ i)
|
||||
// for(let j = 0; j < 2; ++ j)
|
||||
// mesh = mesh.concat(verts[6 - i*2 + j]);
|
||||
// for(let j = 0; j < 2; ++ j)
|
||||
// mesh = mesh.concat(verts[j]);
|
||||
// mesh = mesh.concat(verts[5]);
|
||||
// mesh = mesh.concat(verts[3]);
|
||||
// mesh = mesh.concat(verts[7]);
|
||||
// mesh = mesh.concat(verts[7]);
|
||||
// mesh = mesh.concat(verts[6]);
|
||||
// mesh = mesh.concat(verts[6]);
|
||||
// mesh = mesh.concat(verts[4]);
|
||||
// mesh = mesh.concat(verts[2]);
|
||||
// mesh = mesh.concat(verts[0]);
|
||||
// return new Float32Array(mesh);
|
||||
//CREATING CUBES THIS WAY REDUCES VERTICES BUT MAKES IT HARDER TO DO LIGHTING
|
||||
}
|
||||
|
||||
function updatePositions() {
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||||
positionsupdated = false;
|
||||
}
|
||||
function hitTest(pos){
|
||||
if(!enableSelection)
|
||||
return -1;
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3);
|
||||
|
||||
let W=normalize(minus(trPos, V));
|
||||
let tMin=10000.;
|
||||
let iMin = -1;
|
||||
for(let i=0;i<cns;i++){
|
||||
let Vp=minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||||
let B=dot(W,Vp);
|
||||
let C=dot(Vp,Vp)-Sph[i][4]*Sph[i][4];
|
||||
let D=B*B-C;
|
||||
if(D>0.){
|
||||
let t=-B-sqrt(D);
|
||||
if(t > 0.0 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
return iMin;
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
var ns = 5, cns = 5;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
var fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
console.log('glstart');
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = 'Build Your Own Universe!';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
textures[i] = i;
|
||||
}
|
||||
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
|
||||
positionsupdated = true;
|
||||
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., .3,1.,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.,
|
||||
.0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for(let i = 0; i < ns; 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.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);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL);
|
||||
gl.clearDepth(-1);
|
||||
let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(oid);
|
||||
gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4);
|
||||
|
||||
let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(normal);
|
||||
gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
//gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if(gl)
|
||||
{
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
||||
//let VERTEX_SIZE = 3;
|
||||
|
||||
|
||||
let VERTEX_SIZE = 7;
|
||||
|
||||
|
||||
let drawMesh = (mesh, m) => {
|
||||
setUniform('Matrix4fv', 'uMatrix', false, m);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, mesh.length / VERTEX_SIZE);
|
||||
}
|
||||
|
@ -0,0 +1,253 @@
|
||||
|
||||
#define _DEBUG_BREAK {gl_FragColor=vec4(1,0,0,1); return;}
|
||||
#define REFRACTION (c2 >= 0.? (eta*W + (eta*c1 - sqrt(c2))*N) : ((W + c1*N)/sqrt(1.-c1*c1)))
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
vec3 groundColor = vec3(.2, .3, .5);
|
||||
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
|
||||
uniform float uTime;// TIME, IN SECONDS
|
||||
uniform vec3 Ambient[ns], Diffuse[ns];
|
||||
uniform vec4 Specular[ns];
|
||||
uniform float ks[ns], kr[ns], kf[ns];
|
||||
uniform vec4 Sph[ns];
|
||||
uniform sampler2D uSampler[ns];
|
||||
uniform vec3 V0;
|
||||
uniform int sel;
|
||||
const float kf_air = 1.000293;
|
||||
varying vec3 trPos;
|
||||
varying vec3 norm;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
varying vec3 apos;
|
||||
const float pi=3.14159265359;
|
||||
const float _2pi=2.*pi;
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
vec3 LCol=vec3(1.,1.,1.);
|
||||
/***********PLEASE DO INCREASE n_ref(RT DEPTH) FOR BETTER RESULTS************/
|
||||
/*---->*/const int n_ref=15; //2^n-1 because each hit now spawn at most 2 rays.
|
||||
/**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);
|
||||
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;
|
||||
}
|
||||
vec2 getTextCoord(vec3 tex_sph, float R){
|
||||
float tex_x=atan(tex_sph.z,tex_sph.x)/_2pi + 0.5;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
|
||||
tex_x=fract(tex_x+uTime/20.);
|
||||
return vec2(tex_x,-asin(tex_sph.y/R)/pi + 0.5);
|
||||
}
|
||||
|
||||
void rtx(){
|
||||
|
||||
float currKf = kf_air;
|
||||
vec3 color=vec3(.2, .3, .5);
|
||||
vec3 V = V0;
|
||||
vec3 W=(trPos-V);
|
||||
bool rtxoff = false, showtexture = true, selected = false;
|
||||
float currentK = 1.;
|
||||
int curr_ptr = 0, curr_top = 0, next_top = 0;
|
||||
bool final = false, stackswap = false, stop = false;
|
||||
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.;
|
||||
int iMin = -1;
|
||||
for(int i=0;i<cns;i++){
|
||||
vec3 Vp=V-Sph[i].xyz;
|
||||
float B=dot(W,Vp);
|
||||
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
|
||||
float D=B*B-C;
|
||||
if(D>0.){
|
||||
float t=-B-sqrt(D);
|
||||
if(t >= 0.01 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
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(iMin >= 0){
|
||||
if(j == 0 && iMin == sel)
|
||||
selected = true;
|
||||
float t = tMin;
|
||||
vec3 S=V+t*W;
|
||||
for(int i = 0; i < cns; ++ i)
|
||||
if(i == iMin)
|
||||
{
|
||||
vec3 texture_color;
|
||||
if(showtexture)
|
||||
{
|
||||
vec3 tex_sph = (S-Sph[i].xyz);
|
||||
texture_color=texture2D(uSampler[i],getTextCoord(tex_sph, Sph[i].w)).xyz;
|
||||
}
|
||||
else texture_color = foregroundColor;
|
||||
|
||||
vec3 N=normalize(S-Sph[i].xyz);
|
||||
vec3 realLDir=normalize(LDir-S);
|
||||
float c1 =dot(N, W);
|
||||
float eta, nextkf;
|
||||
if(c1<0.){
|
||||
color=(Ambient[i]+Diffuse[i]*max(0.,dot(N,realLDir))*LCol)*texture_color;
|
||||
if(rtxoff || final) //if it's the last hit
|
||||
{
|
||||
color += Specular[i].xyz*pow(max(0.,
|
||||
dot(-2.*c1*N-realLDir,realLDir)),Specular[i].w);
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
c1 = -c1;
|
||||
eta = currKf/kf[i];
|
||||
nextkf = kf[i];
|
||||
}
|
||||
}
|
||||
else{
|
||||
N = -N;
|
||||
eta = currKf/kf_air;
|
||||
nextkf = kf_air;
|
||||
color = Ambient[i];
|
||||
}
|
||||
float c2 = (1.-eta*eta*(1.-c1*c1));
|
||||
float nextks = currentK * ks[i], nextkr = currentK * kr[i];
|
||||
bool refl = nextks > 0.01, refr = nextkr > 0.01;
|
||||
if(refl || refr)
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
if(stackswap){
|
||||
if(refl)
|
||||
{
|
||||
stack2[k] = Ray(S, 2. * c1 * N + W, currKf, nextks); //reflection
|
||||
currentK -= nextks;
|
||||
next_top ++;
|
||||
}
|
||||
if(refr)
|
||||
{
|
||||
if(refl)
|
||||
stack2[k+1] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack2[k] = Ray(S, REFRACTION, nextkf, 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, nextkf, nextkr); //refraction
|
||||
else
|
||||
stack1[k] = Ray(S, REFRACTION, nextkf, nextkr); //refraction
|
||||
currentK -= nextkr;
|
||||
next_top ++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
scolor += color * currentK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float t = -(.2+V.y)/W.y;
|
||||
float sx = V.x + t* W.x, sz = V.z + t * W.z;
|
||||
|
||||
if(t >= 0. && abs(sx) < 1.5 && abs(sz) < 3.)
|
||||
{
|
||||
vec3 S = vec3(sx, -.2, sz);
|
||||
vec3 realLDir=normalize(LDir - S);
|
||||
color=(0.5+0.5*max(0.,realLDir.y)*LCol)*texture2D(uSampler[4],vec2((sx+1.4)/3., (sz+1.5)/4.)).xyz;
|
||||
if(rtxoff || final&&abs(sx)<1.5 && abs(sz+.6)<3.)
|
||||
{
|
||||
color += groundSpecular.xyz* //specular for ground.
|
||||
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
|
||||
scolor += currentK * color;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < max_stack; ++k)
|
||||
if(k == next_top){
|
||||
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{
|
||||
if(j > 0)
|
||||
scolor += currentK * (pow(max(0.,dot(W, normalize(LDir - V))), 10.) * vec3(3.,3.,3.) + foregroundColor*0.1);
|
||||
else scolor = foregroundColor*0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(++curr_ptr >= curr_top){
|
||||
if(next_top <= 0)
|
||||
stop = true;
|
||||
if(next_top * 2 > max_stack)
|
||||
final = true;
|
||||
curr_top = next_top;
|
||||
next_top = 0;
|
||||
curr_ptr = 0;
|
||||
stackswap = !stackswap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(stop)
|
||||
break;
|
||||
}
|
||||
if(selected)
|
||||
scolor.x += 0.5;
|
||||
gl_FragColor=vec4((scolor),1.);
|
||||
}
|
||||
void main(){
|
||||
vec3 color =foregroundColor.xyz;
|
||||
float sp = 0.4, df = 0.4, amb = 0.4;
|
||||
if(id == 4.) {rtx();color = gl_FragColor.xyz;}
|
||||
else if (id == 1.) color = vec3(1.,.4,.6);
|
||||
else if (id == 5.) {color = vec3(1, .9375,.7329);sp = .5; df=.8; amb = .05;}
|
||||
vec3 V = V0;
|
||||
vec3 W=normalize(glpos-V);
|
||||
vec3 realLDir=normalize(LDir - glpos);
|
||||
color = color*(amb+ df*max(0.,dot(norm,realLDir)))//specular for ground.
|
||||
+ sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),5.)*vec3(1,1,1);
|
||||
gl_FragColor=vec4(sqrt(color), 1.);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
uniform mat4 uMatrix;
|
||||
uniform mat3 transformation;
|
||||
attribute float oid;
|
||||
attribute vec3 aPos;
|
||||
attribute vec3 normal;
|
||||
varying vec3 trPos;
|
||||
varying float id;
|
||||
varying vec3 norm;
|
||||
varying vec3 glpos;
|
||||
varying vec3 apos;
|
||||
void main() {
|
||||
vec4 pos = uMatrix * vec4(aPos, 1.);
|
||||
gl_Position = pos * vec4(1., 1., -1., 1.);
|
||||
id = oid;
|
||||
norm = normalize((uMatrix*vec4(normal,0.)).xyz);
|
||||
trPos = transformation *vec3(pos.xy, -1);
|
||||
apos = aPos;
|
||||
glpos = gl_Position.xyz;
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,109 @@
|
||||
<!--<video src="pjsk.mp4" id="pjsk" muted="muted" loop="true" style="position:fixed; left:0; top:0;max-width:100%;min-width:100%;min-height: 100%;z-index: -100;"></video>-->
|
||||
<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=lib6.header.js></script>
|
||||
<script src=lib6.js></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
|
||||
<font size=7 color=#909090>
|
||||
|
||||
Spring Break
|
||||
|
||||
<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"
|
||||
alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>
|
||||
<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>
|
||||
<TABLE cellspacing=0 cellpadding=0><TR>
|
||||
<td><font color=red size=5><div id=errorMessage></div></font></td>
|
||||
</TR><TR>
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td valign=top>
|
||||
<div id="ace" style="opacity:90%;width:800px;height:2200px;"></div>
|
||||
</td><td valign=top style="background-color:azure;opacity: 100%;">
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" background-color:#FFF7F8;opacity: 100%;overflow: hidden !important; width: 600px !important; height:600px !important;" width=1199 height=1199></canvas>
|
||||
</center>
|
||||
</body>
|
||||
<div id="controls">
|
||||
<input type="number" id="ins" style="display:none;margin-left:0px;font-size:24px;width:35px;height:45px" value="5" max="5" min = "1">
|
||||
<button id="bns" style="display:none;margin-left:0px;font-size:24px;width:105px;height:45px">Spheres</button>
|
||||
<input type="number" id="insamp" style="margin-left:2px;font-size:24px;width:60px;height:45px" value="2" max="10" min = "0.1" step="0.2">
|
||||
<button id="bnsamp" style="margin-left:0px;font-size:24px;width:190px;height:45px">Super Sampling</button>
|
||||
<button id="bnfs" style="margin-left:2px;font-size:24px;width:180px;height:45px">Fullscreen</button>
|
||||
<button id="clrsel" style="display:none;margin-left:0px;font-size:24px;width:180px;height:45px">Clear Selection</button>
|
||||
<button id="reset" style="margin-left:0px;font-size:24px;width:100px;height:45px">Reset</button>
|
||||
<button id="mov" style="margin-left:0px;font-size:24px;width:180px;height:45px">Move Lighting</button>
|
||||
<button id="pause" style="margin-left:0px;font-size:24px;width:100px;height:45px">Pause</button>
|
||||
<div style='font-size:25px;'>
|
||||
|
||||
<font color=#909090>
|
||||
|
||||
<i style="font-size:25px;">What's new: </i>
|
||||
<p style="font-size:20px;">
|
||||
I corrected the Phong lighting, added perspective and implemented both splines.<br>
|
||||
Since we're learning splines. I figured it would be interesting to
|
||||
do vector graphics.<br>
|
||||
It seems that most paths in a svg file (see <a href="./paths.txt">path.txt</a>)
|
||||
are a combination multiple of end-to-end Bezier Splines.<br>
|
||||
I created these shapes in Adobe Illustrator and output them to svg.<br>
|
||||
I processed the path data in (see <a href="./lib6.header.js">lib6.header.js</a>) and
|
||||
transformed them into multiple Bezier functions and then interpolated them in <a href="./lib6.ext.js">lib6.ext.js</a>
|
||||
so that they appears to be smooth shapes.
|
||||
<br>
|
||||
I also made animations with Hermite Splines.<br>
|
||||
As before, you can 'walk' by pressing arrow keys, it's now looked more like it because
|
||||
I added a ground and the scene is now perspective. Again, better used in Fullscreen mode.<br>
|
||||
</p>
|
||||
<div id="howitworks">
|
||||
<br>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</TR></TABLE>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script src="lib6.ext.js"></script>
|
||||
|
||||
<script>
|
||||
setInterval(() => {
|
||||
if(window.vs != null && window.fs != null&& canvas1.setShaders === undefined)
|
||||
gl_start(canvas1, vs, fs);
|
||||
}, 200);
|
||||
</script>
|
@ -0,0 +1,795 @@
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let lastClick = undefined;
|
||||
let animating = true;
|
||||
let flags = 0x0;
|
||||
var startTime = Date.now();
|
||||
let lastTime = Date.now();
|
||||
var lastFrameTime = 0;
|
||||
let oldDocument;
|
||||
let fullscreen = false, btntoggled = false;
|
||||
let movescene = true;
|
||||
let oldparents = {};
|
||||
var tr, div;
|
||||
let canvas_originalsize;
|
||||
let Sph = [];
|
||||
let SphTr = [];
|
||||
let SphDletaR = []
|
||||
let selected = false, selection = -1, dragging = false;
|
||||
let overall_trans = matrix_identity(), overall_ground;
|
||||
let rebuild = true, buildsplines = true, presentation = false, sRotation = matrix_identity();
|
||||
let facing = 1, running = 0;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
function ev_supersample(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);
|
||||
}
|
||||
function toggleFullscreen(element){
|
||||
if(fullscreen)
|
||||
{
|
||||
if (document.exitFullscreen)
|
||||
document.exitFullscreen();
|
||||
else if (document.webkitExitFullscreen)
|
||||
document.webkitExitFullscreen();
|
||||
else if (document.mozCancelFullScreen)
|
||||
document.mozCancelFullScreen();
|
||||
else if (document.msExitFullscreen)
|
||||
document.msExitFullscreen();
|
||||
fullscreen = false;
|
||||
bnfs.innerText = "Fullscreen";
|
||||
}
|
||||
else{
|
||||
if(element.requestFullscreen)
|
||||
element.requestFullscreen();
|
||||
else if (element.webkitRequestFullscreen)
|
||||
element.webkitRequestFullscreen();
|
||||
else if(element.msRequestFullscreen)
|
||||
element.msRequestFullscreen();
|
||||
fullscreen = true;
|
||||
bnfs.innerText = "Exit Fullscreen";
|
||||
}
|
||||
}
|
||||
bnfs.onclick = function(e){
|
||||
if(e === "no")
|
||||
;
|
||||
else
|
||||
btntoggled = true;
|
||||
if(fullscreen){
|
||||
oldparents[controls].appendChild(controls);
|
||||
oldparents[canvas1].appendChild(canvas1);
|
||||
canvas1.style.width = canvas_originalsize[0];
|
||||
canvas1.style.height = canvas_originalsize[1];
|
||||
howitworks.hidden = false;
|
||||
}else{
|
||||
div = document.createElement("div");
|
||||
tr = document.createElement("table").insertRow();
|
||||
tr.style.backgroundColor="white";
|
||||
let size = Math.min(screen.availHeight, screen.availWidth);
|
||||
canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height];
|
||||
canvas1.style.height = canvas1.style.width = size;
|
||||
howitworks.hidden=true;
|
||||
oldparents[controls] = controls.parentNode;
|
||||
oldparents[canvas1] = canvas1.parentNode;
|
||||
|
||||
let td1 = tr.insertCell();
|
||||
td1.appendChild(canvas1);
|
||||
let td2;
|
||||
td2 = tr.insertCell();
|
||||
td2.style.verticalAlign="top";
|
||||
td2.appendChild(controls);
|
||||
|
||||
div.appendChild(tr);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
toggleFullscreen(div);
|
||||
ev_supersample();
|
||||
}
|
||||
mov.onclick=function(_){
|
||||
movescene = !movescene;
|
||||
if(!movescene)
|
||||
{
|
||||
mov.innerText= "Move Scene";
|
||||
mov.style.width = "170px";
|
||||
}
|
||||
else
|
||||
{
|
||||
mov.innerText = "Move Lighting";
|
||||
mov.style.width = "180px";
|
||||
}
|
||||
}
|
||||
document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
clrsel.onclick=function(_){
|
||||
setUniform("1i", "sel", -1);
|
||||
selected = false;
|
||||
selection = -1;
|
||||
}
|
||||
reset.onclick = function(_){
|
||||
clrsel.onclick();
|
||||
if(!animating)
|
||||
pause_resume();
|
||||
flags = 0;
|
||||
moving = false;
|
||||
mousedx = mousedy = mousedz = 0;
|
||||
positionsupdated = true;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
}
|
||||
}
|
||||
bnsamp.onclick=ev_supersample;
|
||||
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
if(fs != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
|
||||
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<200)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
});
|
||||
pause.onclick = pause_resume;
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
const mask = 0x8;
|
||||
flags |= mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true
|
||||
mouselastX = mouselastY = undefined;
|
||||
let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1]);
|
||||
if(i >= 0)
|
||||
{
|
||||
dragging = true;
|
||||
selected = true;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
else if(selected = true){
|
||||
dragging = false;
|
||||
selected = false;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
let dx = (mouselastX - e.offsetX),
|
||||
dy = (mouselastY - e.offsetY);
|
||||
if(movescene){
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60));
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60));
|
||||
|
||||
}
|
||||
else if(!selected)
|
||||
{
|
||||
mousedx -= dx/60;
|
||||
mousedy -= dy/60;
|
||||
positionsupdated = true;
|
||||
}else if(dragging){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width),
|
||||
2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3);
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2]));
|
||||
}
|
||||
|
||||
}
|
||||
mouselastX = e.offsetX;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
moving = false;
|
||||
dragging = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
if(!selected){
|
||||
mousedz += e.wheelDelta/600;
|
||||
positionsupdated = true;
|
||||
}
|
||||
else{
|
||||
SphDletaR[selection] += e.wheelDelta / 800;
|
||||
}
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
rtx.style.cursor="pointer";
|
||||
let rtswitch = function(){
|
||||
alert('Ray Tracing is off due to global silicon shortage. \nSend me an RTX 3090 to enable Ray Tracing again.')
|
||||
rtx.src='./RTXon.svg';
|
||||
}
|
||||
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;
|
||||
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'){
|
||||
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')
|
||||
{
|
||||
reset.onclick();
|
||||
}
|
||||
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;
|
||||
|
||||
if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft'
|
||||
||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS')
|
||||
{
|
||||
switch(e.code){
|
||||
case 'ArrowUp':
|
||||
facing = -2;
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
facing = 2;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
facing = -1;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
facing = 1;
|
||||
break;
|
||||
case 'KeyW':
|
||||
break;
|
||||
case 'KeyS':
|
||||
break;
|
||||
}
|
||||
running = 50;
|
||||
rebuild = true;
|
||||
}
|
||||
if(fullscreen && selected ){
|
||||
if(e.code =='KeyF'||e.code =='KeyB'){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
|
||||
switch(e.code){
|
||||
case 'KeyB':
|
||||
var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
case 'KeyF':
|
||||
var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
}
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], m);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
let squareMesh = new Float32Array([ -1,-1,1,0,0,0,1,-1, 1,1,0,0,0,1,-1, -1,-1,0,0,0,1, -1,1,-1,0 ,0,0,1]);
|
||||
let sphereMesh = createMesh(32, 32, uvToSphere);
|
||||
let tubeMesh = createMesh(32, 2, uvToTube,0,1);
|
||||
let diskMesh = createMesh(32, 2, uvToDisk,0,1);
|
||||
let tubeMesh2 = createMesh(32, 2, uvToTube,0,2);
|
||||
let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2);
|
||||
let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2);
|
||||
let tubeMesh3 = createMesh(32, 2, uvToTube,0,3);
|
||||
let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3);
|
||||
let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3);
|
||||
let diskNMesh = createMesh(32, 2, uvToDisk, -1,1);
|
||||
let diskPMesh = createMesh(32, 2, uvToDisk, 1,1);
|
||||
let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh);
|
||||
let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2);
|
||||
let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3);
|
||||
let torusMash = createMesh(32, 32, uvToTorus, 1, 5);
|
||||
let head = createCube(1.5,1,1, 4);
|
||||
|
||||
let objects = [];
|
||||
let addObject = (obj, mat) => {
|
||||
objects.push([obj, mat]);
|
||||
};
|
||||
let clearObject = () => {delete objects; objects = [];};
|
||||
|
||||
let delta_height = 0, delta_l = [0,0];
|
||||
class State{
|
||||
constructor() {
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
this.rh = this.lh = .5*pi;
|
||||
this.lf = this.rf = 0;
|
||||
}
|
||||
initialize(){
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
}
|
||||
next(){
|
||||
//return this.presentation();
|
||||
if(running <= 0)
|
||||
return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0}
|
||||
running --;
|
||||
const steps = 100;
|
||||
let dl = 0;
|
||||
if(this.progress >= steps/2)
|
||||
{
|
||||
this.progress = 0;
|
||||
this.leg = !this.leg;
|
||||
}
|
||||
let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi];
|
||||
for (let i = 0; i < 4; ++i) delta[i] /= steps;
|
||||
if(this.leg)
|
||||
{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[0];
|
||||
this.rh += delta[3];
|
||||
this.lf += delta[1];
|
||||
this.rf += delta[2];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[0];
|
||||
this.rh -= delta[3];
|
||||
this.lf -= delta[1];
|
||||
this.rf-= delta[2];
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[3];
|
||||
this.rh += delta[0];
|
||||
this.lf += delta[2];
|
||||
this.rf += delta[1];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[3];
|
||||
this.rh -= delta[0];
|
||||
this.lf -= delta[2];
|
||||
this.rf-= delta[1];
|
||||
}
|
||||
}
|
||||
let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6);
|
||||
this.progress++;
|
||||
return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps};
|
||||
}
|
||||
// presentation(){
|
||||
// return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0};
|
||||
// }
|
||||
};
|
||||
|
||||
let star1 = [], star = [];
|
||||
let sakura = [];
|
||||
let star2=[], newstar, star4;
|
||||
const curvex = matrix_multiply(hermiteMat, [-.3,.8,.6,.5]);
|
||||
const curvey = matrix_multiply(hermiteMat, [-.2,.5,.7,.2]);
|
||||
const curvez = matrix_multiply(hermiteMat, [-.5,.2,.3,.8]);
|
||||
let adt = [];
|
||||
|
||||
let build_objects = (state)=>{
|
||||
if(running === 0)
|
||||
rebuild = false;
|
||||
let {lh, lf, rh, rf, dh, dl} = state.next();
|
||||
delta_l[abs(facing)-1] += Math.sign(facing) * dl;
|
||||
delta_height = dh;
|
||||
clearObject();
|
||||
M.save();
|
||||
M.save();
|
||||
M.rotateX(pi/2);
|
||||
M.scale(0.5, 0.5, 1);
|
||||
addObject(cylinderMesh, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0,1,0);
|
||||
addObject(head, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.53*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.55*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.3, -1, 0.);
|
||||
M.rotateX(lh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(lf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.3, -1, 0.);
|
||||
M.rotateX(rh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(rf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.restore();
|
||||
if(running < 0)
|
||||
rebuild = false;
|
||||
};
|
||||
let build_splines = ()=>{
|
||||
star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = [];
|
||||
var n = 11, innerN = Math.ceil((paths[0].length*n)/paths[1].length);
|
||||
for(let j = 0; j < paths[0].length; ++j)
|
||||
{
|
||||
let xf = paths[0][j][0], yf = paths[0][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
star1.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
|
||||
for(let j = 13; j >=0; j--)
|
||||
{
|
||||
let xf = paths[1][j][0], yf = paths[1][j][1];
|
||||
for(var k = innerN-1; k >=0 ; --k){
|
||||
let t = k/(innerN-1);
|
||||
star2.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
for(let j = paths[1].length-1; j >12; --j)
|
||||
{
|
||||
let xf = paths[1][j][0], yf = paths[1][j][1];
|
||||
for(var k = innerN; k >=0 ; --k){
|
||||
let t = k/innerN;
|
||||
star2.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
let concat = (a, b) => b.forEach(e => a.push(e));
|
||||
for(let i = 0; i < star1.length; ++i){
|
||||
concat(star, star1[i]);
|
||||
concat(star, star2[i]);
|
||||
}
|
||||
n = 25;
|
||||
for(let l = 2; l < 6; ++l)
|
||||
{
|
||||
adt[l - 2] = [];
|
||||
for(let j = 0; j < paths[l].length; ++j)
|
||||
{
|
||||
let xf = paths[l][j][0], yf = paths[l][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
adt[l-2].push(10+l,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
-dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
n = 20;
|
||||
for(let l = 6; l < 13; ++l)
|
||||
{
|
||||
sakura[l-6] = [];
|
||||
for(let j = 0; j < paths[l].length; ++j)
|
||||
{
|
||||
let xf = paths[l][j][0], yf = paths[l][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
sakura[l-6].push(10,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
star4 = star.slice();
|
||||
newstar = star.slice()
|
||||
for(let i = 0; i < star.length; i+=7)
|
||||
{
|
||||
star4[i] = 9;
|
||||
newstar[i] = 8;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let state = new State();
|
||||
var M = new Matrix();
|
||||
|
||||
function animate(gl) {
|
||||
let uTime;
|
||||
buildsplines &&= build_splines();
|
||||
if(animating)
|
||||
{
|
||||
uTime = (Date.now() - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
uTime = (lastTime - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
Sph[0] = [0,0.05*Math.cos(uTime + 1.),.045*Math.cos(uTime), 1,.15];
|
||||
Sph[1] = [0,0,0,1,.25];
|
||||
Sph[2] = [.22*Math.sin(uTime*1.2),0.05,.22*Math.cos(uTime*1.2),1,.05];
|
||||
Sph[3] = [.9*Math.sin(uTime*.4),0.,.9*Math.cos(uTime*.4),1,.25];
|
||||
Sph[4] = [0.5*Math.sin(uTime*1.),0.08*Math.sin(uTime *0.9),.5*Math.cos(uTime*1.),1,.12];
|
||||
|
||||
for(let i = 0; i < ns; ++ i)
|
||||
{
|
||||
let trsph = matrix_multiply(SphTr[i], Sph[i]);
|
||||
trsph[3] = Sph[i][4];
|
||||
setUniform('4fv', 'Sph['+ i + ']', trsph);
|
||||
}
|
||||
if(presentation){
|
||||
M.save();
|
||||
M.scale(0.3);
|
||||
M.rotateY(uTime/5);
|
||||
M.rotateX(1);
|
||||
overall_ground = M.value();
|
||||
if(facing !=2)
|
||||
M.rotateY(pi*(facing/2));
|
||||
M.translate(delta_l[0], -delta_height, delta_l[1]);
|
||||
|
||||
//M.translate(0, -delta_height, 0);
|
||||
overall_trans = M.value();
|
||||
M.restore();
|
||||
}else{
|
||||
M.save();
|
||||
// M.rotateY(2);
|
||||
M.translate(-.3, -.3, 0);
|
||||
M.rotateX(.6);
|
||||
M.rotateZ(.35);
|
||||
M.scale(0.3);
|
||||
overall_ground = M.value();
|
||||
|
||||
M.translate(delta_l[0], -delta_height, delta_l[1]);
|
||||
if(facing !=2)
|
||||
M.rotateY(pi*(facing/2));
|
||||
overall_trans = M.value();
|
||||
M.restore();
|
||||
}
|
||||
if(rebuild)
|
||||
build_objects(state);
|
||||
for(const [obj, mat] of objects){
|
||||
let m = matrix_multiply(sRotation,matrix_multiply(overall_trans, mat));
|
||||
setM(m);
|
||||
drawMesh(obj);
|
||||
}
|
||||
M.save();
|
||||
// M.rotateX(uTime/4);
|
||||
const _curvez = matrix_multiply(hermiteMat, [-.5,-.2,.3,.8]);
|
||||
// M.rotateZ(uTime/4);
|
||||
|
||||
M.rotateZ((uTime));
|
||||
//M.scale(100);
|
||||
setM(M.value());
|
||||
drawMesh(new Float32Array(star));
|
||||
M.restore();
|
||||
M.save();
|
||||
let t = (sin(uTime/1.5)), mat = [t*t*t, t*t, t, 1]
|
||||
if(t < 0)
|
||||
{ mat = const_multiply(-t, [1,1,1,1]);
|
||||
mat[3] +=1;
|
||||
}
|
||||
|
||||
M.translate(dot(curvex, mat)*2, dot(curvey, mat)*2, dot(curvez, mat)*2);
|
||||
M.scale(0.2);
|
||||
setM(M.value());
|
||||
drawMesh(new Float32Array(newstar));
|
||||
M.restore();
|
||||
const __curvey = matrix_multiply(hermiteMat, [-.3,.5,1.9,.8]);
|
||||
|
||||
M.save();
|
||||
M.translate(0,dot(__curvey, mat)*4, 0);
|
||||
t = abs(((uTime%4)/1.2)), mat = [t*t*t, t*t, t, 1];
|
||||
M.rotateZ(dot(__curvey, mat));
|
||||
M.scale(0.2);
|
||||
setM(M.value());
|
||||
drawMesh(new Float32Array(star4));
|
||||
M.restore();
|
||||
M.save();
|
||||
M.scale(0.1);
|
||||
M.translate(6,0,1);
|
||||
M.rotateX(1);
|
||||
M.rotateY(uTime/4);
|
||||
t = abs(sin((uTime%3.1415)/3.)), mat = [t*t*t, t*t, t, 1];
|
||||
|
||||
//M.scale(abs((dot(matrix_multiply(hermiteMat, [-.3,.9,1.7,2.8]), mat))));
|
||||
setM(M.value());
|
||||
drawMesh(torusMash,gl.LINES);
|
||||
M.restore();
|
||||
|
||||
M.save();
|
||||
M.translate(-.7,.75,0);
|
||||
//M.rotateZ(pi);
|
||||
M.scale(0.6)
|
||||
setM(M.value());
|
||||
|
||||
for(let l = 2; l < 6; ++l)
|
||||
drawMesh(new Float32Array(adt[l-2]),gl.LINE_LOOP);
|
||||
M.restore();
|
||||
|
||||
M.save();
|
||||
// M.scale(sin(uTime/2));
|
||||
M.translate(-.23, .7, 0);
|
||||
|
||||
M.rotateZ(sin(uTime/6) + 0.2);
|
||||
M.scale(0.4);
|
||||
for(let i = 0; i < 10; i++){
|
||||
M.scale(0.995);
|
||||
M.save();
|
||||
setM(M.value());
|
||||
M.restore();
|
||||
for(let l = 8; l < 13; ++l)
|
||||
drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP);
|
||||
}
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.2, .7, 0);
|
||||
|
||||
M.rotateZ(sin(uTime/6) + 0.2);
|
||||
M.scale(0.4);
|
||||
for(let i = 0; i < 10; i++){
|
||||
M.scale(0.995);
|
||||
M.save();
|
||||
setM(M.value());
|
||||
M.restore();
|
||||
for(let l = 8; l < 13; ++l)
|
||||
drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP);
|
||||
}
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(.7, .7, 0);
|
||||
M.rotateZ(sin(uTime/6) + 0.2);
|
||||
M.scale(0.6);
|
||||
for(let i = 0; i < 10; i++){
|
||||
M.scale(0.995);
|
||||
M.save();
|
||||
setM(M.value());
|
||||
M.restore();
|
||||
for(let l = 8; l < 13; ++l)
|
||||
drawMesh(new Float32Array(sakura[l-8]),gl.LINE_LOOP);
|
||||
}
|
||||
M.restore()
|
||||
M.save();
|
||||
M.translate(0,-3.155,0);
|
||||
|
||||
M.rotateX(pi/2);
|
||||
M.scale(10);
|
||||
|
||||
setM(matrix_multiply(sRotation,matrix_multiply(overall_ground,M.value())));
|
||||
|
||||
drawMesh(squareMesh);
|
||||
M.restore();
|
||||
if(positionsupdated)
|
||||
updatePositions();
|
||||
|
||||
// setM(M.value());
|
||||
}
|
||||
|
||||
requestAnimationFrame(fpscounter);
|
||||
//pjsk.play();
|
@ -0,0 +1,454 @@
|
||||
//Header file, contains global variable definitions,
|
||||
// asynchronized shader loading and utility functions
|
||||
|
||||
var mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let seldx = 0, seldy = 0, seldz = 0;
|
||||
var enableSelection = false;
|
||||
var cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
var mouselastX, mouselastY;
|
||||
var fl = 3;
|
||||
let start;
|
||||
var vs, fs;
|
||||
var bezierMat = [-1,3,-3,1,3,-6,3,0,-3,3,0,0,1,0,0,0],
|
||||
hermiteMat = [2,-3,0,1,-2,3,0,0,1,-2,1,0,1,-1,0,0];
|
||||
var starColors = [0.9921, 0.5378,0.7109,
|
||||
0.65, 0.56, 0.992,
|
||||
0.992,0.7994,0.2402,
|
||||
0.1760,0.5094,0.5378,
|
||||
.1164, .1274, .2289,
|
||||
.9784,.71,.4482,
|
||||
], n_shapes = starColors.length/3;
|
||||
var editor = undefined
|
||||
var cos = Math.cos, sin = Math.sin, tan = Math.tan,
|
||||
acos = Math.acos, asin = Math.asin, atan = Math.atan,
|
||||
sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs;
|
||||
var positionsupdated = true;
|
||||
var paths = [], origpath= [];
|
||||
let vsfetch = new XMLHttpRequest();
|
||||
vsfetch.open('GET', './shader.vert');
|
||||
vsfetch.onloadend = function () {
|
||||
vs = vsfetch.responseText;
|
||||
};
|
||||
vsfetch.send();
|
||||
//* LOADING FRAGMENT SHADER
|
||||
let fsfetch = new XMLHttpRequest();
|
||||
fsfetch.open('GET', './shader.frag');
|
||||
fsfetch.onloadend = function () {
|
||||
fs = (fsfetch.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
if (editor != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
};
|
||||
fsfetch.send();
|
||||
let pathFetch = new XMLHttpRequest();
|
||||
pathFetch.open('GET', './paths.txt');
|
||||
pathFetch.onloadend = function () {
|
||||
let text = pathFetch.responseText;
|
||||
let currX = 0, currY = 0, maxX = -1000, maxY = -1000, minX = 1000, minY = 1000;
|
||||
var currShape = [], currCurve = [];
|
||||
let i = 0;
|
||||
let postProcess = () => {
|
||||
if(currShape.length){
|
||||
let spanX = maxX - minX;
|
||||
let spanY = maxY - minY;
|
||||
let span = Math.max(spanX, spanY);
|
||||
for (var k = 0; k < currShape.length; ++k) {
|
||||
let funcs = [];
|
||||
const curve = currShape[k];
|
||||
for (let j = 0; j < curve.length; j += 2){
|
||||
curve[j] = (curve[j] - minX) / span-spanX/(span*2);
|
||||
curve[j + 1] = (curve[j + 1] - minY) / span - spanY/(span*2);
|
||||
origpath.push(1,curve[j], curve[j+1],0,0,0,1);
|
||||
if(j%6==0 && j > 5){
|
||||
let X = [], Y = [];
|
||||
for(let k = j - 6; k <= j+1; k += 2){
|
||||
X.push(curve[k]);
|
||||
Y.push(curve[k+1]);
|
||||
}
|
||||
|
||||
funcs.push([matrix_multiply(bezierMat, X),
|
||||
matrix_multiply(bezierMat, Y)]);
|
||||
}
|
||||
|
||||
}
|
||||
paths.push(funcs);
|
||||
}
|
||||
}
|
||||
}
|
||||
let read_num = () =>{
|
||||
let num = 0, sign = 1, accepted = 0;
|
||||
|
||||
while(i < text.length && (text[i] <'0' || text[i] > '9') && text[i]!='-') ++i;
|
||||
if(text[i] == '-')
|
||||
{
|
||||
sign = -1;
|
||||
++i;
|
||||
}
|
||||
while(i < text.length&&text[i] >= '0' && text[i] <= '9'){
|
||||
let n = text[i++] - '0';
|
||||
accepted *= 10;
|
||||
accepted += n;
|
||||
}
|
||||
|
||||
num += accepted;
|
||||
if(text[i] == '.'){
|
||||
i++;
|
||||
let multiplier = 0.1;
|
||||
accepted = 0;
|
||||
while(i < text.length&&text[i] >= '0' && text[i] <= '9'){
|
||||
let n = text[i++] - '0';
|
||||
accepted += n * multiplier;
|
||||
multiplier /= 10;
|
||||
}
|
||||
num += accepted;
|
||||
}
|
||||
return num * sign;
|
||||
}
|
||||
let cRevs = [], c_idx = 0, prevX = 0, prevY = 0, getC = ()=>{
|
||||
return cRevs[c_idx--];
|
||||
}
|
||||
let get_next = (delta = false) => {
|
||||
if(delta){
|
||||
currX = prevX + read_num();
|
||||
currY = prevY + read_num();
|
||||
}else{
|
||||
currX = read_num();
|
||||
currY = read_num();
|
||||
}
|
||||
maxX = currX > maxX? currX:maxX;
|
||||
maxY = currY > maxY? currY:maxY;
|
||||
minX = currX < minX? currX:minX;
|
||||
minY = currY < minY? currY:minY;
|
||||
|
||||
currCurve.push(currX);
|
||||
currCurve.push(currY);
|
||||
}
|
||||
while( i < text.length ){
|
||||
if(text[i] == 'z'){
|
||||
currCurve.length && currShape.push(currCurve);
|
||||
currCurve = [];
|
||||
++i
|
||||
} else if (text[i] == 'N'){
|
||||
postProcess();
|
||||
currShape = [];
|
||||
maxX = -1000, maxY = -1000, minX = 1000, minY = 1000;
|
||||
++i;
|
||||
} else if (text[i] == 'c'){
|
||||
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
|
||||
for(let j = 0; j < 3; ++j){
|
||||
get_next(true);
|
||||
}
|
||||
} else if (text[i] == 'C'){
|
||||
for(let j = 0; j < 3; ++j){
|
||||
get_next();
|
||||
}
|
||||
} else if (text[i] == 'M'){
|
||||
get_next();
|
||||
}
|
||||
else ++i;
|
||||
}
|
||||
};
|
||||
pathFetch.send();
|
||||
let matrix_inverse = src => {
|
||||
let dst = [], det = 0, cofactor = (c, r) => {
|
||||
let s = (i, j) => src[c+i & 3 | (r+j & 3) << 2];
|
||||
return (c+r & 1 ? -1 : 1) * ( (s(1,1) * (s(2,2) * s(3,3) - s(3,2) * s(2,3)))
|
||||
- (s(2,1) * (s(1,2) * s(3,3) - s(3,2) * s(1,3)))
|
||||
+ (s(3,1) * (s(1,2) * s(2,3) - s(2,2) * s(1,3))) );
|
||||
}
|
||||
for (let n = 0 ; n < 16 ; n++) dst.push(cofactor(n >> 2, n & 3));
|
||||
for (let n = 0 ; n < 4 ; n++) det += src[n] * dst[n << 2];
|
||||
for (let n = 0 ; n < 16 ; n++) dst[n] /= det;
|
||||
return dst;
|
||||
}
|
||||
|
||||
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||||
let matrix_identity = () => {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
}
|
||||
let matrix_translate = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[12] = x;
|
||||
m[13] = y;
|
||||
m[14] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_perspective = (m) => {
|
||||
let ret = []
|
||||
for (let i = 0; i < 16; i ++)
|
||||
ret[i] = m[i];
|
||||
for (let i = 2; i < 15; i += 4)
|
||||
{
|
||||
ret[i] = -ret[i];
|
||||
ret[i+1] += ret[i]/fl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||||
let matrix_rotateX = theta => {
|
||||
let m = matrix_identity();
|
||||
m[5] = cos(theta);
|
||||
m[6] = sin(theta);
|
||||
m[9] = -sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
|
||||
let matrix_rotateY = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[2] = -sin(theta);
|
||||
m[8] = sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_rotateZ= theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[1] = sin(theta);
|
||||
m[4] = -sin(theta);
|
||||
m[5] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_scale = (x, y, z) => {
|
||||
if (y === undefined)
|
||||
y = z = x;
|
||||
let m = matrix_identity();
|
||||
m[0] = x;
|
||||
m[5] = y;
|
||||
m[10] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||||
let res = [];
|
||||
if (b.length < m*n) { //mat-vec multiply (i did this for my convenience)
|
||||
for (let i = 0; i < m; ++i) {
|
||||
res[i] = 0;
|
||||
for (let j = 0; j < n; ++j)
|
||||
res[i] += b[j] * a[m * j + i];
|
||||
}
|
||||
return res;
|
||||
} //otherwise mm multiply
|
||||
for (let i = 0; i < m; ++i)
|
||||
for (let j = 0; j < m; ++j) {
|
||||
var t = 0;
|
||||
for (let k = 0; k < n; ++k)
|
||||
t += a[k * m + j] * b[i * n + k];
|
||||
res.push(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
let const_multiply = (c, a) => {
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++ i)
|
||||
m[i] = a[i] * c;
|
||||
return m;
|
||||
}
|
||||
function dot(a, b){
|
||||
let m = 0;
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m += a[i] * b[i];
|
||||
return m;
|
||||
}
|
||||
function plus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] + b[i];
|
||||
return m;
|
||||
}
|
||||
function minus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] - b[i];
|
||||
return m;
|
||||
}
|
||||
function normalize(v){
|
||||
let res = [];
|
||||
sum = 0;
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
sum += v[i] * v[i];
|
||||
sum = sqrt(sum);
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
res[i] = v[i] / sum;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
let Matrix = function() {
|
||||
let top = 0, m = [ matrix_identity() ];
|
||||
this.identity = () => m[top] = matrix_identity();
|
||||
this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z));
|
||||
this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta));
|
||||
this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta));
|
||||
this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta));
|
||||
this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z));
|
||||
this.value = () => m[top];
|
||||
this.save = () => { m[top+1] = m[top].slice(); top++; }
|
||||
this.restore = () => --top;
|
||||
}
|
||||
|
||||
let setM = (m) => {
|
||||
let mm = matrix_perspective(m);
|
||||
setUniform('Matrix4fv', 'uMatrix', false, mm);
|
||||
setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(m));
|
||||
}
|
||||
//------ CREATING MESH SHAPES
|
||||
|
||||
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||||
|
||||
let createMesh = (nu, nv, f, data, oid = 0) => {
|
||||
let tmp = [];
|
||||
for (let v = 0 ; v < 1 ; v += 1/nv) {
|
||||
for (let u = 0 ; u <= 1 ; u += 1/nu) {
|
||||
tmp = tmp.concat(f(u,v,oid,data));
|
||||
tmp = tmp.concat(f(u,v+1/nv,oid,data));
|
||||
}
|
||||
tmp = tmp.concat(f(1,v,oid,data));
|
||||
tmp = tmp.concat(f(0,v+1/nv,oid,data));
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
|
||||
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||||
|
||||
let glueMeshes = (a, b) => {
|
||||
let c = [];
|
||||
for (let i = 0 ; i < a.length ; i++)
|
||||
c.push(a[i]); // a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(b[i]); // + first vertex of b
|
||||
for (let i = 0 ; i < b.length ; i++)
|
||||
c.push(b[i]); // + b
|
||||
return new Float32Array(c);
|
||||
}
|
||||
|
||||
|
||||
let uvToSphere = (u,v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = Math.PI * (v - .5);
|
||||
let x = Math.cos(theta) * Math.cos(phi);
|
||||
let y = Math.sin(theta) * Math.cos(phi);
|
||||
let z = Math.sin(phi);
|
||||
return [i, x,y,z].concat(normalize([x, y, z]));
|
||||
}
|
||||
|
||||
let uvToTube = (u,v,i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta);
|
||||
let y = Math.sin(theta);
|
||||
let z = 2 * v - 1;
|
||||
return [i,x,y,z].concat(normalize([x,y,0]));
|
||||
}
|
||||
|
||||
let uvToDisk = (u,v,i,dz) => {
|
||||
if (dz === undefined)
|
||||
dz = 0;
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta) * v;
|
||||
let y = Math.sin(theta) * v;
|
||||
let z = dz;
|
||||
return [i,x,y,z].concat([0,0,Math.sign(z)]);
|
||||
}
|
||||
let uvToTorus = (u,v,i,r) => {
|
||||
let theta = 2 * pi;
|
||||
let phi = theta * v;
|
||||
theta *= u;
|
||||
let x = 1 + r * cos(phi);
|
||||
let y = sin(theta)*x;
|
||||
x *=cos(theta);
|
||||
let z = r * sin(phi);
|
||||
let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi);
|
||||
tsx*=-ty;
|
||||
return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx]));
|
||||
}
|
||||
|
||||
let createCube = (w, h, l,id) => {
|
||||
let mesh = [];
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]);
|
||||
return new Float32Array(mesh);
|
||||
}
|
||||
|
||||
function updatePositions() {
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||||
positionsupdated = false;
|
||||
}
|
||||
function hitTest(pos){
|
||||
if(!enableSelection)
|
||||
return -1;
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3);
|
||||
|
||||
let W=normalize(minus(trPos, V));
|
||||
let tMin=10000.;
|
||||
let iMin = -1;
|
||||
for(let i=0;i<cns;i++){
|
||||
let Vp=minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||||
let B=dot(W,Vp);
|
||||
let C=dot(Vp,Vp)-Sph[i][4]*Sph[i][4];
|
||||
let D=B*B-C;
|
||||
if(D>0.){
|
||||
let t=-B-sqrt(D);
|
||||
if(t > 0.0 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
return iMin;
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
var ns = 0, cns = 0;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
var fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
console.log('glstart');
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = 'In Spring Break We Did Homeworks.';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
textures[i] = i;
|
||||
}
|
||||
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
|
||||
positionsupdated = true;
|
||||
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., .3,1.,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.,
|
||||
.0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for(let i = 0; i < ns; 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));
|
||||
}
|
||||
offset = 0;
|
||||
for(let i = 0; i < n_shapes; i++){
|
||||
setUniform('3fv', 'starColors['+i+']', starColors.slice(offset, offset += 3));
|
||||
}
|
||||
|
||||
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);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL);
|
||||
gl.clearDepth(-1);
|
||||
let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(oid);
|
||||
gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4);
|
||||
|
||||
let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(normal);
|
||||
gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
//gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if(gl)
|
||||
{
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
||||
//let VERTEX_SIZE = 3;
|
||||
|
||||
|
||||
let VERTEX_SIZE = 7;
|
||||
|
||||
|
||||
let drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => {
|
||||
gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW);
|
||||
gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE);
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76
|
||||
c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04
|
||||
c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65
|
||||
c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8
|
||||
c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11
|
||||
c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43
|
||||
c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83
|
||||
C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57
|
||||
c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04
|
||||
c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14
|
||||
c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54
|
||||
c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78
|
||||
c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1
|
||||
c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55
|
||||
c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09
|
||||
c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z
|
||||
N
|
||||
M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8
|
||||
c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69
|
||||
c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28
|
||||
c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1
|
||||
c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45
|
||||
c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73
|
||||
c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33
|
||||
c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48
|
||||
c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28
|
||||
c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78
|
||||
c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86
|
||||
c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4
|
||||
c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22
|
||||
C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87
|
||||
c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35
|
||||
c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81
|
||||
c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79
|
||||
c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12
|
||||
c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83
|
||||
c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57
|
||||
c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85
|
||||
c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74
|
||||
c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08
|
||||
c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59
|
||||
c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28
|
||||
c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22
|
||||
c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28
|
||||
c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56
|
||||
c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19
|
||||
c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83
|
||||
c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2
|
||||
c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27
|
||||
c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54
|
||||
c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55
|
||||
c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69
|
||||
c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23
|
||||
c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21
|
||||
C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49
|
||||
c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z
|
||||
N
|
||||
M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z
|
||||
N
|
||||
M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z
|
||||
N
|
||||
M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z
|
||||
N
|
@ -0,0 +1,39 @@
|
||||
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
uniform vec3 starColors[10];
|
||||
uniform vec3 V0;
|
||||
varying vec3 norm;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
void main(){
|
||||
vec3 color =foregroundColor.xyz;
|
||||
float sp = 0.4, df = 0.4, amb = 0.4, ex=5.;
|
||||
vec3 l = vec3(1,1,1);
|
||||
|
||||
if(id < 1.5) {color = vec3(0.,1.,0.2034);}
|
||||
else if (id < 2.5) color = vec3(1.,.16,.36);
|
||||
else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;}
|
||||
else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;}
|
||||
else if (id < 6.5) {}
|
||||
else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;}
|
||||
else if (id < 8.5) {color = starColors[1]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 14.5) {color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
if(id < 0.){
|
||||
vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.));
|
||||
// APPLY PROCEDURAL NOISE TEXTURE.
|
||||
float cloud = min(0.99, max(0., 1. * noise(1. * P)));
|
||||
color = (1.-cloud)*color + starColors[5] * cloud*3.;
|
||||
}
|
||||
vec3 V = V0;
|
||||
vec3 W=normalize(glpos-V);
|
||||
vec3 realLDir=normalize(LDir - glpos);
|
||||
color = color*(amb+ df*max(0.,dot(norm,realLDir)))
|
||||
+ sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l;
|
||||
gl_FragColor=vec4(sqrt(color), 1.);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
uniform mat4 uMatrix;
|
||||
uniform mat4 invMatrix;
|
||||
uniform mat3 transformation;
|
||||
attribute float oid;
|
||||
attribute vec3 aPos;
|
||||
attribute vec3 normal;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
varying vec3 norm;
|
||||
void main() {
|
||||
vec4 pos = uMatrix * vec4(aPos, 1.);
|
||||
gl_Position = pos ;
|
||||
glpos = pos.xyz;
|
||||
id = oid;
|
||||
norm = normalize(vec4(normal,0.)*invMatrix).xyz;
|
||||
}
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,105 @@
|
||||
<!--<video src="pjsk.mp4" id="pjsk" muted="muted" loop="true" style="position:fixed; left:0; top:0;max-width:100%;min-width:100%;min-height: 100%;z-index: -100;"></video>-->
|
||||
<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=lib7.header.js></script>
|
||||
<script src=lib7.js></script>
|
||||
<style>
|
||||
.ace_gutter-layer {
|
||||
/* original width is 48px */
|
||||
width: 25px !important;
|
||||
}
|
||||
|
||||
.ace_gutter-layer > * {
|
||||
/* 48 - 32 = 16 */
|
||||
margin-left: 0;
|
||||
}
|
||||
.ace_gutter-cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
.code{
|
||||
font-family: "monaco, menlo, ubuntu mono, consolas, source-code-pro" ;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!!-------- VERTEX SHADER: YOU PROBABLY DON'T WANT TO CHANGE THIS RIGHT NOW -------->
|
||||
|
||||
|
||||
|
||||
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
|
||||
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag!! LOADED IN lib2.js -------->
|
||||
|
||||
<font size=7 color=#909090>
|
||||
|
||||
Magician
|
||||
|
||||
<img id="rtx" style="float:right;" src="./RTXon.svg" type="image/svg+xml"
|
||||
alt="Turn Ray Tracing On/OFF" title="Turn Ray Tracing On/OFF" height=60px /img>
|
||||
<div id="fps" style="font-size:25;float:right;margin-right:18px;"></div>
|
||||
<TABLE cellspacing=0 cellpadding=0><TR>
|
||||
<td><font color=red size=5><div id=errorMessage></div></font></td>
|
||||
</TR><TR>
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td valign=top>
|
||||
<div id="ace" style="opacity:90%;width:800px;height:2200px;"></div>
|
||||
</td><td valign=top style="background-color:azure;opacity: 100%;">
|
||||
<body bgcolor=white text=black link=black alink=blue vlink=blue>
|
||||
<center>
|
||||
<!!--- SUPER SAMPLING THE W/H PARAMS FOR CANVAS ARE RENDER SIZE, IN THE CSS IS ACTUAL(DISPLAY) SIZE.--->
|
||||
<canvas id='canvas1' style=" background-color:#FFF7F8;opacity: 100%;overflow: hidden !important; width: 600px !important; height:600px !important;" width=1199 height=1199></canvas>
|
||||
</center>
|
||||
</body>
|
||||
<div id="controls">
|
||||
<input type="number" id="ins" style="display:none;margin-left:0px;font-size:24px;width:35px;height:45px" value="5" max="5" min = "1">
|
||||
<button id="bns" style="display:none;margin-left:0px;font-size:24px;width:105px;height:45px">Spheres</button>
|
||||
<input type="number" id="insamp" style="margin-left:2px;font-size:24px;width:60px;height:45px" value="2" max="10" min = "0.1" step="0.2">
|
||||
<button id="bnsamp" style="margin-left:0px;font-size:24px;width:190px;height:45px">Super Sampling</button>
|
||||
<button id="bnfs" style="margin-left:2px;font-size:24px;width:180px;height:45px">Fullscreen</button>
|
||||
<button id="clrsel" style="display:none;margin-left:0px;font-size:24px;width:180px;height:45px">Clear Selection</button>
|
||||
<button id="reset" style="margin-left:0px;font-size:24px;width:100px;height:45px">Reset</button>
|
||||
<button id="mov" style="margin-left:0px;font-size:24px;width:180px;height:45px">Move Lighting</button>
|
||||
<button id="pause" style="margin-left:0px;font-size:24px;width:100px;height:45px">Pause</button>
|
||||
<div style='font-size:25px;'>
|
||||
|
||||
<font color=#000000>
|
||||
|
||||
<i style="font-size:28px;">What's new: </i>
|
||||
<p style="font-size:24px;">
|
||||
I made a 3D character with Bezier splines and surface of revolution.<br>
|
||||
Because human body is more complicated than the shapes I've created so far,
|
||||
I first made up a simple and highly symmetric character and drew it on paper. Then I used the pen tool
|
||||
in Adobe Illustrator to trace some segments of the contour in my drawing that I can use to create
|
||||
the 3D mesh by rotating it. The selected paths are in the end of (see <a href="./paths.txt">path.txt</a>)<br>
|
||||
The character is supposed to look like the below image (or <a href="./schema.svg">schema.svg</a>).
|
||||
It would look much more realistic if I use another spline to make the radius change over u. But this would require
|
||||
a 3D model, which is hard for me to create.<br>
|
||||
<center><image id="schema" src='./schema.svg'></image></center>
|
||||
</p>
|
||||
<div id="howitworks">
|
||||
<br>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</TR></TABLE>
|
||||
|
||||
|
||||
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
|
||||
|
||||
<script src="lib7.ext.js"></script>
|
||||
|
||||
<script>
|
||||
setInterval(() => {
|
||||
if(window.vs != null && window.fs != null&& canvas1.setShaders === undefined)
|
||||
gl_start(canvas1, vs, fs);
|
||||
}, 200);
|
||||
</script>
|
@ -0,0 +1,783 @@
|
||||
let ctrl = false, alt = false, shift = false, fpson = true, moving = false, over = false;
|
||||
let lastClick = undefined;
|
||||
let animating = true;
|
||||
let flags = 0x0;
|
||||
var uTime = 0, startTime = Date.now();
|
||||
let lastTime = Date.now(), rotTime = 0;
|
||||
var lastFrameTime = 0;
|
||||
let oldDocument;
|
||||
let fullscreen = false, btntoggled = false;
|
||||
let movescene = true;
|
||||
let oldparents = {};
|
||||
var tr, div;
|
||||
let canvas_originalsize;
|
||||
let Sph = [];
|
||||
let SphTr = [];
|
||||
let SphDletaR = [];
|
||||
let selected = false, selection = -1, dragging = false;
|
||||
let overall_trans = matrix_identity(), overall_ground;
|
||||
let rebuild = true, presentation = true, sRotation = matrix_identity();
|
||||
let facing = 1, running = 0;
|
||||
let curr_mouse_pos = [-10, -10];
|
||||
schema.height=screen.height*.9;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
function ev_supersample(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);
|
||||
}
|
||||
function toggleFullscreen(element){
|
||||
if(fullscreen)
|
||||
{
|
||||
if (document.exitFullscreen)
|
||||
document.exitFullscreen();
|
||||
else if (document.webkitExitFullscreen)
|
||||
document.webkitExitFullscreen();
|
||||
else if (document.mozCancelFullScreen)
|
||||
document.mozCancelFullScreen();
|
||||
else if (document.msExitFullscreen)
|
||||
document.msExitFullscreen();
|
||||
fullscreen = false;
|
||||
bnfs.innerText = "Fullscreen";
|
||||
}
|
||||
else{
|
||||
if(element.requestFullscreen)
|
||||
element.requestFullscreen();
|
||||
else if (element.webkitRequestFullscreen)
|
||||
element.webkitRequestFullscreen();
|
||||
else if(element.msRequestFullscreen)
|
||||
element.msRequestFullscreen();
|
||||
fullscreen = true;
|
||||
bnfs.innerText = "Exit Fullscreen";
|
||||
}
|
||||
}
|
||||
bnfs.onclick = function(e){
|
||||
if(e === "no")
|
||||
;
|
||||
else
|
||||
btntoggled = true;
|
||||
if(fullscreen){
|
||||
oldparents[controls].appendChild(controls);
|
||||
oldparents[canvas1].appendChild(canvas1);
|
||||
canvas1.style.width = canvas_originalsize[0];
|
||||
canvas1.style.height = canvas_originalsize[1];
|
||||
howitworks.hidden = false;
|
||||
}else{
|
||||
div = document.createElement("div");
|
||||
tr = document.createElement("table").insertRow();
|
||||
tr.style.backgroundColor="white";
|
||||
let size = Math.min(screen.availHeight, screen.availWidth);
|
||||
canvas_originalsize = [canvas1.style.width, canvas1.style.height, canvas1.width, canvas1.height];
|
||||
canvas1.style.height = canvas1.style.width = size;
|
||||
howitworks.hidden=true;
|
||||
oldparents[controls] = controls.parentNode;
|
||||
oldparents[canvas1] = canvas1.parentNode;
|
||||
|
||||
let td1 = tr.insertCell();
|
||||
td1.appendChild(canvas1);
|
||||
let td2;
|
||||
td2 = tr.insertCell();
|
||||
td2.style.verticalAlign="top";
|
||||
td2.appendChild(controls);
|
||||
|
||||
div.appendChild(tr);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
toggleFullscreen(div);
|
||||
ev_supersample();
|
||||
}
|
||||
mov.onclick=function(_){
|
||||
movescene = !movescene;
|
||||
if(!movescene)
|
||||
{
|
||||
mov.innerText= "Move Scene";
|
||||
mov.style.width = "170px";
|
||||
}
|
||||
else
|
||||
{
|
||||
mov.innerText = "Move Lighting";
|
||||
mov.style.width = "180px";
|
||||
}
|
||||
}
|
||||
document.addEventListener("webkitfullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
document.addEventListener("fullscreenchange", ()=>{if(!btntoggled && fullscreen)bnfs.onclick("no");btntoggled = false;});
|
||||
clrsel.onclick=function(_){
|
||||
setUniform("1i", "sel", -1);
|
||||
selected = false;
|
||||
selection = -1;
|
||||
}
|
||||
reset.onclick = function(_){
|
||||
clrsel.onclick();
|
||||
if(!animating)
|
||||
pause_resume();
|
||||
flags = 0;
|
||||
moving = false;
|
||||
mousedx = mousedy = mousedz = 0;
|
||||
positionsupdated = true;
|
||||
for(let i = 0; i < ns; ++i)
|
||||
{
|
||||
SphTr[i]=matrix_identity();
|
||||
SphDletaR[i] = 0;
|
||||
}
|
||||
rtx.src='./RTXon.svg';
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
bns.onclick=function(e){
|
||||
if(ins.value>0 &&ins.value<=ns &&cns!=ins.value)
|
||||
{
|
||||
cns = ins.value;
|
||||
fragmentShaderDefs = '\n const int cns = ' + cns + ';';
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
}
|
||||
}
|
||||
bnsamp.onclick=ev_supersample;
|
||||
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
|
||||
ace.require("ace/ext/language_tools");
|
||||
var editor = ace.edit("ace", {
|
||||
mode:"ace/mode/glsl",
|
||||
theme:"ace/theme/crimson_editor"
|
||||
});
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "monaco, menlo, ubuntu mono, consolas, source-code-pro",
|
||||
fixedWidthGutter: true,
|
||||
showGutter: true,
|
||||
showPrintMargin: false,
|
||||
});
|
||||
editor.setAutoScrollEditorIntoView(true);
|
||||
if(fs != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
editor.session.on('change', function(delta) {
|
||||
if(typeof canvas1.setShaders === "function")
|
||||
{
|
||||
canvas1.setShaders(vs, editor.getSession().getValue());
|
||||
setUniform('1i', 'flags', flags);
|
||||
}
|
||||
});
|
||||
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
|
||||
delete editor.KeyBinding;
|
||||
|
||||
|
||||
let pause_resume = function(){
|
||||
if(animating)
|
||||
lastTime = Date.now();
|
||||
else
|
||||
startTime += Date.now() - lastTime;
|
||||
animating = !animating;
|
||||
};
|
||||
canvas1.addEventListener('click',function(ev){
|
||||
if(!(shift && alt) && lastClick&& Date.now()-lastClick<200)
|
||||
pause_resume();
|
||||
lastClick = Date.now();
|
||||
});
|
||||
pause.onclick = pause_resume;
|
||||
canvas1.addEventListener('mouseover', function(e){
|
||||
over = true;
|
||||
if(e.offsetX >0 && e.offsetY > 0)
|
||||
curr_mouse_pos = [2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1];
|
||||
else over = false;
|
||||
});
|
||||
canvas1.addEventListener('mousedown', function(e){
|
||||
moving = true;
|
||||
rotTime = uTime;
|
||||
presentation = false;
|
||||
mouselastX = mouselastY = undefined;
|
||||
let i = hitTest([2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1]);
|
||||
if(i >= 0)
|
||||
{
|
||||
dragging = true;
|
||||
selected = true;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
else if(selected = true){
|
||||
dragging = false;
|
||||
selected = false;
|
||||
setUniform("1i", "sel", i);
|
||||
selection = i;
|
||||
}
|
||||
});
|
||||
canvas1.addEventListener('mousemove', function(e){
|
||||
curr_mouse_pos = [2*e.offsetX/ parseInt(canvas1.style.width)-1,
|
||||
1-2*e.offsetY/ parseInt(canvas1.style.height), -1];
|
||||
if(!(mouselastX==undefined || mouselastY == undefined)&&moving){
|
||||
let dx = (mouselastX - e.offsetX),
|
||||
dy = (mouselastY - e.offsetY);
|
||||
if(movescene){
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateY(-dy/60));
|
||||
sRotation = matrix_multiply(sRotation, matrix_rotateX(-dx/60));
|
||||
|
||||
}
|
||||
else if(!selected)
|
||||
{
|
||||
mousedx -= dx/60;
|
||||
mousedy -= dy/60;
|
||||
positionsupdated = true;
|
||||
}else if(dragging){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let dv = matrix_multiply(m, [2*-dx/ parseInt(canvas1.style.width),
|
||||
2*dy/ parseInt(canvas1.style.height), 0, 1]).slice(0,3);
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], matrix_translate(dv[0], dv[1], dv[2]));
|
||||
}
|
||||
|
||||
}
|
||||
mouselastX = e.offsetX;
|
||||
mouselastY = e.offsetY;
|
||||
});
|
||||
canvas1.addEventListener('mouseup', function(e){
|
||||
moving = false;
|
||||
dragging = false;
|
||||
});
|
||||
canvas1.addEventListener('mouseout', function(e){
|
||||
const mask = 0x8;
|
||||
flags &= !mask;
|
||||
setUniform('1i', 'flags', flags);
|
||||
over = false;
|
||||
moving = false;
|
||||
});
|
||||
canvas1.addEventListener('wheel', function(e){
|
||||
if(!selected){
|
||||
mousedz += e.wheelDelta/600;
|
||||
positionsupdated = true;
|
||||
}
|
||||
else{
|
||||
SphDletaR[selection] += e.wheelDelta / 800;
|
||||
}
|
||||
});
|
||||
canvas1.scroll(function(e) {e.stopPropagation();});
|
||||
rtx.style.cursor="pointer";
|
||||
let rtswitch = function(){
|
||||
alert('Ray Tracing is off for now. I\'ll try to add it back as some sort of background or texture later.')
|
||||
rtx.src='./RTXon.svg';
|
||||
}
|
||||
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;
|
||||
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'){
|
||||
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')
|
||||
{
|
||||
reset.onclick();
|
||||
}
|
||||
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;
|
||||
|
||||
if(e.code == 'ArrowUp' || e.code == 'ArrowDown' || e.code =='ArrowLeft'
|
||||
||e.code == 'ArrowRight' || e.code =='KeyW'||e.code =='KeyS')
|
||||
{
|
||||
switch(e.code){
|
||||
case 'ArrowUp':
|
||||
facing = -2;
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
facing = 2;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
facing = -1;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
facing = 1;
|
||||
break;
|
||||
case 'KeyW':
|
||||
break;
|
||||
case 'KeyS':
|
||||
break;
|
||||
}
|
||||
running = 50;
|
||||
rebuild = true;
|
||||
}
|
||||
if(fullscreen && selected ){
|
||||
if(e.code =='KeyF'||e.code =='KeyB'){
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
|
||||
switch(e.code){
|
||||
case 'KeyB':
|
||||
var dv = matrix_multiply(m, [0,0, -0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
case 'KeyF':
|
||||
var dv = matrix_multiply(m, [0,0, 0.1, 1]).slice(0,3);
|
||||
m = matrix_translate(dv[0], dv[1], dv[2]);
|
||||
break;
|
||||
}
|
||||
SphTr[selection] = matrix_multiply(SphTr[selection], m);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
let squareMesh = new Float32Array([ -1,-1,1,0,0,0,1,-1, 1,1,0,0,0,1,-1, -1,-1,0,0,0,1, -1,1,-1,0 ,0,0,1]);
|
||||
let sphereMesh = createMesh(32, 32, uvToSphere);
|
||||
let tubeMesh = createMesh(32, 2, uvToTube,0,1);
|
||||
let diskMesh = createMesh(32, 2, uvToDisk,0,1);
|
||||
let tubeMesh2 = createMesh(32, 2, uvToTube,0,2);
|
||||
let diskNMesh2 = createMesh(32, 2, uvToDisk, -1,2);
|
||||
let diskPMesh2 = createMesh(32, 2, uvToDisk, 1,2);
|
||||
let tubeMesh3 = createMesh(32, 2, uvToTube,0,3);
|
||||
let diskNMesh3 = createMesh(32, 2, uvToDisk, -1,3);
|
||||
let diskPMesh3 = createMesh(32, 2, uvToDisk, 1,3);
|
||||
let diskNMesh = createMesh(32, 2, uvToDisk, -1,1);
|
||||
let diskPMesh = createMesh(32, 2, uvToDisk, 1,1);
|
||||
let cylinderMesh = glueMeshes(glueMeshes(tubeMesh, diskPMesh), diskNMesh);
|
||||
let cylinderMesh2 = glueMeshes(glueMeshes(tubeMesh2, diskPMesh2), diskNMesh2);
|
||||
let cylinderMesh3 = glueMeshes(glueMeshes(tubeMesh3, diskPMesh3), diskNMesh3);
|
||||
let torusMash = createMesh(32, 32, uvToTorus, 1, 5);
|
||||
let head = createCube(1.5,1,1, 4);
|
||||
|
||||
let objects = [];
|
||||
let addObject = (obj, mat) => {
|
||||
objects.push([obj, mat]);
|
||||
};
|
||||
let clearObject = () => {delete objects; objects = [];};
|
||||
|
||||
let delta_height = 0, delta_l = [0,0];
|
||||
class State{
|
||||
constructor() {
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
this.rh = this.lh = .5*pi;
|
||||
this.lf = this.rf = 0;
|
||||
}
|
||||
initialize(){
|
||||
this.leg = true;
|
||||
this.progress = 0;
|
||||
}
|
||||
next(){
|
||||
//return this.presentation();
|
||||
if(running <= 0)
|
||||
return {rh:.5*pi, lh:.5*pi, rf:0, lf:0, dh:0,dl:0}
|
||||
running --;
|
||||
const steps = 100;
|
||||
let dl = 0;
|
||||
if(this.progress >= steps/2)
|
||||
{
|
||||
this.progress = 0;
|
||||
this.leg = !this.leg;
|
||||
}
|
||||
let delta = [-pi/5, 0.5*pi, 0.44*pi, 0.55*pi];
|
||||
for (let i = 0; i < 4; ++i) delta[i] /= steps;
|
||||
if(this.leg)
|
||||
{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[0];
|
||||
this.rh += delta[3];
|
||||
this.lf += delta[1];
|
||||
this.rf += delta[2];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[0];
|
||||
this.rh -= delta[3];
|
||||
this.lf -= delta[1];
|
||||
this.rf-= delta[2];
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(this.progress < steps/4)
|
||||
{
|
||||
this.lh += delta[3];
|
||||
this.rh += delta[0];
|
||||
this.lf += delta[2];
|
||||
this.rf += delta[1];
|
||||
}
|
||||
else{
|
||||
this.lh -= delta[3];
|
||||
this.rh -= delta[0];
|
||||
this.lf -= delta[2];
|
||||
this.rf-= delta[1];
|
||||
}
|
||||
}
|
||||
let delta_h = Math.max((1-cos(abs(this.lh - pi/2)))*.5+(1-cos(abs(this.lf)))*.6,(1-cos(abs(this.rh - pi/2)))*.5+(1-cos(abs(this.rf)))*.6);
|
||||
this.progress++;
|
||||
return {lh:this.lh, lf:this.lf, rh:this.rh,rf:this.rf, dh:delta_h, dl:1.8522/steps};
|
||||
}
|
||||
// presentation(){
|
||||
// return {lh:.4*pi, lf:pi/6,rh:.7*pi, rf:pi/8, dh:0};
|
||||
// }
|
||||
};
|
||||
|
||||
let star1 = [], star = [];
|
||||
let sakura = [], stars = [], nstars = 25;
|
||||
let star2=[], newstar, star4;
|
||||
const curvex = matrix_multiply(hermiteMat, [-.3,.8,.6,.5]);
|
||||
const curvey = matrix_multiply(hermiteMat, [-.2,.5,.7,.2]);
|
||||
const curvez = matrix_multiply(hermiteMat, [-.5,.2,.3,.8]);
|
||||
let adt = [];
|
||||
|
||||
let build_objects = (state)=>{
|
||||
if(running === 0)
|
||||
rebuild = false;
|
||||
let {lh, lf, rh, rf, dh, dl} = state.next();
|
||||
delta_l[abs(facing)-1] += Math.sign(facing) * dl;
|
||||
delta_height = dh;
|
||||
clearObject();
|
||||
M.save();
|
||||
M.save();
|
||||
M.rotateX(pi/2);
|
||||
M.scale(0.5, 0.5, 1);
|
||||
addObject(cylinderMesh, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0,1,0);
|
||||
addObject(head, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.53*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.5, 0.2, 0.3);
|
||||
M.rotateX(pi/4);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.4);
|
||||
M.rotateX(-0.55*pi);
|
||||
M.scale(0.2, 0.2, 0.4);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh2, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0.3, -1, 0.);
|
||||
M.rotateX(lh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(lf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-0.3, -1, 0.);
|
||||
M.rotateX(rh);
|
||||
M.translate(0,0,.5);
|
||||
M.save();
|
||||
M.translate(0,0,.45);
|
||||
M.rotateX(rf);
|
||||
M.scale(0.2, 0.2, 0.6);
|
||||
M.translate(0,0,1);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.scale(0.2, 0.2, 0.5);
|
||||
addObject(cylinderMesh3, M.value());
|
||||
M.restore();
|
||||
M.restore();
|
||||
if(running < 0)
|
||||
rebuild = false;
|
||||
};
|
||||
|
||||
let build_splines = ()=>{
|
||||
star = [], star1 = [], star2 = [], star4 = [], newstar = [], adt = [], sakura = [];
|
||||
var n = 11, innerN = Math.ceil((paths[0].length*n)/paths[1].length);
|
||||
for(let j = 0; j < paths[0].length; ++j)
|
||||
{
|
||||
let xf = paths[0][j][0], yf = paths[0][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
star1.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
|
||||
for(let j = 13; j >=0; j--)
|
||||
{
|
||||
let xf = paths[1][j][0], yf = paths[1][j][1];
|
||||
for(var k = innerN-1; k >=0 ; --k){
|
||||
let t = k/(innerN-1);
|
||||
star2.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
for(let j = paths[1].length-1; j >12; --j)
|
||||
{
|
||||
let xf = paths[1][j][0], yf = paths[1][j][1];
|
||||
for(var k = innerN; k >=0 ; --k){
|
||||
let t = k/innerN;
|
||||
star2.push([7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1]);
|
||||
}
|
||||
}
|
||||
for(let i = 0; i < star1.length; ++i){
|
||||
concat(star, star1[i]);
|
||||
concat(star, star2[i]);
|
||||
}
|
||||
n = 25;
|
||||
for(let l = 2; l < 6; ++l)
|
||||
{
|
||||
adt[l - 2] = [];
|
||||
for(let j = 0; j < paths[l].length; ++j)
|
||||
{
|
||||
let xf = paths[l][j][0], yf = paths[l][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
adt[l-2].push(10+l,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
-dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
n = 20;
|
||||
for(let l = 6; l < 13; ++l)
|
||||
{
|
||||
sakura[l-6] = [];
|
||||
for(let j = 0; j < paths[l].length; ++j)
|
||||
{
|
||||
let xf = paths[l][j][0], yf = paths[l][j][1];
|
||||
for(var k = 0; k <= n; ++k){
|
||||
let t = k/n;
|
||||
sakura[l-6].push(10,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
dot(yf, [t*t*t, t*t, t, 1]),
|
||||
0,0,0,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
star4 = star.slice();
|
||||
newstar = star.slice()
|
||||
for(let i = 0; i < star.length; i+=7)
|
||||
{
|
||||
star4[i] = 9;
|
||||
newstar[i] = 8;
|
||||
}
|
||||
for(let i = 0; i < nstars; ++i){
|
||||
stars.push(star.slice());
|
||||
startz.push(.6*Math.random());
|
||||
random_rot.push(0);
|
||||
random_rot_pos.push(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let state = new State();
|
||||
var M = new Matrix();
|
||||
var buildsplines = true;
|
||||
let magician = false, magician_neck = false,
|
||||
magician_houki = false, body = false,
|
||||
leg = false, hand = false ;
|
||||
let sa2 = [
|
||||
1.6,0.4, .9, 1., .3,.2, -.4,.8,
|
||||
-.8, -.1, -1.4, .5, -1.6, -.5
|
||||
], fsa2 = [[matrix_multiply(bezierMat,[sa2[0], sa2[2], sa2[4], sa2[6]]),
|
||||
matrix_multiply(bezierMat,[sa2[1], sa2[3], sa2[5], sa2[7]])],
|
||||
[matrix_multiply(bezierMat,[sa2[6], sa2[8], sa2[10], sa2[12]]),
|
||||
matrix_multiply(bezierMat,[sa2[7], sa2[9], sa2[11], sa2[13]])]],
|
||||
random_rot = [0,0,0,0,0,0,0], random_rot_pos = [0,0,0,0,0,0,0], startz = [];
|
||||
|
||||
let division = -1;
|
||||
let changeID = (id, obj) => {for(let i = 0; i < obj.length; i+=7) obj[i] = id;}
|
||||
function animate(gl) {
|
||||
buildsplines &&= build_splines();
|
||||
magician = magician?magician:createMeshFromSpline( 13, 32, 4, 4, 0, (i,_,oid) => {
|
||||
if (oid == 4 && i == 10) return 3;
|
||||
else if (oid == 3 && i ==16) return 2;
|
||||
else if(oid == 2 && i == 23) return 1;
|
||||
});
|
||||
magician_neck = magician_neck?magician_neck:createMeshFromSpline( 14, 32, 4, 5, -.2);
|
||||
magician_houki = magician_houki?magician_houki:createMeshFromSpline( 15, 32, 4, 6);
|
||||
body = body?body:createMeshFromSpline( 16, 32, 4, 7, 0, (i, tmp)=>{if(division < 0 && i == 25) division = tmp.length;});
|
||||
leg = leg?leg:createMeshFromSpline(17, 32, 4, 8);
|
||||
hand = hand?hand:createMeshFromSpline(18, 32, 4, 9,.015);
|
||||
|
||||
if(animating)
|
||||
{
|
||||
uTime = (Date.now() - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
uTime = (lastTime - startTime) / 1000;
|
||||
setUniform('1f', 'uTime', uTime);
|
||||
}
|
||||
let curve = [];
|
||||
let n = 8;
|
||||
// for(let j = 0; j < paths[13].length; ++j)
|
||||
// {
|
||||
// let xf = paths[13][j][0], yf = paths[13][j][1];
|
||||
// for(var k = 0; k <= n; ++k){
|
||||
// let t = k/n;
|
||||
// concat(curve, [7,dot(xf, [t*t*t, t*t, t, 1]),
|
||||
// -dot(yf, [t*t*t, t*t, t, 1]),
|
||||
// 0,0,0,1]);
|
||||
// }
|
||||
// }
|
||||
// M.save()
|
||||
// M.scale(0.3);
|
||||
// setM(matrix_multiply(sRotation, M.value()));
|
||||
// drawMesh(leg);
|
||||
// M.restore();
|
||||
// return;
|
||||
M.save()
|
||||
if(presentation){
|
||||
M.rotateX(uTime/12);
|
||||
M.rotateY(uTime/3);
|
||||
M.rotateZ(uTime/6);
|
||||
} else {
|
||||
M.rotateX(rotTime/12);
|
||||
M.rotateY(rotTime/3);
|
||||
M.rotateZ(rotTime/6);
|
||||
}
|
||||
M.scale(0.6);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(body.slice(0, division));
|
||||
drawMesh(body.slice(division - 7), gl.LINE_LOOP);
|
||||
M.save();
|
||||
M.translate(0,0,-1.05);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(magician);
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(0,0,-.53);
|
||||
M.scale(.1);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(magician_neck);
|
||||
M.restore();
|
||||
M.save();
|
||||
M.translate(-1,0,0);
|
||||
M.scale(2);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(magician_houki);
|
||||
M.restore();
|
||||
M.save()
|
||||
M.translate(-.06,0,.85);
|
||||
M.rotateY(0.02);
|
||||
M.scale(1.2);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(leg);
|
||||
M.restore();
|
||||
M.save()
|
||||
M.translate(.06,0,.85);
|
||||
M.rotateY(-0.02);
|
||||
M.scale(1.2);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(leg);
|
||||
M.restore();
|
||||
M.save()
|
||||
M.translate(-.35,0,-.1);
|
||||
M.rotateY(-pi/6);
|
||||
M.scale(.82);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(hand);
|
||||
M.restore();
|
||||
M.save()
|
||||
M.translate(.35,0,-.1);
|
||||
M.rotateY(pi/6);
|
||||
M.scale(.82);
|
||||
setM(matrix_multiply(sRotation, M.value()));
|
||||
drawMesh(hand);
|
||||
M.restore();
|
||||
M.restore();
|
||||
for(let i = 0; i < nstars; ++ i)
|
||||
{M.save()
|
||||
let f_idx = (uTime/(abs(sin(i*1234))*8 + 10))%2,
|
||||
t = f_idx - Math.floor(f_idx);
|
||||
f_idx -=t;
|
||||
let curr_mat = [t*t*t, t*t, t, 1];
|
||||
M.translate(1.2*sin(sin(uTime/(i+2)+i + 8)*.9 + dot(fsa2[f_idx][0], curr_mat)*5 + startz[Math.floor(2*startz[i]*nstars)%nstars]), 1.2*cos(i + sin(uTime/(i/2+18)+12)*.1 + startz[nstars-i-1] + dot(fsa2[f_idx][1], curr_mat)*startz[(nstars + i)%nstars]*.4 + i * sin(random_rot_pos[i]/30)/pi), .65*startz[i] + .3*sin(random_rot_pos[i]/20));
|
||||
let epsilon = 1/(i*2+20);
|
||||
let random = () => {
|
||||
var u = 0, v = 0;
|
||||
while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
|
||||
while(v === 0) v = Math.random();
|
||||
return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v )/pi;
|
||||
}
|
||||
if((window.performance.now() + i*(2+abs(random()))) %69 <2){
|
||||
changeID(Math.ceil(Math.random()*20)-5,stars[i])
|
||||
}
|
||||
random_rot[i] += random()*epsilon;
|
||||
random_rot_pos[i] += .5 + abs(random())/(pi+i);
|
||||
//setM(M.value);
|
||||
M.rotateZ(sin(random_rot_pos[i]/10)*pi);
|
||||
M.rotateX(.1*sin(random_rot_pos[i]/(i+50))*pi);
|
||||
M.rotateY(.1*sin(random_rot_pos[i]/(i*2+50))*pi);
|
||||
|
||||
M.scale(0.2 + startz[i]*.2);
|
||||
setM(M.value());
|
||||
drawMesh(new Float32Array(stars[i]));
|
||||
M.restore();}
|
||||
|
||||
if(over);
|
||||
}
|
||||
|
||||
requestAnimationFrame(fpscounter);
|
||||
//pjsk.play();
|
@ -0,0 +1,537 @@
|
||||
//Header file, contains global variable definitions,
|
||||
// asynchronized shader loading and utility functions
|
||||
|
||||
var mousedx = 0, mousedy = 0, mousedz = 0;
|
||||
let seldx = 0, seldy = 0, seldz = 0;
|
||||
var enableSelection = false;
|
||||
var cx = 1, cy = 1, sx = 0, sy = 0;
|
||||
var mouselastX, mouselastY;
|
||||
var fl = 3;
|
||||
let start;
|
||||
var vs, fs;
|
||||
var bezierMat = [-1,3,-3,1,3,-6,3,0,-3,3,0,0,1,0,0,0],
|
||||
hermiteMat = [2,-3,0,1,-2,3,0,0,1,-2,1,0,1,-1,0,0],
|
||||
catmullRomMat = [ -.5,1,-.5,0, 1.5,-2.5,0,1, -1.5,2,.5,0, .5,-.5,0,0 ];
|
||||
var starColors = [0.9921, 0.5378,0.7109,
|
||||
0.65, 0.56, 0.992,
|
||||
0.992,0.7994,0.2402,
|
||||
0.1760,0.5094,0.5378,
|
||||
.1164, .1274, .2289,
|
||||
.9784,.71,.4482,
|
||||
], n_shapes = starColors.length/3;
|
||||
var editor = undefined
|
||||
var cos = Math.cos, sin = Math.sin, tan = Math.tan,
|
||||
acos = Math.acos, asin = Math.asin, atan = Math.atan,
|
||||
sqrt = Math.sqrt, pi = Math.PI, abs = Math.abs;
|
||||
var positionsupdated = true;
|
||||
var paths = [], origpath= [], path_misc = [];
|
||||
let vsfetch = new XMLHttpRequest();
|
||||
vsfetch.open('GET', './shader.vert');
|
||||
vsfetch.onloadend = function () {
|
||||
vs = vsfetch.responseText;
|
||||
};
|
||||
vsfetch.send();
|
||||
//* LOADING FRAGMENT SHADER
|
||||
let fsfetch = new XMLHttpRequest();
|
||||
fsfetch.open('GET', './shader.frag');
|
||||
fsfetch.onloadend = function () {
|
||||
fs = (fsfetch.responseText);
|
||||
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
|
||||
if (editor != undefined)
|
||||
editor.getSession().setValue(fs);
|
||||
};
|
||||
fsfetch.send();
|
||||
let pathFetch = new XMLHttpRequest();
|
||||
pathFetch.open('GET', './paths.txt');
|
||||
pathFetch.onloadend = function () {
|
||||
let text = pathFetch.responseText;
|
||||
let currX = 0, currY = 0, maxX = -10000, maxY = -10000, minX = 10000, minY = 10000;
|
||||
var currShape = [], currCurve = [];
|
||||
let i = 0;
|
||||
let postProcess = () => {
|
||||
if(currShape.length){
|
||||
let spanX = maxX - minX;
|
||||
let spanY = maxY - minY;
|
||||
let span = Math.max(spanX, spanY);
|
||||
let l_total = 0;
|
||||
for (var k = 0; k < currShape.length; ++k) {
|
||||
let funcs = [];
|
||||
const curve = currShape[k];
|
||||
for (let j = 0; j < curve.length; j += 2){
|
||||
curve[j] = (curve[j] - minX) / span-spanX/(span*2);
|
||||
curve[j + 1] = (curve[j + 1] - minY) / span - spanY/(span*2);
|
||||
origpath.push(1,curve[j], curve[j+1],0,0,0,1);
|
||||
if(j%6==0 && j > 5){
|
||||
let X = [], Y = [];
|
||||
for(let k = j - 6; k <= j+1; k += 2){
|
||||
X.push(curve[k]);
|
||||
Y.push(curve[k+1]);
|
||||
}
|
||||
let l = (vec_len(minus([X[3], Y[3]], [X[0], Y[0]])) +
|
||||
vec_len(minus([X[3], Y[3]], [X[2], Y[2]])) +
|
||||
vec_len(minus([X[2], Y[2]], [X[1], Y[1]])) +
|
||||
vec_len(minus([X[1], Y[1]], [X[0], Y[0]])))/2.;
|
||||
l_total += l;
|
||||
funcs.push([matrix_multiply(bezierMat, X),
|
||||
matrix_multiply(bezierMat, Y), l]);
|
||||
}
|
||||
|
||||
}
|
||||
paths.push(funcs);
|
||||
path_misc.push([l_total, spanX/(2*span), spanY/(2*span)]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
let read_num = () =>{
|
||||
let num = 0, sign = 1, accepted = 0;
|
||||
|
||||
while(i < text.length && (text[i] <'0' || text[i] > '9') && text[i]!='-') ++i;
|
||||
if(text[i] == '-')
|
||||
{
|
||||
sign = -1;
|
||||
++i;
|
||||
}
|
||||
while(i < text.length&&text[i] >= '0' && text[i] <= '9'){
|
||||
let n = text[i++] - '0';
|
||||
accepted *= 10;
|
||||
accepted += n;
|
||||
}
|
||||
|
||||
num += accepted;
|
||||
if(text[i] == '.'){
|
||||
i++;
|
||||
let multiplier = 0.1;
|
||||
accepted = 0;
|
||||
while(i < text.length&&text[i] >= '0' && text[i] <= '9'){
|
||||
let n = text[i++] - '0';
|
||||
accepted += n * multiplier;
|
||||
multiplier /= 10;
|
||||
}
|
||||
num += accepted;
|
||||
}
|
||||
return num * sign;
|
||||
}
|
||||
let cRevs = [], c_idx = 0, prevX = 0, prevY = 0, getC = ()=>{
|
||||
return cRevs[c_idx--];
|
||||
}
|
||||
let get_next = (delta = false) => {
|
||||
if(delta){
|
||||
currX = prevX + read_num();
|
||||
currY = prevY + read_num();
|
||||
}else{
|
||||
currX = read_num();
|
||||
currY = read_num();
|
||||
}
|
||||
maxX = currX > maxX? currX:maxX;
|
||||
maxY = currY > maxY? currY:maxY;
|
||||
minX = currX < minX? currX:minX;
|
||||
minY = currY < minY? currY:minY;
|
||||
|
||||
currCurve.push(currX);
|
||||
currCurve.push(currY);
|
||||
}
|
||||
while( i < text.length ){
|
||||
if(text[i] == 'z'){
|
||||
currCurve.length && currShape.push(currCurve);
|
||||
currCurve = [];
|
||||
++i
|
||||
} else if (text[i] == 'N'){
|
||||
postProcess();
|
||||
currShape = [];
|
||||
maxX = -1000, maxY = -1000, minX = 1000, minY = 1000;
|
||||
++i;
|
||||
} else if (text[i] == 'c'){
|
||||
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
|
||||
for(let j = 0; j < 3; ++j){
|
||||
get_next(true);
|
||||
}
|
||||
} else if (text[i] == 'C'){
|
||||
for(let j = 0; j < 3; ++j){
|
||||
get_next();
|
||||
}
|
||||
} else if (text[i] == 'M'){
|
||||
get_next();
|
||||
}
|
||||
else ++i;
|
||||
}
|
||||
};
|
||||
pathFetch.send();
|
||||
let vec_len = v =>{
|
||||
let len = 0;
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
len += v[i] * v[i];
|
||||
return sqrt(len);
|
||||
}
|
||||
let matrix_inverse = src => {
|
||||
let dst = [], det = 0, cofactor = (c, r) => {
|
||||
let s = (i, j) => src[c+i & 3 | (r+j & 3) << 2];
|
||||
return (c+r & 1 ? -1 : 1) * ( (s(1,1) * (s(2,2) * s(3,3) - s(3,2) * s(2,3)))
|
||||
- (s(2,1) * (s(1,2) * s(3,3) - s(3,2) * s(1,3)))
|
||||
+ (s(3,1) * (s(1,2) * s(2,3) - s(2,2) * s(1,3))) );
|
||||
}
|
||||
for (let n = 0 ; n < 16 ; n++) dst.push(cofactor(n >> 2, n & 3));
|
||||
for (let n = 0 ; n < 4 ; n++) det += src[n] * dst[n << 2];
|
||||
for (let n = 0 ; n < 16 ; n++) dst[n] /= det;
|
||||
return dst;
|
||||
}
|
||||
|
||||
// I HAVE IMPLEMENTED THESE FUNCTIONS FOR YOU
|
||||
let matrix_identity = () => {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
}
|
||||
let matrix_translate = (x, y, z) => {
|
||||
let m = matrix_identity();
|
||||
m[12] = x;
|
||||
m[13] = y;
|
||||
m[14] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_perspective = (m) => {
|
||||
let ret = []
|
||||
for (let i = 0; i < 16; i ++)
|
||||
ret[i] = m[i];
|
||||
for (let i = 2; i < 15; i += 4)
|
||||
{
|
||||
ret[i] = -ret[i];
|
||||
ret[i+1] += ret[i]/fl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// YOU NEED TO PROPERLY IMPLEMENT THE FOLLOWING FIVE FUNCTIONS:
|
||||
let matrix_rotateX = theta => {
|
||||
let m = matrix_identity();
|
||||
m[5] = cos(theta);
|
||||
m[6] = sin(theta);
|
||||
m[9] = -sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
|
||||
let matrix_rotateY = theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[2] = -sin(theta);
|
||||
m[8] = sin(theta);
|
||||
m[10] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_rotateZ= theta => {
|
||||
let m = matrix_identity();
|
||||
m[0] = cos(theta);
|
||||
m[1] = sin(theta);
|
||||
m[4] = -sin(theta);
|
||||
m[5] = cos(theta);
|
||||
return m;
|
||||
}
|
||||
let matrix_scale = (x, y, z) => {
|
||||
if (y === undefined)
|
||||
y = z = x;
|
||||
let m = matrix_identity();
|
||||
m[0] = x;
|
||||
m[5] = y;
|
||||
m[10] = z;
|
||||
return m;
|
||||
}
|
||||
let matrix_multiply = (a, b, m = 4, n = 4) => { //dim=mn*nm=mm
|
||||
let res = [];
|
||||
if (b.length < m*n) { //mat-vec multiply (i did this for my convenience)
|
||||
for (let i = 0; i < m; ++i) {
|
||||
res[i] = 0;
|
||||
for (let j = 0; j < n; ++j)
|
||||
res[i] += b[j] * a[m * j + i];
|
||||
}
|
||||
return res;
|
||||
} //otherwise mm multiply
|
||||
for (let i = 0; i < m; ++i)
|
||||
for (let j = 0; j < m; ++j) {
|
||||
var t = 0;
|
||||
for (let k = 0; k < n; ++k)
|
||||
t += a[k * m + j] * b[i * n + k];
|
||||
res.push(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
let const_multiply = (c, a) => {
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++ i)
|
||||
m[i] = a[i] * c;
|
||||
return m;
|
||||
}
|
||||
function dot(a, b){
|
||||
let m = 0;
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m += a[i] * b[i];
|
||||
return m;
|
||||
}
|
||||
function plus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] + b[i];
|
||||
return m;
|
||||
}
|
||||
function minus(a, b){
|
||||
let m = [];
|
||||
for(let i = 0; i < a.length; ++i)
|
||||
m[i] = a[i] - b[i];
|
||||
return m;
|
||||
}
|
||||
function normalize(v){
|
||||
let res = [];
|
||||
sum = 0;
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
sum += v[i] * v[i];
|
||||
sum = sqrt(sum);
|
||||
for(let i = 0; i < v.length; ++ i)
|
||||
res[i] = v[i] / sum;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
let Matrix = function() {
|
||||
let top = 0, m = [ matrix_identity() ];
|
||||
this.identity = () => m[top] = matrix_identity();
|
||||
this.translate = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_translate(x,y,z));
|
||||
this.rotateX = theta => m[top] = matrix_multiply(m[top], matrix_rotateX(theta));
|
||||
this.rotateY = theta => m[top] = matrix_multiply(m[top], matrix_rotateY(theta));
|
||||
this.rotateZ = theta => m[top] = matrix_multiply(m[top], matrix_rotateZ(theta));
|
||||
this.scale = (x,y,z) => m[top] = matrix_multiply(m[top], matrix_scale(x,y,z));
|
||||
this.value = () => m[top];
|
||||
this.save = () => { m[top+1] = m[top].slice(); top++; }
|
||||
this.restore = () => --top;
|
||||
}
|
||||
|
||||
let setM = (m) => {
|
||||
let mm = matrix_perspective(m);
|
||||
setUniform('Matrix4fv', 'uMatrix', false, mm);
|
||||
setUniform('Matrix4fv', 'invMatrix', false, matrix_inverse(m));
|
||||
}
|
||||
//------ CREATING MESH SHAPES
|
||||
|
||||
// CREATE A MESH FROM A PARAMETRIC FUNCTION
|
||||
|
||||
let createMesh = (nu, nv, f, data, oid = 0) => {
|
||||
let tmp = [];
|
||||
for (let v = 0 ; v < 1 ; v += 1/nv) {
|
||||
for (let u = 0 ; u <= 1 ; u += 1/nu) {
|
||||
tmp = tmp.concat(f(u,v,oid,data));
|
||||
tmp = tmp.concat(f(u,v+1/nv,oid,data));
|
||||
}
|
||||
tmp = tmp.concat(f(1,v,oid,data));
|
||||
tmp = tmp.concat(f(0,v+1/nv,oid,data));
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
//Create a Mesh from Splines
|
||||
let createMeshFromSpline = ( idx,
|
||||
nu, nv, oid = 16, additional_offset = 0, f = ()=>{}) => {
|
||||
let S = paths[idx], meta = path_misc[idx];
|
||||
const n_min = 2;
|
||||
let ds = meta[0]/nv, curr_s = 0, curr_d = S[0][2]/Math.ceil(S[0][2]/ds), i = 0, s = 0;
|
||||
let tmp = [], ret = undefined;
|
||||
while (s < meta[0]-1/100000) {
|
||||
|
||||
for (let u = 0 ; u <= 1 ; u += 1/nu) {
|
||||
tmp = tmp.concat(getSurface(S[i][1],S[i][0],u,curr_s, idx, oid, additional_offset));
|
||||
tmp = tmp.concat(getSurface(S[i][1],S[i][0],u,curr_s+curr_d, idx, oid, additional_offset));
|
||||
}
|
||||
tmp = tmp.concat(getSurface(S[i][1],S[i][0],0,curr_s, idx, oid, additional_offset));
|
||||
tmp = tmp.concat(getSurface(S[i][1],S[i][0],1,curr_s+curr_d, idx, oid, additional_offset));
|
||||
if( ret = f(i, tmp, oid)){
|
||||
oid = ret;
|
||||
}
|
||||
curr_s += curr_d;
|
||||
if(curr_s >= 1)
|
||||
{
|
||||
s += S[i][2];
|
||||
++i;
|
||||
if(i >= S.length)
|
||||
break;
|
||||
let curr_n = Math.ceil(S[i][2]/ds);
|
||||
curr_n = curr_n < n_min ? n_min : curr_n;
|
||||
curr_d = 1/curr_n;
|
||||
curr_s = 0;
|
||||
}
|
||||
}
|
||||
return new Float32Array(tmp);
|
||||
}
|
||||
// GLUE TWO MESHES TOGETHER INTO A SINGLE MESH
|
||||
|
||||
let glueMeshes = (a, b) => {
|
||||
let c = [];
|
||||
for (let i = 0 ; i < a.length ; i++)
|
||||
c.push(a[i]); // a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(a[a.length - VERTEX_SIZE + i]); // + last vertex of a
|
||||
for (let i = 0 ; i < VERTEX_SIZE ; i++)
|
||||
c.push(b[i]); // + first vertex of b
|
||||
for (let i = 0 ; i < b.length ; i++)
|
||||
c.push(b[i]); // + b
|
||||
return new Float32Array(c);
|
||||
}
|
||||
|
||||
|
||||
let uvToSphere = (u,v, i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let phi = Math.PI * (v - .5);
|
||||
let x = Math.cos(theta) * Math.cos(phi);
|
||||
let y = Math.sin(theta) * Math.cos(phi);
|
||||
let z = Math.sin(phi);
|
||||
return [i, x,y,z].concat(normalize([x, y, z]));
|
||||
}
|
||||
|
||||
let uvToTube = (u,v,i) => {
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta);
|
||||
let y = Math.sin(theta);
|
||||
let z = 2 * v - 1;
|
||||
return [i,x,y,z].concat(normalize([x,y,0]));
|
||||
}
|
||||
|
||||
let uvToDisk = (u,v,i,dz) => {
|
||||
if (dz === undefined)
|
||||
dz = 0;
|
||||
let theta = 2 * Math.PI * u;
|
||||
let x = Math.cos(theta) * v;
|
||||
let y = Math.sin(theta) * v;
|
||||
let z = dz;
|
||||
return [i,x,y,z].concat([0,0,Math.sign(z)]);
|
||||
}
|
||||
let uvToTorus = (u,v,i,r) => {
|
||||
let theta = 2 * pi;
|
||||
let phi = theta * v;
|
||||
theta *= u;
|
||||
let x = 1 + r * cos(phi);
|
||||
let y = sin(theta)*x;
|
||||
x *=cos(theta);
|
||||
let z = r * sin(phi);
|
||||
let tx = -sin(theta), ty = cos(theta),tsx = sin(phi), tsy = tsx*tx, tsz = cos(phi);
|
||||
tsx*=-ty;
|
||||
return [i,x, y, z].concat(normalize([ty*tsz*0.5, -tx*tsz, tx*tsy-ty*tsx]));
|
||||
}
|
||||
|
||||
let createCube = (w, h, l,id) => {
|
||||
let mesh = [];
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,-1,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,0,1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,-1,0,0]);
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, -w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,0,-1]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,-l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,-h/2,l/2,1,0,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
|
||||
mesh=mesh.concat([id, w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, w/2,h/2,-l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,l/2,0,1,0]);
|
||||
mesh=mesh.concat([id, -w/2,h/2,-l/2,0,1,0]);
|
||||
return new Float32Array(mesh);
|
||||
}
|
||||
|
||||
function updatePositions() {
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
setUniform('3f', 'V0', m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz));
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
setUniform('Matrix3fv', 'transformation', false, [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]]);
|
||||
positionsupdated = false;
|
||||
}
|
||||
function hitTest(pos){
|
||||
if(!enableSelection)
|
||||
return -1;
|
||||
let m = matrix_rotateY(-mousedx);
|
||||
m = matrix_multiply(m, matrix_rotateX(-mousedy));
|
||||
let V = [m[8] * (fl + mousedz), m[9] * (fl + mousedz), m[10] * (fl + mousedz)];
|
||||
m = const_multiply((fl + 1 + mousedz)/(fl+1), m);
|
||||
let trPos = matrix_multiply([m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]], pos, 3,3);
|
||||
|
||||
let W=normalize(minus(trPos, V));
|
||||
let tMin=10000.;
|
||||
let iMin = -1;
|
||||
for(let i=0;i<cns;i++){
|
||||
let Vp=minus(V, matrix_multiply(SphTr[i], Sph[i]));
|
||||
let B=dot(W,Vp);
|
||||
let C=dot(Vp,Vp)-Sph[i][4]*Sph[i][4];
|
||||
let D=B*B-C;
|
||||
if(D>0.){
|
||||
let t=-B-sqrt(D);
|
||||
if(t > 0.0 && t < tMin){
|
||||
tMin = t; // This is an optimization, we don't have to do lighting/tex
|
||||
iMin = i; // for objects that are occuluded, which is expensive!
|
||||
}
|
||||
}
|
||||
}
|
||||
return iMin;
|
||||
}
|
||||
let matrix_transform = (m,p) => {
|
||||
let x = p[0], y = p[1], z = p[2], w = p[3] === undefined ? 1 : p[3];
|
||||
let q = [ m[0]*x + m[4]*y + m[ 8]*z + m[12]*w,
|
||||
m[1]*x + m[5]*y + m[ 9]*z + m[13]*w,
|
||||
m[2]*x + m[6]*y + m[10]*z + m[14]*w,
|
||||
m[3]*x + m[7]*y + m[11]*z + m[15]*w ];
|
||||
return p[3] === undefined ? [ q[0]/q[3],q[1]/q[3],q[2]/q[3] ] : q;
|
||||
}
|
||||
let evalSpline = (h,t) => {
|
||||
// t *= (h.length - 2) / 2;
|
||||
// let n = 2 * Math.floor(t);
|
||||
// t = t % 1;
|
||||
// let C = matrix_transform(type, [h[n+0],h[n+2],h[n+1],h[n+3]]);
|
||||
// return t*t*t*C[0] + t*t*C[1] + t*C[2] + C[3];
|
||||
return t*t*t*h[0] + t*t*h[1] + t*h[2] + h[3];
|
||||
}
|
||||
let getSurface = (S0, S1, u, v,offsetidx, id = 15, additional_offset = 0) => {
|
||||
const epsilon = .001;
|
||||
let z0 = evalSpline(S0, v),
|
||||
z1 = evalSpline(S0, v + epsilon),
|
||||
|
||||
r0 = evalSpline(S1, v)-path_misc[offsetidx][1] + additional_offset,
|
||||
r1 = evalSpline(S1, v + epsilon),
|
||||
|
||||
tilt = Math.atan2(r0 - r1, z1 - z0);
|
||||
|
||||
let xx = cos(2 * pi * u), yy = sin(2 * pi * u);
|
||||
let x = r0 *xx, // POSITION
|
||||
y = r0 *yy,
|
||||
z = z0,
|
||||
nx = cos(tilt), // NORMAL
|
||||
ny = nx*yy,
|
||||
nz = sin(tilt);
|
||||
nx*=xx;
|
||||
return [id, x,y,z, nx,ny,nz];
|
||||
}
|
||||
let concat = (a, b) => b.forEach(e => a.push(e));
|
@ -0,0 +1,216 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// THIS IS THE SUPPORT LIBRARY. YOU PROBABLY DON'T WANT TO CHANGE ANYTHING HERE JUST YET.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let fragmentShaderHeader = ['' // WHATEVER CODE WE WANT TO PREDEFINE FOR FRAGMENT SHADERS
|
||||
, 'precision highp float;'
|
||||
, 'float noise(vec3 point) { float r = 0.; for (int i=0;i<16;i++) {'
|
||||
, ' vec3 D, p = point + mod(vec3(i,i/4,i/8) , vec3(4.0,2.0,2.0)) +'
|
||||
, ' 1.7*sin(vec3(i,5*i,8*i)), C=floor(p), P=p-C-.5, A=abs(P);'
|
||||
, ' C += mod(C.x+C.y+C.z,2.) * step(max(A.yzx,A.zxy),A) * sign(P);'
|
||||
, ' D=34.*sin(987.*float(i)+876.*C+76.*C.yzx+765.*C.zxy);P=p-C-.5;'
|
||||
, ' r+=sin(6.3*dot(P,fract(D)-.5))*pow(max(0.,1.-2.*dot(P,P)),4.);'
|
||||
, '} return .5 * sin(r); }'
|
||||
].join('\n');
|
||||
var ns = 0, cns = 0;
|
||||
fragmentShaderHeader+= 'const int ns = ' + ns + ';\n';
|
||||
var fragmentShaderDefs = 'const int cns = ' + cns + ';\n';
|
||||
let nfsh = fragmentShaderHeader.split('\n').length + 1; // NUMBER OF LINES OF CODE IN fragmentShaderHeader
|
||||
|
||||
let isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
function getBlob(data) {
|
||||
let bytes = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
bytes[i] = data.charCodeAt(i);
|
||||
}
|
||||
return new Blob([new Uint8Array(bytes)]);
|
||||
}
|
||||
let texture = [], gl, program;
|
||||
let textures = [];
|
||||
let lock = false;
|
||||
|
||||
function loadTexture(gl, url, i) {
|
||||
const level = 0;
|
||||
const internalFormat = gl.RGBA;
|
||||
const width = 1;
|
||||
const height = 1;
|
||||
const border = 0;
|
||||
const srcFormat = gl.RGBA;
|
||||
const srcType = gl.UNSIGNED_BYTE;
|
||||
if (texture[i] == null)
|
||||
{
|
||||
texture[i] = gl.createTexture();
|
||||
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
width, height, border, srcFormat, srcType,
|
||||
pixel);
|
||||
}
|
||||
const image = new Image();
|
||||
image.onload = function () {
|
||||
gl.activeTexture(gl.TEXTURE0+i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
srcFormat, srcType, image);
|
||||
|
||||
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
}
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function isPowerOf2(value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
function gl_start(canvas, vertexShader, fragmentShader) { // START WEBGL RUNNING IN A CANVAS
|
||||
console.log('glstart');
|
||||
setTimeout(function () {
|
||||
try {
|
||||
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:
|
||||
|
||||
gl = this.gl;
|
||||
program = gl.createProgram(); // Create the WebGL program.
|
||||
|
||||
function addshader(type, src) { // Create and attach a WebGL shader.
|
||||
function spacer(color, width, height) {
|
||||
return '<table bgcolor=' + color +
|
||||
' width=' + width +
|
||||
' height=' + height + '><tr><td> </td></tr></table>';
|
||||
}
|
||||
errorMessage.innerHTML = 'There is no real magic, but Computer Graphics is a close one.';
|
||||
// errorMarker.innerHTML = spacer('white', 1, 1) + '<font size=1 color=white>\u25B6</font>';
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
let msg = gl.getShaderInfoLog(shader);
|
||||
console.log('Cannot compile shader:\n\n' + msg);
|
||||
|
||||
let a = msg.substring(6, msg.length);
|
||||
let line = 0;
|
||||
if (a.substring(0, 3) == ' 0:') {
|
||||
a = a.substring(3, a.length);
|
||||
line = parseInt(a) - nfsh;
|
||||
|
||||
editor.session.setAnnotations([{
|
||||
row: line,
|
||||
column: 0,
|
||||
text: msg,
|
||||
type: "error"
|
||||
}]);
|
||||
}
|
||||
let j = a.indexOf(':');
|
||||
a = 'line ' + (line+1) + a.substring(j, a.length);
|
||||
if ((j = a.indexOf('\n')) > 0)
|
||||
a = a.substring(0, j);
|
||||
errorMessage.innerHTML = a;
|
||||
}
|
||||
else
|
||||
editor.session.clearAnnotations();
|
||||
gl.attachShader(program, shader);
|
||||
};
|
||||
|
||||
addshader(gl.VERTEX_SHADER, vertexShader); // Add the vertex and fragment shaders.
|
||||
addshader(gl.FRAGMENT_SHADER, fragmentShaderHeader +fragmentShaderDefs+ fragmentShader);
|
||||
|
||||
gl.linkProgram(program); // Link the program, report any errors.
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
||||
console.log('Could not link the shader program!');
|
||||
gl.useProgram(program);
|
||||
gl.program = program;
|
||||
for(let i = 0; i < ns; ++i){
|
||||
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
|
||||
textures[i] = i;
|
||||
}
|
||||
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
|
||||
positionsupdated = true;
|
||||
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., .3,1.,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.,
|
||||
.0,.0,.0, .0,.0,.0, .0,.0,.0,40., 0.,.85,1.5
|
||||
]
|
||||
var offset = 0;
|
||||
for(let i = 0; i < ns; 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));
|
||||
}
|
||||
offset = 0;
|
||||
for(let i = 0; i < n_shapes; i++){
|
||||
setUniform('3fv', 'starColors['+i+']', starColors.slice(offset, offset += 3));
|
||||
}
|
||||
|
||||
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);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL);
|
||||
gl.clearDepth(-1);
|
||||
//gl.enable(gl.BLEND);
|
||||
//gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
let oid = gl.getAttribLocation(program, 'oid'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(oid);
|
||||
gl.vertexAttribPointer(oid, 1, gl.FLOAT, false, 4*7, 0);
|
||||
|
||||
let aPos = gl.getAttribLocation(program, 'aPos'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(aPos);
|
||||
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 4*7, 4);
|
||||
|
||||
let normal = gl.getAttribLocation(program, 'normal'); // Set aPos attribute for each vertex.
|
||||
gl.enableVertexAttribArray(normal);
|
||||
gl.vertexAttribPointer(normal, 3, gl.FLOAT, false, 4*7, 4*4);
|
||||
}
|
||||
|
||||
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
|
||||
setInterval(function () { // Start the animation loop.
|
||||
gl = canvas.gl;
|
||||
if (gl.startTime === undefined) // First time through,
|
||||
gl.startTime = Date.now(); // record the start time.
|
||||
animate(gl);
|
||||
//gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Render the square.
|
||||
}, 30);
|
||||
|
||||
}, 100); // Wait 100 milliseconds after page has loaded before starting WebGL.
|
||||
}
|
||||
|
||||
// THE animate() CALLBACK FUNCTION CAN BE REDEFINED IN index.html.
|
||||
|
||||
function animate() { }
|
||||
|
||||
function setUniform(type, name, a, b, c, d, e, f) {
|
||||
if(gl)
|
||||
{
|
||||
let loc = gl.getUniformLocation(gl.program, name);
|
||||
(gl['uniform' + type])(loc, a, b, c, d, e, f);
|
||||
}
|
||||
}
|
||||
|
||||
//let VERTEX_SIZE = 3;
|
||||
|
||||
|
||||
let VERTEX_SIZE = 7;
|
||||
|
||||
|
||||
let drawMesh = (mesh, func = gl.TRIANGLE_STRIP) => {
|
||||
gl.bufferData(gl.ARRAY_BUFFER, mesh, gl.STATIC_DRAW);
|
||||
gl.drawArrays(func, 0, mesh.length / VERTEX_SIZE);
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
M11.02,39.45c-2.67-2.97-5.37-5.93-8.01-8.94c-2.42-2.76-2.92-5.66-1.11-8.78c0.81-1.39,1.97-2.41,3.65-2.76c3.9-0.82,7.78-1.71,11.67-2.56c0.86-0.19,1.5-0.51,1.99-1.38c1.9-3.42,3.92-6.76,5.89-10.14c2.6-4.46,9.11-4.47,11.71-0.04
|
||||
c1.98,3.37,3.99,6.72,5.89,10.14c0.48,0.86,1.08,1.23,1.96,1.42c3.94,0.85,7.88,1.68,11.81,2.6c2.44,0.57,3.73,2.34,4.36,4.65c0.77,2.82-0.27,5.08-2.16,7.14c-2.52,2.74-4.97,5.53-7.44,8.32c-0.18,0.2-0.41,0.49-0.38,0.72c0.18,1.94,0.41,3.87,0.63,5.8
|
||||
c0.12,1.04,0.33,2.07,0.36,3.11c0.05,1.93,0.55,3.8,0.17,5.81c-0.65,3.42-4.81,6.08-8.02,4.95c-1.83-0.64-3.65-1.36-5.44-2.11
|
||||
c-2.24-0.94-4.48-1.89-6.67-2.95c-0.75-0.36-1.36-0.27-2.02,0.03c-2.75,1.22-5.47,2.48-8.24,3.66c-1.3,0.56-2.64,1.08-4.01,1.43
|
||||
c-2.45,0.63-4.53-0.26-6.14-2.08c-1.65-1.86-2.05-4.09-1.55-6.54c0.26-1.25,0.25-2.55,0.37-3.83
|
||||
C10.52,44.62,10.77,42.12,11.02,39.45z M56.81,25.75c0.02-1.65-1.04-2.64-2.79-3.01c-4.04-0.84-8.06-1.74-12.1-2.57
|
||||
c-0.91-0.19-1.58-0.65-2.04-1.42c-0.98-1.64-1.93-3.29-2.89-4.93c-1.32-2.26-2.6-4.55-3.98-6.77c-1.12-1.79-2.96-1.74-4.13,0.04
|
||||
c-0.65,0.99-1.23,2.04-1.83,3.07c-1.66,2.84-3.32,5.68-4.97,8.52c-0.48,0.83-1.17,1.32-2.14,1.5c-1.82,0.34-3.62,0.74-5.42,1.14
|
||||
c-2.45,0.54-4.92,1.04-7.34,1.68C5,23.57,4.46,25.5,5.92,27.15c1.64,1.85,3.31,3.69,4.95,5.54c1.33,1.5,2.68,2.97,3.92,4.54
|
||||
c0.38,0.48,0.61,1.21,0.61,1.82c0.01,1.33-0.17,2.65-0.29,3.98c-0.09,0.99-0.19,1.98-0.3,2.97c-0.26,2.26-0.58,4.51-0.77,6.78
|
||||
c-0.05,0.52,0.18,1.14,0.48,1.59c0.8,1.19,1.9,1.37,3.54,0.65c3.75-1.66,7.49-3.32,11.22-5.02c1.06-0.48,2.04-0.61,3.13-0.1
|
||||
c1.94,0.92,3.9,1.78,5.86,2.65c2.05,0.91,4.11,1.78,6.15,2.69c0.93,0.42,1.77,0.3,2.52-0.37c0.76-0.68,1.19-1.49,0.83-2.55
|
||||
c-0.08-0.22-0.12-0.46-0.14-0.7c-0.21-2.51-0.38-5.02-0.61-7.53c-0.12-1.32-0.38-2.63-0.51-3.95c-0.11-1.09-0.17-2.16,0.7-3.09
|
||||
c1.83-1.95,3.6-3.95,5.37-5.94c1.23-1.39,2.46-2.79,3.64-4.22C56.54,26.51,56.69,25.99,56.81,25.75z
|
||||
N
|
||||
M66.36,818.02c-106.2-110.9-61-662.8-36.4-813.56c71.19,1.55,58.77-12.26,72.22,57.92c12.21-15.09,10.15-62.97,37.19-55.8
|
||||
c-11.66,49.82-40.93,95.92-1.62,142.63c15.22,3.36,56.55-137.38,59.66-136.45c2.49,0.74-22.25,91.36-42.07,162.69
|
||||
c8.65,35.12,58.93,87.86,92.29,98.19c14.23-79.7,33.22-361.27,73.99-244.33c41.13-55.27-7.85,38.44-11.46,55.28
|
||||
c-18.92,98.99-27.77,241.63,53.45,119.52c20.21-34.31,20.1-114.01,53.68-126.53c3.84-12.82,10.48-73.87,24.02-73.1
|
||||
c12.83-0.56,65.91-1.83,35.15,7.41c-61.79-15.5-48.75,200.28-45.07,243.92c2.25,20.42,27.14,13.8,38.12,25.45
|
||||
c6.03,4.39,36.44,27.66,18.27,21.91c-40.8-27.63-66.71-54.35-46.2,16.36c-19.89-38.23,1.59-50.13-50.98-60.73
|
||||
c8.31-2.96,35.82,10.04,30.87-4.68c-5.47-26.59-6.9-54.75-14.96-80.46c-22.25,80.4-137.99,191.25-134.59,255.33
|
||||
c28.43-24.91,73.72-62.68,120.64-46.55c78.08,7.49,7.79-16.2-21.17-22.89c25.68-8.32,88.81,23.09,96.53,50.48
|
||||
c-14.88-13.87-8.97-8.07-4.72,6.57c-20.12-14.39-11.58-35.73-44.46-26.75c28.79,18.11,36.81,27.82,49.27,59.28
|
||||
c-15.78,0.19,5.06,35.44-28.02-9.3c-39.56-53.22-130.39-50.05-158.76,12.21c-0.57,107.56,60.34,213.63,68.06,323.78
|
||||
c1.71,10.93,25.65,17.02,33.27,28.59c-11.57-2.96-19.66-12.97-31.03-16.47c-1.22,193.89-65.94,75.43,75.87,233.86
|
||||
c-37.15-15.29-59.1-63.14-92.21-88.24c-15.18-7.04-28.22,43.71-46.26,43.68c57.38-81.24-1.81-123.16,10.19-218.4
|
||||
c8.56,76.16-24.52,144.42-59.52,208.52c-8.14,12.44,0.12,54.47-13.87,54.44c9.71-16.15,13.17-61.15-18.44-81.22
|
||||
C166.51,916.66,113.43,878.18,66.36,818.02z M925.87,407.64c-54.74-44.89,19.53-197.95-29.21-257.87
|
||||
c-7.8,0.23,0.01,139.34-12.78,138.06c-0.76-11.74,2.94-57.56-13.24-57.45c-12.13,43.49-18.05,56.87-20.76,56.35
|
||||
c-3.44-0.66-1.37-22.36-9.37-26.3c-1.4-0.69-3.39-0.47-6.27,1.08c-37.75,31.13-5.8-23.15-21.95-34.81
|
||||
c-10.77-4.3-7.19,51.51-13.81,37.09c-6.3-84.43-23.11-192.91-70.65-258.62c15.21,86.12,50.46,187.09,38.31,278.79
|
||||
c-21.94-86.63-52.72-175.52-70.62-266.49c2.29-9.8-18.19-21-13.94-4.4c30.28,57.1,21.26,334.09,14.6,185.12
|
||||
c-1.5-9.87,2.15-67.2-7.12-67.14c-2.9,30.13-5.79,60.27-8.69,90.4c-32.81-59.32-16.97-176.63-52.31-212.83
|
||||
c11.12,94.4-6.13,188.39-24.34,280.85c3.07-43.88,20.04-96.61,8.43-137.16c-6.3,14.9-20.93,66.96-26.59,61.57
|
||||
c-8.59-0.89-9.05-48.09-14.45-48.15c-3.39-0.04-6.04,18.34-7.95,31.07c-7.59,50.46-15.93,93.98-17.18,93.85
|
||||
c8.57-40.75,5.67-198.5-18.91-251.56c4.95,62.89-1.02,125.94-10.23,188.11c3.54-69.31,13.94-164.75-15.9-222.74
|
||||
c32.11,126.08-28.75,489.29,30.52,197.35c28.79,98.72-65.28,261.35,58.73,108.86c-37.19,187.32,36.13-32.28,47.76-103.08
|
||||
c8.96,94.67-2.01-42.55,6.66-79.88c30.75,97.92,53.06,199.4-5.06,292.92C737.6,336.4,694.28,5.48,752.2,275.59
|
||||
c12.58,35.83-30.59,24.88-39.09,50.66c61.29-24.55,38.95-35.44,52.83,53.39c0.4,3.52-0.24,7.19-4.7,7.28
|
||||
c-25.86,1.03-45.18,18.23-57.25,38.9c13.43-3.39,50.98-46.17,61.23-33.44c-21.97,29.52-78.97,40.99-72.67,90.22
|
||||
c3.55-15.72,78.25-89.59,75.21-54.58c-1.37,4.97-0.6,13.02-7.63,13.72c-61.1,2.32-80.25,119.34-40.63,125.28
|
||||
c-13.21-11.3-15.2-27.18-9.08-34.25c7.41-8.57,27.36-4.95,29.07,0.51c0.78,2.5-2.15,5.13-0.96,7.46c0.58,1.13,2.72,1.77,8.48,1.56
|
||||
c38.89-7.77,7.11,28.36-12.62,33.66c17.65,7.15,41.53-6.09,43.26-25.21c-13.61-10.4-3.01-29.64,12.23-22.19
|
||||
c39.17-39.2-23.35-78.46-17.83-98.65c2.53-4.97,11.94-9.86,47.83-3.67c17.2,5.95,27.38,81.41,34.69,33.83
|
||||
c26.79,65.65,1.08,95.95-22.65,157.49c-19.8,84.29-61.4,159.4-148.77,185.06c-117.87,55.02-132.92,97.58-258.09,25.2
|
||||
c46.48,32.06,76.67,55,136.6,42.95c-16.18,37.26-58.98,60.98-65.42,102.39c20.1-25.76,40.19-51.51,60.29-77.27
|
||||
c-8.67,85.19,10.27,58.11-48.65,125.91c90.48-25.72,17.5-180.83,122.56-177.14c18.96,52.7-4.18,120.02,8.63,173.54
|
||||
c2.68-39.19,12.24-88.12,46.19-106.21c456.17,81.79,348.93-736.88,256.65-896.72c22.97,42.24,68.59,278.45,36,201.55
|
||||
c-3.91-9.81-16.93-10.42-12.04,2.06c-3.92,64.31,14.29,179.15-11.75,192.94z M348.31,563.23c5.68,9.7,20.47-3.9,12.83-8.26c-3.76-6.73-7.6-13.49-12.17-19.69
|
||||
c-8.79-11.93,9.75-6.48,8.3-13.53c21.55,9.97,28.08,14.2,46.14-5.3c-0.61,18.31-0.16,38.03-18.36,50.23
|
||||
c18.72,10.53,30.46-6.95,33.62-24.89c-25.93,2.12,1.38-48.33,8.9-22.45c7.53-11.49,6.87-26.71,9.65-39.21
|
||||
C420.8,350.18,268.58,459.17,348.31,563.23z M517.13,804.37c-10.18-5.07-29.37-7.14-13.37-22.49
|
||||
c18.15-20.96,102.96-23.38,73.03,12.59c29.3-17.57-7.7-35.22-30.46-31.69C518.69,758.1,459.7,799.43,517.13,804.37z
|
||||
N
|
||||
M106.8,0c-3.3,4.6-6.3,9.4-8.8,14.5c-2.8-5-5.7-9.8-8.8-14.5C52.9,32.5,56.8,74.2,98,99.6c0,0,0,0,0,0c0,0,0,0,0,0C139.5,74,142.9,32.2,106.8,0z M5.4,60.7c3.7,4.5,7.4,8.8,11,12.8c-5.4,1-10.9,2.3-16.5,3.9 c19.6,44.6,60.5,53.8,97.4,22.5c0,0,0,0,0,0c0,0,0,0,0,0C85.9,52.4,47.2,36.3,5.4,60.7z M31.2,174.9c5.3-1.5,10.5-3.8,15.6-6.6c-0.8,5.7-1.2,11.3-1.4,16.8c48.5-4.9,69.9-40.9,51.5-85.7c0,0,0,0-0.1,0c0,0,0,0,0,0C48.2,95.8,20.9,127.6,31.2,174.9zM148.3,186.1c-0.4-5.5-0.9-11.1-1.4-16.8c5.1,2.4,10.3,4.6,15.6,6.6c10.4-47.6-17.3-79.1-65.6-75.5c0,0,0,0,0,0c0,0-0.1,0-0.1,0C78.3,145.5,100.1,181.3,148.3,186.1z M195.1,76.9c-5.9-1.5-11.5-2.9-16.5-3.9c3.9-4,7.6-8.3,11-12.8c-42.1-24.6-80.6-8-92.1,39.1c0,0,0,0,0,0c0,0,0,0,0,0C134.9,130.9,175.6,121.2,195.1,76.9z
|
||||
N
|
||||
M35.2,14.9c-2.4,0-4.8,1-6.5,2.7c-1.7-1.7-4.1-2.7-6.5-2.7c-5.6,0.2-10,5-9.8,10.6c0,9.7,13.1,17.8,14.6,18.6c1,0.6,2.3,0.6,3.3,0C31.8,43.2,45,35.2,45,25.5C45.2,19.8,40.8,15.1,35.2,14.9z
|
||||
N
|
||||
M140 20C73 20 20 74 20 140c0 135 136 170 228 303 c88-132 229-173 229-303 c0-66-54-120-120-120c-48 0-90 28-109 69c-19-41-60-69-108-69z
|
||||
N
|
||||
M96,2.86c-2.05,4.41-4.22,9.48-6.33,15.17
|
||||
c-1.08,2.92-3,8.28-4.95,15.1c-3.16,11.03-4.62,19.6-5.81,26.71c-2.49,14.88-3.28,26.75-3.45,30.36c-0.02,0.43-0.08,1.78-0.2,3.64
|
||||
c-0.36,5.44-0.87,9.65-1.16,11.83c0,0-0.56,4.27-1.3,8.34c-0.19,1.07-0.4,2.13-0.4,2.13c-0.02,0.09-0.03,0.16-0.03,0.17
|
||||
c-0.05,0.25-5.9,30.37-5.91,40.92c0,0.85,0.03,3.62-1.34,4.24c-0.46,0.21-0.96,0.13-1.34,0.01c-7.07,0.06-12.87,0.76-16.99,1.42
|
||||
c0,0-13,2.1-30.7,9.21c-3.62,1.46-7.03,3-7.34,3.14c-2.48,1.13-4.67,2.19-6.52,3.12c1.83-0.17,4-0.52,6.39-1.21
|
||||
c1.84-0.53,3.45-1.16,4.82-1.78c0,0,10.45-3.6,19.4-5.26c5.58-1.03,6.34-0.55,17.45-1.7c6.41-0.66,11.59-1.36,14.88-1.84
|
||||
c7.74-1.12,10.4-0.32,11,1.04c0.13,0.29,0.13,0.55,0.11,0.94c-0.24,5.58-3.01,9.41-2.26,13.44c0.04,0.22,0.07,0.33,0.33,1.59
|
||||
c0.13,0.62,0.56,2.75,0.85,4.34c0.4,2.22,0.41,2.72,0.72,4.65c0.6,3.67,0.9,5.5,1.48,6.82c1.14,2.59,2.86,4.11,4.88,5.88
|
||||
c2.01,1.76,3.74,2.73,6.91,4.49c2.61,1.45,4.85,2.52,6.44,3.23z
|
||||
N
|
||||
M12.43,1.78c0.4,0.5,0.94,1.28,1.36,2.32
|
||||
c0.67,1.66,0.67,3.07,0.67,4.45c0,0.92,0.04,4.84,0,6.15c-0.19,6.59,0.61,7.24,0,11.71c-0.34,2.51-0.83,4.02-1.19,4.96
|
||||
c-0.18,0.48-0.94,2.43-2.52,4.74c-0.44,0.64-1.1,1.54-3.04,3.63c-3.35,3.61-5.27,4.39-5.19,5.19c0.13,1.28,5.07,2.54,25.78,2.55z
|
||||
N
|
||||
M105,654.21c-7.27-2.1-16.75-4.87-27.83-8.17
|
||||
c-40.23-11.98-49.04-15.35-58.28-22.6c-5.71-4.47-14.05-12.37-21.32-25.91C2.14,588.3,8.74,575.32,17.11,560
|
||||
c13.51-24.74,22.16-40.57,35.49-58.91c7.85-10.79,19.4-25.32,35.36-41.18c0.72-5.12,1.23-9.06,1.53-11.49
|
||||
c0.57-4.57,0.8-6.77,2.34-9.27c1.46-2.37,3.38-3.84,4.75-4.7c0.63-143.54,1.26-287.08,1.89-430.62z
|
||||
N
|
||||
M204.68,0.14c-1.76,0.11-4.62,0.27-8.17,0.38
|
||||
c-6.7,0.21-8.06-0.01-10.6,0.77c-3.92,1.2-3.97,2.71-8.07,4.59c-2.36,1.08-3.84,1.26-12.22,2.17c-5.45,0.59-10.87,1.53-16.34,1.91
|
||||
c-5.84,0.41-9.63,0.36-12,3.2c-1.04,1.25-1.47,2.63-1.66,3.56c-3.32,18.64-2.48,32.37-1.02,41.62c0.57,3.57,3.63,21.7,0.89,34.76
|
||||
c-0.17,0.79-0.64,2.93-1.15,5.97c-0.28,1.67-1.58,9.69-1.66,17.62c-0.05,5.4,1.24,12.84,3.83,27.7c0.5,2.88,1.27,7.13,2.17,13.28
|
||||
c0.59,4.02,1.01,7.31,1.28,9.45c-0.52,3.62-0.43,6.53-0.26,8.55c0.29,3.26,0.86,4.56,0.13,6.77c-0.77,2.31-1.92,2.45-2.43,4.85
|
||||
c-0.48,2.29,0.42,2.86-0.15,4.95c-0.41,1.49-1.13,2.2-2.79,4.24c-1.48,1.82-2.74,3.8-4.21,5.62c-4.31,5.35-8.49,10.81-12.89,16.09
|
||||
c-5.78,6.93-6.86,8.58-17.49,21.96c-18.52,23.3-21.63,26.32-32.55,40.21c-24.98,31.79-37.81,53.07-40.72,57.96
|
||||
c0,0-7.82,13.11-17.62,36.64c-2.39,5.73-5.45,13.54-6.38,24.13c-0.58,6.56-0.34,12.62,0,12.64c0.41,0.02,0.43-8.67,2.7-18.95
|
||||
c1.86-8.39,4.48-14.51,8.17-22.47c8.35-18.03,12.53-27.04,19.74-39.15c9.69-16.26,19.31-28.61,38.55-53.32
|
||||
c5.65-7.26,4.63-5.77,17.11-21.45c13.19-16.57,27.11-32.56,39.85-49.48c0.35-0.47,1.41-1.88,3.13-3.42c0,0,2.37-2.12,5.36-3.58
|
||||
c6.09-2.99,20.05-2.23,25.95-2c7.4,0.28,14.81-0.1,22.22-0.1c8.52,0,15.39-0.01,19.63-0.01z
|
||||
N
|
||||
M0.94,0.6c2.08,18.15,2.97,32.18,3.39,41.88
|
||||
c0,0,0.86,19.42,2.87,34.97c1.76,13.6,2.61,14.56,5.45,32.49c1.14,7.2,1.2,8.27,5.73,44.13c3.64,28.81,4.03,31.51,4.32,38.54
|
||||
c0.2,4.94,0.57,17.17-0.63,32.88c-0.89,11.66-2.25,19.73-3,24.73c-3.72,24.69-3.65,45.64-3.59,66.15
|
||||
c0.04,13.85,0.17,33.71,3.42,59.26c2.56,20.15,6,35.46,6.44,62.42c0.01,0.88,0.13,1.85,0.2,3.47c0.31,6.41-0.11,11.45-0.35,14.17
|
||||
c-3.35,37.28-5.52,47.52-5.52,47.52c-1.06,4.71-2.95,10.98-0.08,13.41c1.1,0.94,2.42,0.94,9.8,0.98c3.87,0.02,7.02,0.04,8.28,0.05z
|
||||
N
|
||||
M9.22,0C6.92,4.49,4,11.35,2.55,20.09
|
||||
c-1.21,7.29-0.82,12.5-0.33,20.94C3.3,59.23,3.79,73.41,3.9,76.76c0.65,20.48-0.9,19.3,0.05,35.96c0.7,12.33,2.2,24.62,2.98,30.95
|
||||
c1.78,14.5,2.82,18.69,2.45,27.6c-0.42,10.1-1.94,8.9-2.49,20.33c-0.54,11.15,0.83,13.91,0.19,27.57c-0.35,7.5-0.78,6.96-0.98,13.91
|
||||
c-0.12,4.35-0.01,6.38,0.61,21.61c0.24,5.92,0.64,10.99,0.78,17.78c0.12,6.38,0.06,11.89,0.02,14.77
|
||||
c-0.07,5.78-0.11,8.68-0.27,11.31c-0.96,16.14-5.06,22.75-1.69,25.57c0.93,0.78,1.57,0.55,8.39,0.78c4.83,0.16,8.78,0.42,11.44,0.61z
|
||||
N
|
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1,39 @@
|
||||
|
||||
vec3 foregroundColor = vec3(.0841, .5329, .9604);
|
||||
uniform vec3 starColors[10];
|
||||
uniform vec3 V0;
|
||||
varying vec3 norm;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
vec3 LDir=vec3(.5,.5,.5);
|
||||
void main(){
|
||||
vec3 color =foregroundColor.xyz;
|
||||
float sp = 0.4, df = 0.4, amb = 0.4, ex=5., alpha = 1.;
|
||||
vec3 l = vec3(1,1,1);
|
||||
|
||||
if(id < 1.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = 1.; df=.2; amb = .7, ex = 25.;}
|
||||
else if (id < 2.5) color = vec3(1.,.16,.36);
|
||||
else if (id < 3.5) {color = vec3(1.0000, 0.7725, 0.7725);sp = .5; df=.8; amb = .05;}
|
||||
else if (id < 4.5) {color = vec3(0.9612,0.3057,0.3369);sp = .5; df=.5; amb = .5; ex=20.;}
|
||||
else if (id < 6.5) {}
|
||||
else if (id < 7.5) {color = starColors[0]; sp = 0.3, df = 0.3, amb = 0.8, ex=5.;}
|
||||
else if (id < 8.5) {color = starColors[1]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 9.5) {color = starColors[2]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 10.5) {color = starColors[3]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 12.5) {color = starColors[4]; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 13.5) {color = starColors[4]*2.; sp = 0., df = 0., amb = 1., ex=10.,l = color;}
|
||||
else if (id < 14.5) {color = .4*foregroundColor + .8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
else if (id < 15.5) {color = .3*vec3(0.9612,0.3057,0.3369)+.8*starColors[4]; sp = 0.5, df = 0.5, amb = 0.8, ex=10.,l = color;}
|
||||
if(id < 0.){
|
||||
vec3 P = vec3(sin(glpos.y*1.), sin(glpos.x*1.5+1.), cos(glpos.z*1.));
|
||||
// APPLY PROCEDURAL NOISE TEXTURE.
|
||||
float cloud = min(0.99, max(0., 1. * noise(1. * P)));
|
||||
color = (1.-cloud)*color + starColors[5] * cloud*3.;
|
||||
}
|
||||
vec3 V = V0;
|
||||
vec3 W=normalize(glpos-V);
|
||||
vec3 realLDir=normalize(LDir - glpos);
|
||||
color = color*(amb+ df*max(0.,dot(norm,realLDir)))
|
||||
+ sp*pow(max(0., dot(2.*dot(norm, realLDir)*norm-realLDir, -W)),ex)*l;
|
||||
gl_FragColor=vec4(sqrt(color), alpha);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
uniform mat4 uMatrix;
|
||||
uniform mat4 invMatrix;
|
||||
uniform mat3 transformation;
|
||||
attribute float oid;
|
||||
attribute vec3 aPos;
|
||||
attribute vec3 normal;
|
||||
varying float id;
|
||||
varying vec3 glpos;
|
||||
varying vec3 norm;
|
||||
void main() {
|
||||
vec4 pos = uMatrix * vec4(aPos, 1.);
|
||||
gl_Position = pos ;
|
||||
glpos = pos.xyz;
|
||||
id = oid;
|
||||
norm = normalize(vec4(normal,0.)*invMatrix).xyz;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch index.html",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"file": "${workspaceFolder}/index.html",
|
||||
"runtimeExecutable": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
||||
"runtimeArgs": ["--args", "--allow-file-access-from-files"]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"file": "${workspaceFolder}/index.html",
|
||||
"runtimeExecutable": "C:/Users/sunyi/AppData/Local/Google/Chrome SxS/Application/chrome.exe",
|
||||
"runtimeArgs": ["--args", "--allow-file-access-from-files"]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"python.pythonPath": "/usr/local/bin/python"
|
||||
}
|