Published
Edited
Sep 1, 2021
Fork of WebGL
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable errorLog = ``
Insert cell
RunAll()
Insert cell
render_shader_code = ({
vs: `
precision mediump float;

// an attribute will receive data from a buffer
attribute vec4 a_position;
attribute vec2 a_texcoord;

// pass value to fragment shader
varying vec2 v_texcoord;
varying vec4 v_position;

// all shaders have a main function
void main() {

// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
v_texcoord = a_texcoord;
v_position = a_position;
}
`,
fs: `
#extension GL_EXT_draw_buffers : require
${fs_commonMacros}

// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision mediump float;

// input from vertex shader
varying vec2 v_texcoord;
varying vec4 v_position;

// the textures
uniform sampler2D u_texture_photonmap;

uniform sampler2D u_texture_colors;

uniform vec2 u_resolution;
uniform vec3 u_cameraEyeUVW[4]; // camera [Eye, U, V, W] at indexes [0, 1, 2, 3]
uniform float fov;

${myGLSLDefinitions}

PhotonData getPhoton(int index){
float baseCoordY = ((float(index) + 0.5) / float(WPM_PHOTONMAP_LENGTH));
vec4 photon_pos = texture2D(u_texture_photonmap, vec2(0.5/4.0, baseCoordY));
vec4 photon_dir = texture2D(u_texture_photonmap, vec2(1.5/4.0, baseCoordY));
vec4 photon_normal = texture2D(u_texture_photonmap, vec2(2.5/4.0, baseCoordY));
vec4 photon_state = texture2D(u_texture_photonmap, vec2(3.5/4.0, baseCoordY));
return PhotonData(photon_pos.xyz, photon_dir.xyz, photon_normal.xyz, photon_state.x, photon_state.y, photon_state.z);
}

void main() {
initMaterials();
float pixelID = v_texcoord.x + v_texcoord.y * u_resolution.x;

// gl_FragColor is a special variable a fragment shader
// is responsible for setting
vec2 pixel = gl_FragCoord.xy;

//--- BASE RAY CALC ----
float height = 2.0 * tan(fov/2.0);
float aspect = u_resolution.x / u_resolution.y;
float width = height * aspect;
vec2 windowDim = vec2(width,height);
vec2 pixelSize = windowDim/u_resolution;
vec2 delta = -0.5*windowDim + pixel*pixelSize;
vec2 pixelOriginOffset = pixelSize * (-0.5);

vec3 rayDir = getRayDirection(vec2(0.0), delta, u_cameraEyeUVW[1], u_cameraEyeUVW[2], u_cameraEyeUVW[3]);

RayCollisionOutput current_rco = detectNearestAABBCollision(u_cameraEyeUVW[0], rayDir);
if(current_rco.matchID > -1){
vec3 dir = vec3(0.0);
float count = 0.0;
float minDistSqr = 4.0 * 4.0;
for(int i=0; i < (WPM_PHOTONMAP_LENGTH); i++){
PhotonData photon = getPhoton(i);
if(int(photon.state) == (WPM_STATE_ABSORBED) || int(photon.state) == (WPM_STATE_REFLECTED)){
float distSqr = dot((photon.pos - current_rco.pos),(photon.pos - current_rco.pos));
if(distSqr < minDistSqr){
dir += photon.dir;
count += 1.0;
//power = this_power;
}
}
}

dir = normalize(dir);
gl_FragColor = vec4(current_rco.matCol * dot(current_rco.normal, -dir), 1.0);
//gl_FragColor = vec4(current_rco.matCol, 1.0);

}else{
gl_FragColor = vec4( rng2d(pixel), rng2d(pixel), rng2d(pixel), 1.0);
}
}
`
})
Insert cell
computing_shader_code = ({
vs: `
precision mediump float;

// an attribute will receive data from a buffer
attribute vec4 a_position;
attribute vec2 a_texcoord;

// pass value to fragment shader
varying vec2 v_texcoord;
varying vec4 v_position;

// all shaders have a main function
void main() {

// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
v_texcoord = a_texcoord;
v_position = a_position;
}
`,
fs: `
#extension GL_EXT_draw_buffers : require
${fs_commonMacros}
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision mediump float;

// input from vertex shader
varying vec2 v_texcoord;
varying vec4 v_position;

uniform sampler2D u_texture_colors;

uniform vec2 u_dimensions;
uniform vec3 u_lightPosMin;
uniform vec3 u_lightPosMax;

uniform sampler2D u_texture_data_pos;
uniform sampler2D u_texture_data_dir;
uniform sampler2D u_texture_data_power;
uniform sampler2D u_texture_data_state;

${myGLSLDefinitions}

void main() {
initMaterials();

// gl_FragData.xy contains the width and height of the framebuffer texture
// therefore variable "pixel" will contain natural index and
// variable "v_texcoord" will contain normalized index
vec2 pixel = gl_FragCoord.xy; // natural index

float pixelID = pixel.x + (pixel.y * u_dimensions.x);

vec4 prev_state = texture2D(u_texture_data_state, v_texcoord);
vec3 photon_origin;
vec3 photon_dir;
bool isAlive = true;
int previous_state = int(prev_state.x);
if( previous_state == (WPM_STATE_INIT) ){
// ----------------Emission of Photon------------------
isAlive = true;
AffineSquare aslight = GenerateAffineSquare(u_lightPosMin.xz, u_lightPosMax.xz);

photon_origin = getLightPosSample_Square(aslight, rng2d(pixel), rng2d(pixel), u_lightPosMin.y );

photon_dir = getRandomHemisphereSample(vec3(0.0, -1.0, 0.0), photon_origin, rng2d(pixel), 2.0 * PI * rng2d(pixel));
}else if( previous_state == (WPM_STATE_REFLECTED) ){
// --------------------A Bounce of Photon-------------------------
isAlive = true;
photon_origin = texture2D(u_texture_data_pos, v_texcoord).xyz;

vec3 prev_dir = texture2D(u_texture_data_dir, v_texcoord).xyz;
vec3 prev_normal = texture2D(u_texture_data_power, v_texcoord).xyz;

//Reflection direction = r = I − 2 (I ⋅ n) n
photon_dir = prev_dir - 2.0 * dot(prev_dir, prev_normal) * prev_normal;

}else if( previous_state == (WPM_STATE_TRANSMITTED) ){
isAlive = false;

}else {
isAlive = false;

}

if(isAlive){
RayCollisionOutput current_rco = detectNearestAABBCollision(photon_origin, photon_dir);
if(current_rco.matchID > -1){
float r = rng2d(pixel);
float stateVal;
if(r < current_rco.matProb.x){ // absorbed
stateVal = float(WPM_STATE_ABSORBED);
}else if(r < current_rco.matProb.x + current_rco.matProb.y){ // reflected
stateVal = float(WPM_STATE_REFLECTED);
}else{ // transmitted
stateVal = float(WPM_STATE_TRANSMITTED);
}
stateVal = float(WPM_STATE_REFLECTED); ///// <-------------- HACK to TEST
gl_FragData[0] = vec4(current_rco.pos, 1.0); // position
gl_FragData[1] = vec4(photon_dir, 1.0); // direction
gl_FragData[2] = vec4(current_rco.normal, 1.0); // power
gl_FragData[3] = vec4(stateVal, current_rco.matchID, prev_state.x, 1.0); // state

}else { // photon went out of the scene
gl_FragData[0] = vec4(WPM_STATE_NOT_VALID); // position
gl_FragData[1] = vec4(WPM_STATE_NOT_VALID); // direction
gl_FragData[2] = vec4(WPM_STATE_NOT_VALID); // power
gl_FragData[3] = vec4(WPM_STATE_NOT_VALID, WPM_STATE_NOT_VALID, prev_state.x, 1.0); // state
}
}else{ // Is a dead photon
gl_FragData[0] = vec4(WPM_STATE_NOT_VALID); // position
gl_FragData[1] = vec4(WPM_STATE_NOT_VALID); // direction
gl_FragData[2] = vec4(WPM_STATE_NOT_VALID); // power
gl_FragData[3] = vec4(WPM_STATE_NOT_VALID); // state
}
// DEBUG COMPUTATION

}
`
})
Insert cell
function ComputePhotonMap(){
// ---------------------INIT-COMPUTE ---------------------------
showDebug("--init-Computing--");
const targetTextureWidth = numPhotons;
const targetTextureHeight = 1;
const ComputingProgram = createProgram(gl, computing_vs, computing_fs);
showDebug("ComputeProg : " , ComputingProgram);
// Tell it to use our program (pair of shaders)
gl.useProgram(ComputingProgram);
// Initialize Attributes and Uniforms

// ------------Set Attributes for Vertex Shader
// --positions
const a_positions = c_LocateAttribute("a_position", ComputingProgram, rectangleByTriangles_3fv_Positions, 3, gl.FLOAT);
//c_ActivateAttribute(a_positions, 3, gl.FLOAT);

// --texcoords
const a_texcoords = c_LocateAttribute("a_texcoord", ComputingProgram, rectangleByTriangles_2fv_TexCoords, 2, gl.FLOAT);

// -----------Set Uniforms for Fragment Shader
// --lightPosMin
c_LocateUniform("u_lightPosMin", ComputingProgram, new Float32Array( [-0.125, 0.999, -0.125] ), "3fv");
// --lightPosMax
c_LocateUniform("u_lightPosMax", ComputingProgram, new Float32Array( [0.125, 1.001, 0.125] ), "3fv");

// --dimensions (dimensions of the textureTarget)
c_LocateUniform("u_dimensions", ComputingProgram, new Float32Array([targetTextureWidth, targetTextureHeight]), "2fv");
// --Transforms of objects
const textrure_all_transforms = c_CreateDataTexture(4,7, gl.RGBA, gl.FLOAT, new Float32Array(transformation_matrices));
c_LocateUniform("u_texture_transforms", ComputingProgram, 0, "1i");
// --current status data of photons stored as textures at following texture locations
c_LocateUniform("u_texture_data_pos", ComputingProgram, 1, "1i");
c_LocateUniform("u_texture_data_dir", ComputingProgram, 2, "1i");
c_LocateUniform("u_texture_data_power", ComputingProgram, 3, "1i");
c_LocateUniform("u_texture_data_state", ComputingProgram, 4, "1i");
const colorTexture_comp = c_CreateDataTexture(4, 2, gl.RGB, gl.UNSIGNED_BYTE, distinct_colors_3fv);
c_LocateUniform("u_texture_colors", ComputingProgram, 5, "1i");
// --------------Setup render target textures for writing-in the computed values
// Create target render textures
const targetTex_Position = c_CreateTargetTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT);
const targetTex_Direction = c_CreateTargetTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT);
const targetTex_Power = c_CreateTargetTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT);
const targetTex_State = c_CreateTargetTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT);
// --------------Setup current state of photons textures with initial photon states
const computed_Position = new Float32Array( Array(targetTextureWidth * targetTextureHeight * 4).fill(wpm_states.INITIAL_STATE) );
const computed_Direction = new Float32Array( Array(targetTextureWidth * targetTextureHeight * 4).fill(wpm_states.INITIAL_STATE) );
const computed_Power = new Float32Array( Array(targetTextureWidth * targetTextureHeight * 4).fill(wpm_states.INITIAL_STATE) );
const computed_State = new Float32Array( Array(targetTextureWidth * targetTextureHeight * 4).fill(wpm_states.INITIAL_STATE) );
//showDebug("power: ", computed_Power);
let dataTex_Position = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Position);
let dataTex_Direction = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Direction);
let dataTex_Power = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Power);
let dataTex_State = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_State);
// The Photon Map
let dataset_photonMap = new Float32Array(numPhotons * numPhotonBounces * photonMapElementSize);
// Create and bind the framebuffer point and attach the target textures for writing-in
const fb = gl.createFramebuffer();
// -------------------------------- COMPUTE -------------------------------
showDebug("--Computing--");
// Shooting photons
for(let bounceId = 0; bounceId < numPhotonBounces; ++bounceId)
{
showDebug("<<< ---- Bounce: " + bounceId + "---- >>>");
// render to our targetTexture by binding the framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

// attach the textures as the data receiving attachments
gl.framebufferTexture2D(gl.FRAMEBUFFER, EXTdb.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, targetTex_Position, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, EXTdb.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, targetTex_Direction, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, EXTdb.COLOR_ATTACHMENT2_WEBGL, gl.TEXTURE_2D, targetTex_Power, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, EXTdb.COLOR_ATTACHMENT3_WEBGL, gl.TEXTURE_2D, targetTex_State, 0);
EXTdb.drawBuffersWEBGL([
EXTdb.COLOR_ATTACHMENT0_WEBGL, // gl_FragData[0] position
EXTdb.COLOR_ATTACHMENT1_WEBGL, // gl_FragData[1] direction
EXTdb.COLOR_ATTACHMENT2_WEBGL, // gl_FragData[2] power
EXTdb.COLOR_ATTACHMENT3_WEBGL // gl_FragData[3] state
]);

// Compute photons into frame buffer objects using fragment shaders
{
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, targetTextureWidth, targetTextureHeight);

// Clear the attachment(s).
gl.clearColor(0, 0, 1, 1); // clear to blue
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// Set textures
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, textrure_all_transforms);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, dataTex_Position);

