加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
webgl_gpgpu_protoplanet.js 13.61 KB
一键复制 编辑 原始数据 按行查看 历史
zhangjin 提交于 2024-06-25 18:11 . .
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
import * as THREE from 'three';
const {
performance,
document,
window,
HTMLCanvasElement,
requestAnimationFrame,
cancelAnimationFrame,
core,
Event,
Event0
} = THREE .DHTML
import Stats from 'three/examples/jsm/libs/stats.module.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { OrbitControls0 } from 'three/examples/jsm/controls/OrbitControls0.js';
import { GPUComputationRenderer } from 'three/examples/jsm/misc/GPUComputationRenderer.js';
const computeShaderPosition = `
#define delta ( 1.0 / 60.0 )
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 tmpPos = texture2D( texturePosition, uv );
vec3 pos = tmpPos.xyz;
vec4 tmpVel = texture2D( textureVelocity, uv );
vec3 vel = tmpVel.xyz;
float mass = tmpVel.w;
if ( mass == 0.0 ) {
vel = vec3( 0.0 );
}
// Dynamics
pos += vel * delta;
gl_FragColor = vec4( pos, 1.0 );
}
`
const computeShaderVelocity = `
// For PI declaration:
#include <common>
#define delta ( 1.0 / 60.0 )
uniform float gravityConstant;
uniform float density;
const float width = resolution.x;
const float height = resolution.y;
float radiusFromMass( float mass ) {
// Calculate radius of a sphere from mass and density
return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
}
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
float idParticle = uv.y * resolution.x + uv.x;
vec4 tmpPos = texture2D( texturePosition, uv );
vec3 pos = tmpPos.xyz;
vec4 tmpVel = texture2D( textureVelocity, uv );
vec3 vel = tmpVel.xyz;
float mass = tmpVel.w;
if ( mass > 0.0 ) {
float radius = radiusFromMass( mass );
vec3 acceleration = vec3( 0.0 );
// Gravity interaction
for ( float y = 0.0; y < height; y++ ) {
for ( float x = 0.0; x < width; x++ ) {
vec2 secondParticleCoords = vec2( x + 0.5, y + 0.5 ) / resolution.xy;
vec3 pos2 = texture2D( texturePosition, secondParticleCoords ).xyz;
vec4 velTemp2 = texture2D( textureVelocity, secondParticleCoords );
vec3 vel2 = velTemp2.xyz;
float mass2 = velTemp2.w;
float idParticle2 = secondParticleCoords.y * resolution.x + secondParticleCoords.x;
if ( idParticle == idParticle2 ) {
continue;
}
if ( mass2 == 0.0 ) {
continue;
}
vec3 dPos = pos2 - pos;
float distance = length( dPos );
float radius2 = radiusFromMass( mass2 );
if ( distance == 0.0 ) {
continue;
}
// Checks collision
if ( distance < radius + radius2 ) {
if ( idParticle < idParticle2 ) {
// This particle is aggregated by the other
vel = ( vel * mass + vel2 * mass2 ) / ( mass + mass2 );
mass += mass2;
radius = radiusFromMass( mass );
}
else {
// This particle dies
mass = 0.0;
radius = 0.0;
vel = vec3( 0.0 );
break;
}
}
float distanceSq = distance * distance;
float gravityField = gravityConstant * mass2 / distanceSq;
gravityField = min( gravityField, 1000.0 );
acceleration += gravityField * normalize( dPos );
}
if ( mass == 0.0 ) {
break;
}
}
// Dynamics
vel += delta * acceleration;
}
gl_FragColor = vec4( vel, mass );
}`
const particleVertexShader = `
// For PI declaration:
#include <common>
uniform sampler2D texturePosition;
uniform sampler2D textureVelocity;
uniform float cameraConstant;
uniform float density;
varying vec4 vColor;
float radiusFromMass( float mass ) {
// Calculate radius of a sphere from mass and density
return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
}
void main() {
vec4 posTemp = texture2D( texturePosition, uv );
vec3 pos = posTemp.xyz;
vec4 velTemp = texture2D( textureVelocity, uv );
vec3 vel = velTemp.xyz;
float mass = velTemp.w;
vColor = vec4( 1.0, mass / 250.0, 0.0, 1.0 );
vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
// Calculate radius of a sphere from mass and density
//float radius = pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
float radius = radiusFromMass( mass );
// Apparent size in pixels
if ( mass == 0.0 ) {
gl_PointSize = 0.0;
}
else {
gl_PointSize = radius * cameraConstant / ( - mvPosition.z );
}
gl_Position = projectionMatrix * mvPosition;
}`
const particleFragmentShader = `
varying vec4 vColor;
void main() {
if ( vColor.y == 0.0 ) discard;
float f = length( gl_PointCoord - vec2( 0.5, 0.5 ) );
if ( f > 0.5 ) {
discard;
}
gl_FragColor = vColor;
}
`
var requestId
Page({
onUnload() {
cancelAnimationFrame(requestId, this.canvas)
this.worker && this.worker.terminate()
if(this.canvas) this.canvas = null
setTimeout(() => {
if (this.renderer instanceof THREE.WebGLRenderer) {
this.renderer.dispose()
this.renderer.forceContextLoss()
this.renderer.context = null
this.renderer.domElement = null
this.renderer = null
}
}, 10)
},
webgl_touch(e){
const web_e = (window.platform=="devtools"?Event:Event0).fix(e)
this.canvas.dispatchEvent(web_e)
},
onLoad() {
document.createElementAsync("canvas", "webgl2",this).then(canvas => {
this.canvas = canvas
this.body_load(canvas).then()
})
},
async body_load(canvas3d) {
// Texture width for simulation (each texel is a debris particle)
const WIDTH = 64;
let container, stats;
let camera, scene, renderer, geometry;
const PARTICLES = WIDTH * WIDTH;
let gpuCompute;
let velocityVariable;
let positionVariable;
let velocityUniforms;
let particleUniforms;
let effectController;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 5, 15000 );
camera.position.y = 120;
camera.position.z = 400;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
const controls = new (window.platform=="devtools"?OrbitControls:OrbitControls0)( camera, renderer.domElement );
controls.minDistance = 100;
controls.maxDistance = 1000;
effectController = {
// Can be changed dynamically
gravityConstant: 100.0,
density: 0.45,
// Must restart simulation
radius: 300,
height: 8,
exponent: 0.4,
maxMass: 15.0,
velocity: 70,
velocityExponent: 0.2,
randVelocity: 0.001
};
initComputeRenderer();
stats = new Stats();
container.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize );
initGUI();
initProtoplanets();
dynamicValuesChanger();
}
function initComputeRenderer() {
gpuCompute = new GPUComputationRenderer( WIDTH, WIDTH, renderer );
if ( renderer.capabilities.isWebGL2 === false ) {
gpuCompute.setDataType( THREE.HalfFloatType );
}
const dtPosition = gpuCompute.createTexture();
const dtVelocity = gpuCompute.createTexture();
fillTextures( dtPosition, dtVelocity );
velocityVariable = gpuCompute.addVariable( 'textureVelocity',computeShaderVelocity, dtVelocity );
positionVariable = gpuCompute.addVariable( 'texturePosition',computeShaderPosition, dtPosition );
gpuCompute.setVariableDependencies( velocityVariable, [ positionVariable, velocityVariable ] );
gpuCompute.setVariableDependencies( positionVariable, [ positionVariable, velocityVariable ] );
velocityUniforms = velocityVariable.material.uniforms;
velocityUniforms[ 'gravityConstant' ] = { value: 0.0 };
velocityUniforms[ 'density' ] = { value: 0.0 };
const error = gpuCompute.init();
if ( error !== null ) {
console.error( error );
}
}
function restartSimulation() {
const dtPosition = gpuCompute.createTexture();
const dtVelocity = gpuCompute.createTexture();
fillTextures( dtPosition, dtVelocity );
gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 0 ] );
gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 1 ] );
gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 0 ] );
gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 1 ] );
}
function initProtoplanets() {
geometry = new THREE.BufferGeometry();
const positions = new Float32Array( PARTICLES * 3 );
let p = 0;
for ( let i = 0; i < PARTICLES; i ++ ) {
positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
positions[ p ++ ] = 0; //( Math.random() * 2 - 1 ) * effectController.radius;
positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
}
const uvs = new Float32Array( PARTICLES * 2 );
p = 0;
for ( let j = 0; j < WIDTH; j ++ ) {
for ( let i = 0; i < WIDTH; i ++ ) {
uvs[ p ++ ] = i / ( WIDTH - 1 );
uvs[ p ++ ] = j / ( WIDTH - 1 );
}
}
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
particleUniforms = {
'texturePosition': { value: null },
'textureVelocity': { value: null },
'cameraConstant': { value: getCameraConstant( camera ) },
'density': { value: 0.0 }
};
// THREE.ShaderMaterial
const material = new THREE.ShaderMaterial( {
uniforms: particleUniforms,
vertexShader:particleVertexShader,
fragmentShader:particleFragmentShader
} );
material.extensions.drawBuffers = true;
const particles = new THREE.Points( geometry, material );
particles.matrixAutoUpdate = false;
particles.updateMatrix();
scene.add( particles );
}
function fillTextures( texturePosition, textureVelocity ) {
const posArray = texturePosition.image.data;
const velArray = textureVelocity.image.data;
const radius = effectController.radius;
const height = effectController.height;
const exponent = effectController.exponent;
const maxMass = effectController.maxMass * 1024 / PARTICLES;
const maxVel = effectController.velocity;
const velExponent = effectController.velocityExponent;
const randVel = effectController.randVelocity;
for ( let k = 0, kl = posArray.length; k < kl; k += 4 ) {
// Position
let x, z, rr;
do {
x = ( Math.random() * 2 - 1 );
z = ( Math.random() * 2 - 1 );
rr = x * x + z * z;
} while ( rr > 1 );
rr = Math.sqrt( rr );
const rExp = radius * Math.pow( rr, exponent );
// Velocity
const vel = maxVel * Math.pow( rr, velExponent );
const vx = vel * z + ( Math.random() * 2 - 1 ) * randVel;
const vy = ( Math.random() * 2 - 1 ) * randVel * 0.05;
const vz = - vel * x + ( Math.random() * 2 - 1 ) * randVel;
x *= rExp;
z *= rExp;
const y = ( Math.random() * 2 - 1 ) * height;
const mass = Math.random() * maxMass + 1;
// Fill in texture values
posArray[ k + 0 ] = x;
posArray[ k + 1 ] = y;
posArray[ k + 2 ] = z;
posArray[ k + 3 ] = 1;
velArray[ k + 0 ] = vx;
velArray[ k + 1 ] = vy;
velArray[ k + 2 ] = vz;
velArray[ k + 3 ] = mass;
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
particleUniforms[ 'cameraConstant' ].value = getCameraConstant( camera );
}
function dynamicValuesChanger() {
velocityUniforms[ 'gravityConstant' ].value = effectController.gravityConstant;
velocityUniforms[ 'density' ].value = effectController.density;
particleUniforms[ 'density' ].value = effectController.density;
}
function initGUI() {
const gui = new GUI( { width: 280 } );
const folder1 = gui.addFolder( 'Dynamic parameters' );
folder1.add( effectController, 'gravityConstant', 0.0, 1000.0, 0.05 ).onChange( dynamicValuesChanger );
folder1.add( effectController, 'density', 0.0, 10.0, 0.001 ).onChange( dynamicValuesChanger );
const folder2 = gui.addFolder( 'Static parameters' );
folder2.add( effectController, 'radius', 10.0, 1000.0, 1.0 );
folder2.add( effectController, 'height', 0.0, 50.0, 0.01 );
folder2.add( effectController, 'exponent', 0.0, 2.0, 0.001 );
folder2.add( effectController, 'maxMass', 1.0, 50.0, 0.1 );
folder2.add( effectController, 'velocity', 0.0, 150.0, 0.1 );
folder2.add( effectController, 'velocityExponent', 0.0, 1.0, 0.01 );
folder2.add( effectController, 'randVelocity', 0.0, 50.0, 0.1 );
const buttonRestart = {
restartSimulation: function () {
restartSimulation();
}
};
folder2.add( buttonRestart, 'restartSimulation' );
folder1.open();
folder2.open();
}
function getCameraConstant( camera ) {
return window.innerHeight / ( Math.tan( THREE.MathUtils.DEG2RAD * 0.5 * camera.fov ) / camera.zoom );
}
function animate() {
requestId = requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
gpuCompute.compute();
particleUniforms[ 'texturePosition' ].value = gpuCompute.getCurrentRenderTarget( positionVariable ).texture;
particleUniforms[ 'textureVelocity' ].value = gpuCompute.getCurrentRenderTarget( velocityVariable ).texture;
renderer.render( scene, camera );
}
}
})
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化