gpgpu on point geom RT3
gpgpu on point geometry RT3
by tfoller
HTML
<div id="grid">
<canvas id="main_canvas" style="border: solid 1px red;" width=400 height=400>
</canvas>
<div id="stats_holder"></div>
<div>
<label id="label" style="padding-right:10px;"></label>
<button id="btn">play</button>
</div>
</div>
<script type="x-shader/x-vertex" id="gpgpu_vs">
#version 100
attribute vec3 position;
attribute vec3 data;
#ifdef GL_FRAGMENT_PRECISION_HIGH
uniform highp float point_size;
#else
uniform mediump float point_size;
#endif
varying vec3 rft;
void main() {
gl_PointSize = point_size;
gl_Position = vec4(position, 1.0);
rft = data;
}
</script>
<script type='x-shader/x-fragment' id='gpgpu_fs'>
#version 100
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
float digits = 5.0; // digits to extract
#else
float digits = 4.0;
#endif
vec4 floatToVec4(float f) { // [8 bits exp] [sign + 23 fract bits]
float sign = 0.0;
if(f < 0.0) { sign = 128.0; f = -f;}
const float ln10 = log(10.0);
float exp = f > 0.0 ? ceil(log(f)/ln10) : 0.0;
exp = digits - exp;
float f24 = f * pow(10.0, exp);
f24 /= 256.0 * 256.0;
float byte2 = floor(f24);
float b10 = (f24 - byte2) * 256.0;
float byte1 = floor(b10);
float byte0 = (b10 - byte1) * 256.0;
return vec4(exp, byte2 + sign, byte1, byte0)/255.0;
}
uniform float time;
uniform float point_size;
uniform float hd;
varying vec3 rft;
void main() {
float r = rft[0];
float fi = rft[1] + time;
float th = rft[2] + time;
int ind = int(point_size * ((gl_PointCoord.t - hd) * point_size + (gl_PointCoord.s - hd)));
float rsinth = r * sin(th);
if(ind == 0) {
float x = rsinth * cos(fi);
gl_FragColor = floatToVec4(x);
}
else if(ind == 1) {
float y = rsinth * sin(fi);
gl_FragColor = floatToVec4(y);
}
else if(ind == 2) {
float z = r * cos(th);
gl_FragColor = floatToVec4(z);
...
CSS
body {
background: #0c2733;
}
#grid {
display: grid;
grid-template-columns: min-content min-content;
grid-template-rows: min-content min-content;
gap: 20px;
}
JavaScript
import * as THREE from "https://threejs.org/build/three.module.js";
import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
let stats;
import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';
const init_stats = () => {
stats = new Stats();
var elem = stats.dom;
elem.setAttribute('id', 'stats');
elem.style.position = null;
const cont = document.getElementById('stats_holder');
cont.insertBefore(elem, cont.children[0]);
};
/* parallel computing on point primitives WebGL 1.0
* input - array of numbers
* sizein - number of input values in the array needed for one computaion, [1...4]
* sizeout - number of output values produced from each input
*
* example:
* convert 10 [r, fi, th] values from spherical coord sys to Cartesian [x, y, z]
* input = [r0, fi0, th0, r1, fi1, th1, ..., r9, fi9, th9]
* sizein = 3; sizeout = 3;
* output = [x0(float in RGBA), y0, z0, ..., x9, y9, z9]
*/
function GpGpuWebGL1(renderer, input, sizein, sizeout) {
const point_size = Math.ceil(Math.sqrt(sizeout));
const attr_size = sizein;
const inp_len = input.length;
const threads = Math.ceil(inp_len / attr_size); // number of vertices
// zero pad input array
const pad_len = threads * attr_size - inp_len;
input.length += pad_len;
for (let i = inp_len; i < input.length; i++) input[i] = 0;
// canvas size
const blocks = Math.ceil(Math.sqrt(threads));
const tw = point_size * blocks;
const th = tw;
this.tw = tw;
this.th = th;
console.log(`GpGpuWebGL1 init\n
inp_len: ${inp_len}, sizein: ${sizein}, pad_len: ${input.length}, \n
point: ${point_size} x ${point_size}, attr_size: ${attr_size}, \n
threads: ${threads}, blocks: ${blocks}, texture: ${tw} x ${th}`);
this.camera = new THREE.OrthographicCamera(-tw / 2, tw / 2, th / 2, -th / 2, 0, 1);
this.scene = new THREE.Scene();
this.renderer = renderer;
this.renderTarget = new THREE.WebGLRenderTarget(tw, th);
this.mat = new...