gl.activeTexture(gl.TEXTURE0 + 2);
gl.bindTexture(gl.TEXTURE_2D, dataTex_Direction);

gl.activeTexture(gl.TEXTURE0 + 3);
gl.bindTexture(gl.TEXTURE_2D, dataTex_Power);

gl.activeTexture(gl.TEXTURE0 + 4);
gl.bindTexture(gl.TEXTURE_2D, dataTex_State);

gl.activeTexture(gl.TEXTURE0 + 5);
gl.bindTexture(gl.TEXTURE_2D, colorTexture_comp);

// Execute shaders to compute values into target textures on framebuffer objects
gl.drawArrays(gl.TRIANGLES, 0, 6 );
}

// Read photons from fragment shaders into buffers
const format = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
const type = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
showDebug("Format : ", ((format == gl.RGBA)? 'RGBA' : format), " Type :",((type == gl.FLOAT)? 'FLOAT' : type));

showDebug("============");
ReadPixelData(computed_Position, targetTextureWidth, targetTextureHeight, targetTex_Position, gl.RGBA, gl.FLOAT);
ReadPixelData(computed_Direction, targetTextureWidth, targetTextureHeight, targetTex_Direction, gl.RGBA, gl.FLOAT);
ReadPixelData(computed_Power, targetTextureWidth, targetTextureHeight, targetTex_Power, gl.RGBA, gl.FLOAT);
ReadPixelData(computed_State, targetTextureWidth, targetTextureHeight, targetTex_State, gl.RGBA, gl.FLOAT);
// Save to the photon map
{
for(let pid = 0; pid < numPhotons; ++pid){
const photonData = [
// computed_Position element size = 4
computed_Position[pid * 4], computed_Position[pid * 4 + 1], computed_Position[pid * 4 + 2],
// computed_Direction element size = 4
computed_Direction[pid * 4], computed_Direction[pid * 4 + 1], computed_Direction[pid * 4 + 2],
// computed_Power element size = 4
computed_Power[pid * 4], computed_Power[pid * 4 + 1], computed_Power[pid * 4 + 2], computed_Power[pid * 4 + 3],
// computed_State element size = 4
computed_State[pid * 4], computed_State[pid * 4 + 1]
];
// dataset_photonMap element size = photonData.length = 12
for(let didx = 0; didx < 14; ++didx){
dataset_photonMap[(bounceId * numPhotons * photonData.length) + (pid * photonData.length) + didx] = photonData[didx];
}
}
}

