-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cfe550c
commit cf0ae7e
Showing
77 changed files
with
12,359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Fun with WebGL 2.0 - 083 - UV Mapped Icosphere / Icosahedron | ||
**Description**: | ||
Building an Icosphere is actually a pretty easy thing to do, but the trickiest part to get working is having it UV mapped procedurally. We explore setting up a new Geometry class with some support classes like Vertex to help us manage the data to produce the base shape of the Icosahedron then we explore how to do subdivision to a triangle that allows us to form it into a Icosphere. Then we continue on with an article that explains how to apply UV mapping and the steps to fix it so maps fairly well without a seam. | ||
|
||
|
||
### Links of Interest | ||
|
||
http://mft-dev.dk/uv-mapping-sphere/ | ||
|
||
http://wiki.unity3d.com/index.php/CreateIcoSphere | ||
|
||
https://en.wikipedia.org/wiki/Icosahedron | ||
|
||
https://medium.com/@peter_winslow/creating-procedural-planets-in-unity-part-1-df83ecb12e91 | ||
|
||
https://experilous.com/1/blog/post/procedural-planet-generation | ||
|
||
http://donhavey.com/blog/tutorials/tutorial-3-the-icosahedron-sphere/ | ||
|
||
http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html | ||
|
||
http://kiwi.atmos.colostate.edu/BUGS/geodesic/text.html | ||
|
||
http://vterrain.org/Textures/spherical.html | ||
|
||
https://github.com/superwills/gtp/blob/master/gtp/geometry/Icosahedron.cpp | ||
|
||
https://stackoverflow.com/questions/47441844/how-do-i-detect-the-vertices-on-the-seam-of-a-procedurally-generated-icosphere/49410688#49410688 | ||
|
||
|
||
### Links | ||
* [Lesson on Youtube](https://youtu.be/4u7HXv4b5-U) | ||
* [Youtube Series PlayList](https://www.youtube.com/playlist?list=PLMinhigDWz6emRKVkVIEAaePW7vtIkaIF) | ||
* [Fungi Git Change](https://github.com/sketchpunk/FunWithWebGL2/commit/cfe550c06c6187e83e7a012df51438887c25f5a3) | ||
* [Twitter](https://twitter.com/SketchpunkLabs) | ||
* [Patreon](https://www.patreon.com/sketchpunk) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import gl, { FBO } from "./gl.js"; | ||
import CameraOrbit from "./cameras/Orbit.js"; | ||
import GridFloor from "./primitives/GridFloor.js"; | ||
import Quad from "./primitives/Quad.js"; | ||
import Renderer from "./util/Renderer.js"; | ||
import RenderLoop from "./util/RenderLoop.js"; | ||
import VDebug from "./entities/VisualDebugger.js"; | ||
import {KBMCtrl, KBMCtrl_Viewport} from "./util/KBMCtrl.js" | ||
|
||
export default{ | ||
render :Renderer, //Main Render Function | ||
renderLoop :null, //Render loop | ||
lblFPS :null, //Html Element reference to a tag to update text of FPS | ||
|
||
mainCamera :null, //Main camera for the scene | ||
ctrlCamera :null, //Keyboard and Mouse controls for the camera | ||
|
||
debugLine :null, //Renderable used to help debug data or models | ||
debugPoint :null, //Same but with points. | ||
|
||
gridFloor :null, //Just a reference to the renderable for the grid floor. | ||
scene :[], //Array that holds the heirarchy of transforms / renderables. | ||
|
||
deltaTime :0, | ||
sinceStart :0, | ||
|
||
//Begin the GL Context | ||
init:function(){ gl.set("FungiCanvas"); return this; }, | ||
|
||
//Build all the main objects needed to get a scene up and running | ||
ready:function(renderHandler,opt=1){ | ||
this.mainCamera = new CameraOrbit().setPosition(0,0.5,2).setEulerDegrees(-15,10,0); | ||
this.ctrlCamera = new KBMCtrl().addHandler("camera",new KBMCtrl_Viewport(this.mainCamera),true,true); | ||
|
||
this.renderLoop = new RenderLoop(renderHandler); | ||
this.lblFPS = document.getElementById("lblFPS"); | ||
setInterval(function(){ this.lblFPS.innerHTML = this.renderLoop.fps; }.bind(this),200); | ||
|
||
if((opt & 1) == 1){ | ||
this.gridFloor = GridFloor(); | ||
this.scene.push(this.gridFloor); | ||
} | ||
|
||
//Setup Features | ||
if(opt){ | ||
if((opt & 2) == 2) this.scene.push( this.debugLine = new VDebug() ); //DEBUG LINE RENDERER | ||
if((opt & 4) == 4) this.scene.push( this.debugPoint = new VDebug().drawPoints() ); //DEBUG POINT RENDERER | ||
} | ||
|
||
return this; | ||
}, | ||
|
||
|
||
//Lesson 61 has example of how to do Deferred Rendering with MultiSample Render Buffers then Blits to Texture Buffers for final render | ||
setupDeferred:function(matName,onPre=null,onPost=null){ | ||
var fbo = new FBO(); //FBO Struct Builder | ||
this.deferred = { | ||
quad : Quad(-1,-1,1,1,matName,"postQuad").setOptions(true,false), | ||
fboRender : fbo.create().texColorBuffer("bColor",0).texDepthBuffer().finalize("fboRender"), | ||
onPreRender : ()=>{ FBO.clear(this.deferred.fboRender,false); }, | ||
onPostRender : ()=>{ | ||
FBO.deactivate(); | ||
this.render.prepareNext(this.deferred.quad).draw(); | ||
} | ||
}; | ||
|
||
if(onPre != null) this.render.onPreRender = onPre; | ||
if(onPost != null) this.render.onPostRender = onPost; | ||
}, | ||
|
||
|
||
//Get a frame ready to be rendered. | ||
update:function(){ | ||
this.mainCamera.update(); | ||
|
||
gl.UBOTransform.update( | ||
"matCameraView",this.mainCamera.invertedLocalMatrix, | ||
"posCamera",this.mainCamera.getWorldPosition(), //Because of Orbit, Position isn't true worldspace position, need to rotate , //this.mainCamera.position | ||
"fTime",new Float32Array( [this.sinceStart] ) | ||
); | ||
|
||
gl.clear(); | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import Vec3 from "./maths/Vec3.js"; | ||
import Mat3 from "./maths/Mat3.js"; | ||
import Mat4 from "./maths/Mat4.js"; | ||
import Quat from "./maths/Quat.js"; | ||
import DualQuat from "./maths/DualQuat.js"; | ||
|
||
const DEG2RAD = Math.PI/180; | ||
const RAD2DEG = 180/Math.PI; | ||
|
||
//Normalize x value to x range, then normalize to lerp the z range. | ||
function map(x, xMin,xMax, zMin,zMax){ return (x - xMin) / (xMax - xMin) * (zMax-zMin) + zMin; } | ||
|
||
function clamp(v,min,max){ return Math.max(min,Math.min(max,v)); } | ||
|
||
function smoothStep(edge1, edge2, val){ //https://en.wikipedia.org/wiki/Smoothstep | ||
var x = Math.max(0, Math.min(1, (val-edge1)/(edge2-edge1))); | ||
return x*x*(3-2*x); | ||
} | ||
|
||
//Get a number between A and B from a normalized number. | ||
function lerp(a,b,t){ return a + t * (b-a); } | ||
|
||
//From a point in space, closest spot to a 2D line | ||
function closestPointToLine2D(x0,y0,x1,y1,px,py){ | ||
var dx = x1 - x0, | ||
dy = y1 - y0, | ||
t = ((px-x0)*dx + (py-y0)*dy) / (dx*dx+dy*dy), | ||
x = x0 + (dx * t), //Util.lerp(x0, x1, t), | ||
y = y0 + (dy * t); //Util.lerp(y0, y1, t); | ||
return [x,y] | ||
} | ||
|
||
//From a point in space, closest spot to a 3D line | ||
function closestPointToLine3D(a,b,p,out){ | ||
if(out == undefined) out = new Vec3(); | ||
var dx = b.x - a.x, | ||
dy = b.y - a.y, | ||
dz = a.z - a.z, | ||
t = ((p.x-a.x)*dx + (p.y-a.y)*dy + (p.z-a.z)*dz) / (dx*dx+dy*dy+dz*dz), | ||
x = a.x + (dx * t), | ||
y = a.y + (dy * t), | ||
z = a.z + (dz * t); | ||
return out.set(x,y,z); | ||
} | ||
|
||
//Return back the two points that are closes on two infinite lines | ||
//http://geomalgorithms.com/a07-_distance.html | ||
function closestpoint_2Lines(A0,A1,B0,B1){ | ||
var u = A1.clone().sub(A0), | ||
v = B1.clone().sub(B0), | ||
w = A0.clone().sub(B0), | ||
a = Vec3.dot(u,u), // always >= 0 | ||
b = Vec3.dot(u,v), | ||
c = Vec3.dot(v,v), // always >= 0 | ||
d = Vec3.dot(u,w), | ||
e = Vec3.dot(v,w), | ||
D = a*c - b*b, // always >= 0 | ||
tU, tV; | ||
//compute the line parameters of the two closest points | ||
if(D < 0.000001){ // the lines are almost parallel | ||
tU = 0.0; | ||
tV = (b>c ? d/b : e/c); // use the largest denominator | ||
}else{ | ||
tU = (b*e - c*d) / D; | ||
tV = (a*e - b*d) / D; | ||
} | ||
|
||
//Calc Length | ||
//Vector vLen = w + (uT * u) - (vT * v); // = L1(sc) - L2(tc) | ||
//Float len = sqrt( dot(vLen,vLen) ); | ||
|
||
return [ u.scale(tU).add(A0), v.scale(tV).add(B0) ]; | ||
} | ||
|
||
//Return back the two points that are the closests but bound by the limit of two segments | ||
//http://geomalgorithms.com/a07-_distance.html | ||
function closestPointS_2Segments(A0,A1,B0,B1){ | ||
var u = A1.clone().sub(A0), | ||
v = B1.clone().sub(B0), | ||
w = A0.clone().sub(B0), | ||
a = Vec3.dot(u,u), // always >= 0 | ||
b = Vec3.dot(u,v), | ||
c = Vec3.dot(v,v), // always >= 0 | ||
d = Vec3.dot(u,w), | ||
e = Vec3.dot(v,w), | ||
D = a*c - b*b, // always >= 0 | ||
sc, sN, sD = D, // sc = sN / sD, default sD = D >= 0 | ||
tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 | ||
|
||
// compute the line parameters of the two closest points | ||
if(D < 0.000001){ // the lines are almost parallel | ||
sN = 0.0; // force using point P0 on segment S1 | ||
sD = 1.0; // to prevent possible division by 0.0 later | ||
tN = e; | ||
tD = c; | ||
}else{ // get the closest points on the infinite lines | ||
sN = (b*e - c*d); | ||
tN = (a*e - b*d); | ||
if(sN < 0.0){ // sc < 0 => the s=0 edge is visible | ||
sN = 0.0; | ||
tN = e; | ||
tD = c; | ||
}else if (sN > sD){ // sc > 1 => the s=1 edge is visible | ||
sN = sD; | ||
tN = e + b; | ||
tD = c; | ||
} | ||
} | ||
|
||
if (tN < 0.0){ // tc < 0 => the t=0 edge is visible | ||
tN = 0.0; | ||
// recompute sc for this edge | ||
if (-d < 0.0) sN = 0.0; | ||
else if (-d > a) sN = sD; | ||
else{ | ||
sN = -d; | ||
sD = a; | ||
} | ||
}else if(tN > tD){ // tc > 1 => the t=1 edge is visible | ||
tN = tD; | ||
// recompute sc for this edge | ||
if((-d + b) < 0.0) sN = 0; | ||
else if ((-d + b) > a) sN = sD; | ||
else{ | ||
sN = (-d + b); | ||
sD = a; | ||
} | ||
} | ||
|
||
// finally do the division to get sc and tc | ||
sc = (Math.abs(sN) < 0.000001 ? 0.0 : sN / sD); | ||
tc = (Math.abs(tN) < 0.000001 ? 0.0 : tN / tD); | ||
|
||
// get the difference of the two closest points | ||
//Vector dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) | ||
|
||
return [ u.scale(sc).add(A0), v.scale(tc).add(B0) ]; | ||
} | ||
|
||
|
||
export default { DEG2RAD:DEG2RAD, RAD2DEG:RAD2DEG, | ||
map:map, | ||
clamp:clamp, | ||
smoothStep:smoothStep, | ||
closestPointToLine2D:closestPointToLine2D, | ||
closestPointToLine3D:closestPointToLine3D, | ||
closestpoint_2Lines:closestpoint_2Lines, | ||
closestPointS_2Segments:closestPointS_2Segments, | ||
} | ||
|
||
export { Vec3, Mat3, Mat4, Quat, DualQuat, DEG2RAD, RAD2DEG } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import gl from "../gl.js"; | ||
import fungi from "../fungi.js"; | ||
import Transform from "../entities/Transform.js"; | ||
import {Vec3, Mat4, Quat, DEG2RAD} from "../Maths.js"; | ||
|
||
class Orbit extends Transform{ | ||
constructor(fov,near,far){ | ||
super(); | ||
//Setup the projection and invert matrices | ||
this.projectionMatrix = new Float32Array(16); | ||
this.invertedProjectionMatrix = new Float32Array(16); | ||
this.invertedLocalMatrix = new Float32Array(16); | ||
|
||
var ratio = gl.ctx.canvas.width / gl.ctx.canvas.height; | ||
Mat4.perspective(this.projectionMatrix, fov || 45, ratio, near || 0.1, far || 100.0); | ||
Mat4.invert(this.invertedProjectionMatrix, this.projectionMatrix); //Save Inverted version for Ray Casting. | ||
|
||
gl.UBOTransform.update( | ||
"matProjection",this.projectionMatrix, | ||
"screenRes", new Float32Array( [ gl.width, gl.height ] ) | ||
); //Initialize The Transform UBO. | ||
|
||
//Orbit Camera will control things based on euler, its cheating but not ready for quaternions | ||
this.euler = new Vec3(); | ||
} | ||
|
||
orthoProjection(zoom=1,near=-10,far=100){ | ||
var w = 1 * zoom, | ||
h = gl.height / gl.width * zoom; | ||
|
||
Mat4.ortho(this.projectionMatrix, -w, w, -h, h, near, far); | ||
Mat4.invert(this.invertedProjectionMatrix, this.projectionMatrix); //Save Inverted version for Ray Casting. | ||
|
||
gl.UBOTransform.update( | ||
"matProjection",this.projectionMatrix, | ||
"screenRes", new Float32Array( [ gl.width, gl.height ] ) | ||
); //Initialize The Transform UBO. | ||
|
||
return this; | ||
} | ||
|
||
//Override how this transfer creates the localMatrix : Call Update, not this function in render loop. | ||
updateMatrix(){ | ||
//Only Update the Matrix if its needed. | ||
//if(!this.position.isModified && !this.rotation.isModified && !this.euler.isModified) return this.localMatrix; | ||
|
||
Quat.setFromEuler(this.rotation,this.euler.x,this.euler.y,this.euler.z,"YXZ"); | ||
Mat4.fromQuaternion(this.localMatrix,this.rotation); | ||
this.localMatrix.resetTranslation().translate(this.position); | ||
|
||
//Set the modified indicator to false on all the transforms. | ||
this.position.isModified = false; | ||
this.rotation.isModified = false; | ||
this.euler.isModified = false; | ||
return this.localMatrix; | ||
} | ||
|
||
//Update the Matrices and UBO. | ||
update(){ | ||
if(this.position.isModified || this.scale.isModified || this.euler.isModified){ | ||
this.updateMatrix(); | ||
Mat4.invert(this.invertedLocalMatrix,this.localMatrix); | ||
} | ||
} | ||
|
||
setEulerDegrees(x,y,z){ this.euler.set(x * DEG2RAD,y * DEG2RAD,z * DEG2RAD); return this; } | ||
|
||
getWorldPosition(){ | ||
//Because of how orbit works, position isn't in worldspace. | ||
//Need to apply rotation to bring it into world splace | ||
var ary = new Float32Array(3); | ||
Quat.rotateVec3(this.rotation,this.position,ary); | ||
return ary; | ||
} | ||
|
||
worldToScreen(vAry){ | ||
var mat = new Float32Array(16), // Matrix4 Holder | ||
p = [0,0,0,0], // Vec4 | ||
rtn = []; // List of vec2 results | ||
|
||
//Move Points from WorldSpace to -> View Space (View Matrix) -> ClipSpace (ProjMatrix) | ||
Mat4.mult(mat,this.projectionMatrix,this.invertedLocalMatrix); | ||
|
||
for(var i=0; i < vAry.length; i++){ | ||
Mat4.transformVec3(p, vAry[i], mat); | ||
|
||
//Move from Clip Space to NDC Space (Normalized Device Coordinate Space) (-1 to 1 opengl viewport) | ||
if(p[3] != 0){ //only if W is not zero, | ||
p[0] = p[0] / p[3]; | ||
p[1] = p[1] / p[3]; | ||
} | ||
|
||
//Then finally move the points to Screen Space | ||
//Map points from -1 to 1 range into 0 to 1 range, Then multiple by canvas size | ||
rtn.push( // Replaced /2 with *0.5 | ||
( p[0] + 1) * 0.5 * gl.width, | ||
(-p[1] + 1) * 0.5 * gl.height | ||
); | ||
} | ||
|
||
if(vAry.length == 1) return rtn[0]; //Just return the one point | ||
return rtn; //Return all the points | ||
} | ||
} | ||
|
||
export default Orbit; |
Oops, something went wrong.