// Rewrite current_data_textures with new data from the buffers
gl.deleteTexture(dataTex_Position);
gl.deleteTexture(dataTex_Direction);
gl.deleteTexture(dataTex_Power);
gl.deleteTexture(dataTex_State);

dataTex_Position = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Position);
dataTex_Direction = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Direction);
dataTex_Power = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_Power);
dataTex_State = c_CreateDataTexture(targetTextureWidth, targetTextureHeight, gl.RGBA, gl.FLOAT, computed_State);
} // end bounce loop

showDebug("============");
// Release resources
gl.deleteTexture(targetTex_Position);
gl.deleteTexture(targetTex_Direction);
gl.deleteTexture(targetTex_Power);
gl.deleteTexture(targetTex_State);
gl.deleteTexture(dataTex_Position);
gl.deleteTexture(dataTex_Direction);
gl.deleteTexture(dataTex_Power);
gl.deleteTexture(dataTex_State);
gl.deleteTexture(colorTexture_comp);
gl.deleteTexture(textrure_all_transforms);
gl.deleteFramebuffer(fb);
gl.deleteProgram(ComputingProgram);
//==============================================================================
return dataset_photonMap;
}
Insert cell
Insert cell
function RunAll(){
const fixedTypeArray_PhotonMap = ComputePhotonMap();
showDebug('============================');
showDebug(fixedTypeArray_PhotonMap);
if(debugging && debugging_arrays)
for(let i = 0; i < fixedTypeArray_PhotonMap.length / photonMapElementSize; i++){
showDebug(fixedTypeArray_PhotonMap.slice(i * photonMapElementSize, i * photonMapElementSize + photonMapElementSize));
if(i == 100) break;
}
showDebug('============================');
RenderScene(fixedTypeArray_PhotonMap);
return md`### WebGL All Programmes Run`;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function ReadPixelData(in_ArrayBufferView, in_targetTextureWidth, in_targetTextureHeight, in_targetTex, in_glFormat, in_glType){
const fb_reader = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb_reader);
gl.framebufferTexture2D(gl.FRAMEBUFFER, EXTdb.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, in_targetTex, 0);

gl.readPixels(0, 0, in_targetTextureWidth, in_targetTextureHeight, in_glFormat, in_glType, in_ArrayBufferView);
gl.deleteFramebuffer(fb_reader);
showDebug("Pixels :\n",in_ArrayBufferView,"\nDone.");
showDebug(in_targetTextureWidth, in_targetTextureHeight);
if(debugging && debugging_arrays)
for(let i = 0; i < in_ArrayBufferView.length / 4; i++){
showDebug(in_ArrayBufferView.slice(i * 4, i * 4 + 4));
if(i == 100) break;
}
return in_ArrayBufferView;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
myGLSLDefinitions = `
// ----------- OTHER LIBS -----------
${glslInverseFunctions}
${rngCode}

// ---------- COMMON VARIABLES -------

uniform sampler2D u_texture_transforms;

vec3 matCols[7];
vec3 matProbs[7];
vec3 matRatios[7]; // loss ratio

void initMaterials(){
matCols[0] = vec3(1.0, 0.89, 0.81);
matCols[1] = vec3(1.0, 0.90, 0.81);
matCols[2] = vec3(1.0,0.99,0.81);
matCols[3] = vec3(0.9,0.1,0.1);
matCols[4] = vec3(0.1,0.9,0.1);
matCols[5] = vec3(1.0,0.69,0.81);
matCols[6] = vec3(1.0,0.59,0.81);

matProbs[0] = vec3(0.5, 0.5, 0.0);
matProbs[1] = vec3(0.5, 0.5, 0.0);
matProbs[2] = vec3(0.5, 0.5, 0.0);
matProbs[3] = vec3(0.5, 0.5, 0.0);
matProbs[4] = vec3(0.5, 0.5, 0.0);
matProbs[5] = vec3(0.1, 0.2, 0.7);
matProbs[6] = vec3(0.3, 0.3, 0.3);

matRatios[0] = vec3(0.1, 0.5, 1.0);
matRatios[1] = vec3(0.1, 0.5, 1.0);
matRatios[2] = vec3(0.1, 0.5, 1.0);
matRatios[3] = vec3(0.1, 0.5, 1.0);
matRatios[4] = vec3(0.1, 0.5, 1.0);
matRatios[5] = vec3(0.1, 0.5, 1.0);
matRatios[6] = vec3(0.1, 0.5, 1.0);
}

// ---------- CONSTANTS ------------
const float EPSILON = 1e-5;
const float INFINITY = 1e5;
const float PI = 3.14159265358;
const int shadowEnabledIndex = 4;
const int numOfTransformations = 7;

// ---------- STRUCTS & FUNCTIONS -------------

struct ObjectFeatures
{
mat4 transform;
vec3 matColor;
vec3 matProbs;
float matRatio;
};

struct PhotonData
{
vec3 pos;
vec3 dir;
vec3 normal;
float power;
float state;
float hitMatchID;
};

struct Ray
{
vec3 origin;
vec3 direction;
float tMin;
float tMax;
};

vec3 pointOnRay(in Ray ray,float t){
return (ray.origin+t*ray.direction);
}

struct AABB{
vec3 minP;
vec3 maxP;
};

AABB cannonical_aabb = AABB(vec3(-0.5),vec3(0.5));

float calcMod(float a, float b){
return a - (b * floor(a/b)); // return a mod b
}

vec2 getDataTextureCoords(float x, float y, float width, float height){
return vec2( (x)/width, (y)/height );
}

/*
Calculates integer index of the given
normalized index (param: nval) on a normalized
dimension of expected length
*/
float getIntIndex(float nval, float length){
return calcMod(nval * length, length);
}

float getIntIndexFromTexcoord(float texcoordVal, float length){
return ((texcoordVal * length) - 0.5);
}

mat4 getTransform(int index){
vec4 col0 = texture2D(u_texture_transforms, getDataTextureCoords(0.5, float(index) + 0.5, 4.0, 7.0));
vec4 col1 = texture2D(u_texture_transforms, getDataTextureCoords(1.5, float(index) + 0.5, 4.0, 7.0));
vec4 col2 = texture2D(u_texture_transforms, getDataTextureCoords(2.5, float(index) + 0.5, 4.0, 7.0));
vec4 col3 = texture2D(u_texture_transforms, getDataTextureCoords(3.5, float(index) + 0.5, 4.0, 7.0));
return mat4(col0, col1, col2, col3);
}



Ray getRay(vec2 pixel, vec2 resolution, float fov, vec3 cameraEye, vec3 cameraU, vec3 cameraV, vec3 cameraW){
Ray ray;
ray.origin = cameraEye;
float height = 2.*tan(fov/2.);
float aspect = resolution.x/resolution.y;
float width = height*aspect;
vec2 windowDim = vec2(width,height);
vec2 pixelSize = windowDim/resolution;
vec2 delta = -0.5*windowDim + pixel*pixelSize;
ray.direction = normalize(-cameraW + cameraV*delta.y + cameraU*delta.x);
ray.tMin = 0.;
ray.tMax = INFINITY;
return ray;
}

bool insersectsRayAABB(inout Ray ray, AABB aabb) {
vec3 tMin = (aabb.minP.xyz - ray.origin) / ray.direction;
vec3 tMax = (aabb.maxP.xyz - ray.origin) / ray.direction;
vec3 t1 = min(tMin, tMax);
vec3 t2 = max(tMin, tMax);
//float tNear = max(max(max(t1.x, t1.y), t1.z),ray.tMin);
//float tFar = min(min(min(t2.x, t2.y), t2.z),ray.tMax);
float tNear = max(max(t1.x, t1.y), t1.z);
float tFar = min(min(t2.x, t2.y), t2.z);
if (tNear < tFar){
ray.tMin = tNear;
ray.tMax = tFar;
return true;
}
return false;
}

vec3 getNormal(AABB aabb,vec3 p){
vec3 c = (aabb.minP+aabb.maxP)/2.;
vec3 d = p-c;
vec3 v = abs(d);
int i = v.y > v.x ? ( v.z > v.y ? 2 : 1 ) : ( v.z > v.x ? 2 : 0 );

// Which Sign of the axis: The sign of the component selects the positive or negative direction.
return (i == 0) ? ((p.x>=0.)?vec3(1,0,0):vec3(-1,0,0)) :
(i == 1) ? ((p.y>=0.)?vec3(0,1,0):vec3(0,-1,0)) :
((p.z>=0.)?vec3(0,0,1):vec3(0,0,-1));
}

vec3 getNormalColor(AABB aabb,vec3 p, vec3 c1, vec3 c2, vec3 c3, vec3 c4, vec3 c5, vec3 c6){
vec3 c = (aabb.minP+aabb.maxP)/2.;
vec3 d = p-c;
vec3 v = abs(d);
int i = v.y > v.x ? ( v.z > v.y ? 2 : 1 ) : ( v.z > v.x ? 2 : 0 );

// Which Sign of the axis: The sign of the component selects the positive or negative direction.
return (i == 0) ? ((p.x>=0.)?c1:c2) :
(i == 1) ? ((p.y>=0.)?c3:c4) :
((p.z>=0.)?c5:c6);
}

bool insersectsRayCannonicalAABB(inout Ray ray) {
return insersectsRayAABB(ray, cannonical_aabb);
}

bool checkRayVsSphereCollision( vec3 origin, vec3 dir, vec3 center, float radius, inout float out_t){
vec3 O_C = origin - center;
float A = dot(dir,dir);
float B = 2.0 * dot( dir, O_C);
float D = dot(O_C, O_C) - (radius * radius);
if ((B * B - 4.0 * A * D) < 0.0){
return false; // not hitting Sphere
}
float det = sqrt(B * B - 4.0 * A * D);
// Far contact: float t1 = (0.0 - B + det) / (2.0 * A);
// Output the Near contact:
out_t = (0.0 - B - det) / (2.0 * A);
return true; // hitting the Sphere
}

// samplePosUV is such that -0.5 <= u,v <= 0.5
vec3 getRayDirection(vec2 samplePosOffset, vec2 delta, vec3 camU, vec3 camV, vec3 camW){
vec2 sampleDelta = delta + samplePosOffset;
return normalize(-camW + camV*sampleDelta.y + camU*sampleDelta.x);
}

struct AffineSquare
{
vec2 minPoint; // p0
vec2 p1;
vec2 maxPoint; // p2
vec2 p3;
};

AffineSquare GenerateAffineSquare(vec2 minPoint, vec2 maxPoint)
{
// Quadrilataral Affine Transformation of a Point
AffineSquare as;
as.minPoint = minPoint; // p0
as.p1 = vec2(maxPoint.x, minPoint.y);
as.maxPoint = maxPoint; // p2
as.p3 = vec2(minPoint.x, maxPoint.y);
return as;
}

vec2 getAffineUVPos(AffineSquare as, float u, float v)
{
return (1.0 - v) * ( (1.0 - u) * as.minPoint + u * as.p1 ) + v * ( (1.0 - u) * as.p3 + u * as.maxPoint );
}

vec3 getLightPosSample_Square(AffineSquare ASLight, float rnd_u, float rnd_v, float lightPosY)
{
vec2 this_randomlightpos2D = getAffineUVPos(ASLight, rnd_u, rnd_v);
return vec3(this_randomlightpos2D.x, lightPosY, this_randomlightpos2D.y);
}

struct RayCollisionOutput
{
float tmin; // collision tmin value
int matchID; // colliding object index
Ray matchInvRay; // colliding object's inverse transformation matrix

vec3 pos; // hitpos
vec3 normal; // hitnormal

vec3 matCol; // surface color
vec3 matProb;
vec3 matRatio;
};

// Used for both shadow test and ray-scene intersection
RayCollisionOutput detectNearestAABBCollision(vec3 rayOrigin, vec3 rayDirection){
// Storage of matching collision:
RayCollisionOutput retVal;
retVal.tmin = INFINITY; // collision tmin value
retVal.matchID = -1; // colliding object index

Ray ray = Ray(rayOrigin, rayDirection, 0.0, INFINITY);

// ---------Iterate over each object for collision-------
for(int i=0; i < numOfTransformations; i++){
//if (ignoreIdx != i){
Ray invRay = ray;
//invRay.direction = ray.direction;
mat4 mt = getTransform(i); // the transform
mat4 imt = inverse(mt); // inverse transform
// apply transform to ray
vec4 transformedRayOrigin = imt * vec4(invRay.origin, 1.0);
vec4 transformedRayDir = imt * vec4(invRay.direction, 0.0);
invRay.origin = transformedRayOrigin.xyz;
invRay.direction = transformedRayDir.xyz;
// compute tmin
if (insersectsRayCannonicalAABB(invRay)){
if(invRay.tMin < retVal.tmin){
retVal.tmin = invRay.tMin;
retVal.matchID = i;
retVal.matchInvRay = invRay;
vec3 q = pointOnRay(invRay, invRay.tMin); // hitpoint on cannonical scene
retVal.pos = pointOnRay(ray, invRay.tMin); // hitpoint on real scene
vec3 N = getNormal(cannonical_aabb, q); // normal on cannonical aabb
vec4 realN4 = getTransform(i) * vec4(N,0.0); // normal on real scene homogenous vector
retVal.normal = normalize(vec3(realN4[0], realN4[1], realN4[2])); // normal on real scene

retVal.matCol = matCols[i]; // color of surface
retVal.matProb = matProbs[i];
retVal.matRatio = matRatios[i];
}
}
//}
}

return retVal;
}

vec3 getRandomHemisphereSample(vec3 realNormal, vec3 hitpos, float randomZ, float randomPhi){
float r = sqrt(1.0 - randomZ * randomZ);
vec3 d = vec3(hitpos.x + r * cos(randomPhi), hitpos.y + r * sin(randomPhi), hitpos.z + randomZ);
//--find U,V,W such that Normal == W and U,V,W are orthogonal
vec3 tempVector = realNormal;
if (abs(realNormal.x) < abs(realNormal.y) && abs(realNormal.x) < abs(realNormal.z))
tempVector.x = 1.0;
else if (abs(realNormal.y) < abs(realNormal.x) && abs(realNormal.y) < abs(realNormal.z))
tempVector.y = 1.0;
else tempVector.z = 1.0;
vec3 U = normalize(cross(tempVector,realNormal));
vec3 V = cross(realNormal,U);
vec3 W = realNormal;
//--
vec3 dsample = vec3(dot(d, U) , dot(d, V), dot(d, W));
return normalize(dsample);
}

`
Insert cell
render_vs = createShader(gl, gl.VERTEX_SHADER, render_shader_code.vs);
Insert cell
Insert cell
computing_vs = createShader(gl, gl.VERTEX_SHADER, computing_shader_code.vs);
Insert cell
computing_fs = createShader(gl, gl.FRAGMENT_SHADER, computing_shader_code.fs);
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
a = new Float32Array(b)
Insert cell
b = {
const aa = Array(10).fill(1).map( (x,j) => (Array(6).fill(1).map((d,i) => Math.floor(Math.random() * 10) )) ).flat();
for(let i=0; i < aa.length; ++i){
if(i % 6 > 3) aa[i] = -1;
}
return aa;
}
Insert cell
a
Insert cell
printElement(a, 6, 2)
Insert cell
function printElement(in_array, in_elementSize, in_index){
const startIdx = getElementIndex(in_elementSize, in_index);
const ret = in_array.slice(startIdx, startIdx + in_elementSize);
//console.log(ret);
return ret;
}
Insert cell
printkdTree(a, 6, 2, 0)
Insert cell
function printkdTree(in_array, in_elementSize, in_numDimensions, in_index){
const idx = getElementIndex(in_elementSize, in_index);
console.log(...in_array.slice(idx,idx+in_numDimensions),": ", in_array[idx+in_elementSize-2], in_array[idx+in_elementSize-1]);
const leftIdx = in_array[getLeftIndex(in_elementSize, in_index)];
const rightIdx = in_array[getRightIndex(in_elementSize, in_index)];
if(leftIdx != -1){ printkdTree(in_array, in_elementSize, in_numDimensions, leftIdx); }
if(rightIdx != -1){ printkdTree(in_array, in_elementSize, in_numDimensions, rightIdx); }
}
Insert cell
generateKdTreeFromArray(a, 6, 2)
Insert cell
function getElementIndex(in_elementSize, in_index){
return in_index * in_elementSize;
}
Insert cell
function getKeyIndex(in_elementSize, in_index, in_dimension){
return in_index * in_elementSize + in_dimension;
}
Insert cell
function getRightIndex(in_elementSize, in_index){
return in_index * in_elementSize + in_elementSize - 1;
}
Insert cell
function getLeftIndex(in_elementSize, in_index){
return in_index * in_elementSize + in_elementSize - 2;
}
Insert cell
Array(a.length/6).fill(0).map((d,i) => printElement(a, 6, i))
Insert cell
foundNbrs.map(d => printElement(a, 6, d))
Insert cell
foundNbrs.map(d => calcSquaredDistance(2, printElement(a, 6, d), 0, [0,0], 0) )
Insert cell
foundNbrs = kdNearestNeighborsInRange(a, 6, 2, [0,0], 9, 5)
Insert cell
Array(6 * 3).fill(-1).map((d,i) => (i % 3 == 2)? (1 + Math.floor(i/3)) % 6 : 100)
Insert cell
{
const maxPoints = 5;
//--sorted list implementation => (keyValue, Value, Next) where Next is nearest smaller keyValue's Index
const sortedList = Array(maxPoints * 3).fill(-100);
let sortedListMaxIndex = 0; // Head as the maximum value
let countItems = 0;
function sortedListInsert(in_itemValue, in_keyValue){
console.log("Inserting: ", in_keyValue);
if (countItems < maxPoints){
sortedList[countItems * 3 + 0] = in_keyValue;
sortedList[countItems * 3 + 1] = in_itemValue;
if (countItems > 0){
if(in_keyValue <= sortedList[sortedListMaxIndex * 3]){
let prev = sortedListMaxIndex;
let cur = sortedList[sortedListMaxIndex * 3 + 2];
while(cur != -1 && in_keyValue <= sortedList[cur * 3]){
console.log(prev, cur);
prev = cur;
cur = sortedList[prev * 3 + 2];
}
console.log(prev, cur, countItems);
sortedList[prev * 3 + 2] = countItems;
sortedList[countItems * 3 + 2] = cur;
}else{
sortedList[countItems * 3 + 2] = sortedListMaxIndex;
sortedListMaxIndex = countItems;
console.log("new max at: ", sortedListMaxIndex);
}
}else{
sortedList[countItems * 3 + 2] = -1;
}
++countItems;
}else{
console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxx");
if(in_keyValue <= sortedList[sortedListMaxIndex * 3]){
const saveLoc = sortedListMaxIndex;
let prev = sortedListMaxIndex;
let cur = sortedList[sortedListMaxIndex * 3 + 2];
while(cur != -1 && in_keyValue <= sortedList[cur * 3]){
console.log(prev, cur);
prev = cur;
cur = sortedList[prev * 3 + 2];
}
console.log(prev, cur, saveLoc);
sortedListMaxIndex = sortedList[sortedListMaxIndex * 3 + 2];
console.log("new max at: ", sortedListMaxIndex);
sortedList[prev * 3 + 2] = saveLoc;
sortedList[saveLoc * 3 + 0] = in_keyValue;
sortedList[saveLoc * 3 + 1] = in_itemValue;
sortedList[saveLoc * 3 + 2] = cur;
}else{
console.log("Too large. Value ignored.");
}
}
}
console.log(sortedList);
sortedListInsert(5,5);
console.log(sortedList);
sortedListInsert(6,6);
console.log(sortedList);
sortedListInsert(6,6);
console.log(sortedList);
sortedListInsert(2,2);
console.log(sortedList);
sortedListInsert(7,7);
console.log(sortedList);
sortedListInsert(2,2);
console.log(sortedList);
//--sorted list end
}
Insert cell
function kdNearestNeighborsInRange(in_array, in_elementSize, in_numDimensions, in_searchPoint, in_searchRadius, in_maxPoints){
if(in_array.length < 1){
return [];
}
const retVals = Array(in_maxPoints).fill(-1);
let foundCount = 0;
//--sorted list implementation => (keyValue, Value, Next) where Next is nearest smaller keyValue's Index
const sortedList = Array(in_maxPoints * 3).fill(-1).map((d,i) => (i % 3 == 2)? 1 + Math.floor(i/3) : -1);
let sortedListMaxIndex = 0; // Head as the maximum value
let nextLoc = 0;
function sortedListInsert(in_itemValue, in_keyValue){
let cur = sortedListMaxIndex;
let prev = -1;
while(in_keyValue <= sortedList[cur]){ // equal values are also stored
prev = cur;
cur = sortedList[cur + 2]; // next smallest value
}
sortedList[nextLoc] = in_keyValue;
sortedList[nextLoc + 1] = in_itemValue;
sortedList[nextLoc + 2] = cur;
sortedList[prev + 2] = nextLoc;
nextLoc = sortedList[nextLoc];
if(in_maxPoints <= nextLoc){
nextLoc = sortedListMaxIndex;
}
}
//--sorted list end
//--stack begin implementation
const stack = Array(in_array.length).fill(-1);
const stackElementSize = 2;
let stackPointer = -stackElementSize;
function stackPush(in_stackElementArray, in_startIndex){
// StackPush: copies the elements to stackArray and updates the stack pointer
stackPointer += stackElementSize;
for(let idx = 0; idx < stackElementSize; ++idx){
stack[stackPointer + idx] = in_stackElementArray[in_startIndex + idx];
}
}
function stackPop(){
// Pop: returns the index to the stack elements and updates the stack pointer.
// User must use the data before pushing to the stack again.
stackPointer -= stackElementSize;
return stackPointer + stackElementSize;
}
//--stack end
const numElements = in_array.length / in_elementSize;
const sqrSearchDist = in_searchRadius * in_searchRadius;
let foundMaxSqrDist = sqrSearchDist;
console.log("searching for: " + sqrSearchDist);
const retVal = [];
stackPush([0, 0], 0);
console.log("------------");
let count = 0;
while(0 <= stackPointer && stackPointer < in_array.length){
if(count++ > 10) break;
console.log("stackPointer: " + stackPointer + "\n stack: ", stack);
const stackIdx = stackPop();
const curElementIdx = stack[stackIdx];
const depth = stack[stackIdx + 1];
console.log("index: " + curElementIdx + " depth: " + depth);
console.log("currentElement: ", printElement(in_array, in_elementSize, curElementIdx));
const arrayIndex = getElementIndex(in_elementSize, curElementIdx);
const curSqrDist = calcSquaredDistance(in_numDimensions, in_array, arrayIndex, in_searchPoint, 0);
console.log("dist to point: " + curSqrDist);
if(curSqrDist <= sqrSearchDist){
console.log("Found: ", printElement(in_array, in_elementSize, curElementIdx));
retVals[foundCount] = curElementIdx;
++foundCount;
if(foundCount > in_maxPoints){
//break;
}
}
const dim = depth % in_numDimensions;
console.log("dimension: " + dim);
let goodSide = -1;
let badSide = -1;
if(in_searchPoint[dim] < in_array[curElementIdx + dim]){
console.log("less than cur " + in_searchPoint[dim] + " < " + in_array[curElementIdx + dim]);
goodSide = in_array[getLeftIndex(in_elementSize, curElementIdx, dim)];
badSide = in_array[getRightIndex(in_elementSize, curElementIdx, dim)];
}else{
console.log("greater than cur " + in_searchPoint[dim] + " >= " + in_array[curElementIdx + dim]);
goodSide = in_array[getRightIndex(in_elementSize, curElementIdx, dim)];
badSide = in_array[getLeftIndex(in_elementSize, curElementIdx, dim)];
}
console.log("goodSide: " + goodSide + " badSide: " + badSide);
if(badSide != -1){
const sqrBadSideDist = (in_searchPoint[dim] - in_array[curElementIdx + dim]) * (in_searchPoint[dim] - in_array[curElementIdx + dim]);
console.log("Bad Side check : " + sqrBadSideDist + " < " + sqrSearchDist + " ? ")
if(sqrBadSideDist < sqrSearchDist){
console.log("pushing bad side");
stackPush([badSide, depth + 1], 0);
}
}
if(goodSide != -1){
console.log("pushing good side");
stackPush([goodSide,depth + 1], 0);
}
}
return retVals;
}
Insert cell
function calcSquaredDistance(in_numDimensions, in_arrayA, in_startIndexA, in_arrayB, in_startIndexB){
let sqrSum = 0;
for(let dim = 0; dim < in_numDimensions; ++dim){
const pdim = in_arrayA[in_startIndexA + dim];
const qdim = in_arrayB[in_startIndexB + dim];
sqrSum += (pdim - qdim) * (pdim - qdim);
}
return sqrSum;
}
Insert cell
function generateKdTreeFromArray(in_array, in_elementSize, in_numDimensions){
if(in_array.length < 1){
return [];
}
const numElements = in_array.length / in_elementSize;
// As root is at index 0, continue setting up the rest starting from index 1
for(let newItmIdx = 1; newItmIdx < numElements; ++newItmIdx){
let depth = 0;
let curIdx = 0;
while(curIdx < numElements){
const curDim = depth % in_numDimensions;
if(in_array[getKeyIndex(in_elementSize, newItmIdx, curDim)] < in_array[getKeyIndex(in_elementSize, curIdx, curDim)]){
const leftIdx = getLeftIndex(in_elementSize, curIdx);
if(in_array[leftIdx] == -1){
in_array[leftIdx] = newItmIdx;
break;
}
curIdx = in_array[leftIdx];
}else{
const rightIdx = getRightIndex(in_elementSize, curIdx);
if(in_array[rightIdx] == -1){
in_array[rightIdx] = newItmIdx;
break;
}
curIdx = in_array[rightIdx];
}
++depth;
}
}
return in_array;
}
Insert cell
Insert cell
Insert cell
rectangleByTriangles_3fv_Positions = new Float32Array(
[-1, -1, 0,
1, -1, 0,
-1, 1, 0,
-1, 1, 0,
1, -1, 0,
1, 1, 0]
)
Insert cell
rectangleByTriangles_2fv_TexCoords = new Float32Array(
[0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1]
)
Insert cell
distinct_colors_3fv = new Uint8Array(
[255, 0, 0,
0, 255, 0,
0, 0, 255,
128,128,128,
255, 255, 0,
255, 0, 255,
0, 255, 255,
192,192,192
]
)
Insert cell
twgl = require("twgl.js")
Insert cell
import {slider} from '@bartok32/diy-inputs'
Insert cell
import {checkbox} from "@jashkenas/inputs"
Insert cell
columns = (args) => {
const form = html`<form></form>`
form.value = {}

let cols = 0
for (const key in args) {
form.appendChild(args[key])
cols++
}
form.style = `display: grid; grid-gap: 10px 15px; grid-template-columns: repeat(${cols}, auto); grid-auto-flow: row;`
form.oninput = () => {
form.value = Object.keys(args).reduce((result, key) => {
result[key] = args[key].value
return result
}, Array.isArray(args) ? [] : {})
}
form.oninput()
return form
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more