Skip to content

Commit

Permalink
Refactored GPUParticleSystem. Improved FP Texture detection
Browse files Browse the repository at this point in the history
  • Loading branch information
ramirofages committed Dec 4, 2022
1 parent 7b70127 commit 648bfaa
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v7.0.1
v7.1.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ohzi-core",
"version": "7.0.1",
"version": "7.1.0",
"description": "OHZI Core Library",
"source": "src/index.js",
"module": "build/index.module.js",
Expand Down
55 changes: 54 additions & 1 deletion src/Graphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import Capabilities from './Capabilities';
import DepthAndNormalsRenderer from './render_utilities/DepthAndNormalsRenderer';
import Blitter from './render_utilities/Blitter';

import {
NearestFilter,
RGBAFormat,
LinearEncoding,
FloatType,
WebGLRenderTarget,
ShaderMaterial,
NoBlending,
AlwaysDepth
} from 'three';

import { WebGLRenderer } from 'three';

class Graphics
Expand Down Expand Up @@ -87,7 +98,7 @@ class Graphics
this.current_render_mode = this.no_render;
Capabilities.max_anisotropy = this._renderer.capabilities.getMaxAnisotropy();
Capabilities.vertex_texture_sampler_available = this._renderer.capabilities.maxVertexTextures > 0;
Capabilities.fp_textures_available = this._renderer.capabilities.floatVertexTextures;
Capabilities.fp_textures_available = this.is_floating_point_texture_available();

this.generateDepthNormalTexture = false;

Expand Down Expand Up @@ -332,6 +343,48 @@ class Graphics
this.current_render_mode.dispose();
this.blitter.dispose();
}

is_floating_point_texture_available()
{
const RT = new WebGLRenderTarget(1, 1, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
encoding: LinearEncoding,
type: FloatType,
stencilBuffer: false,
depthBuffer: false
});

const vert = `
void main()
{
gl_Position = vec4(uv * 2.0 - 1.0, 1.0, 1.0);
}
`;
const frag = `
void main()
{
gl_FragColor = vec4(0.0, 4865.35, 0.0, 1.0);
}
`;

const mat = new ShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
depthWrite: false,
blending: NoBlending,
depthTest: false,
depthFunc: AlwaysDepth
});

this.material_pass(mat, RT);

const output = new Float32Array(4);
this._renderer.readRenderTargetPixels(RT, 0, 0, 1, 1, output);

return Math.abs(output[1] - 4865.35) < 0.001;
}
}

export default new Graphics();
116 changes: 69 additions & 47 deletions src/gpu_particles/GPUParticleSystem.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,109 @@
import { Object3D } from 'three';
import { BufferAttribute } from 'three';
import { PlaneGeometry } from 'three';
import { Mesh } from 'three';
import { BufferGeometry } from 'three';
import { Scene } from 'three';
import { Points } from 'three';
import { InstancedBufferGeometry } from 'three';
import { InstancedBufferAttribute } from 'three';
import ParticleAttribute from './ParticleAttribute';

export default class GPUParticleSystem extends Object3D
{
constructor()
constructor(particle_count, material)
{
super();
this.attributes = [];

this.particles = undefined;
}

set_from_geometry(geometry, material, init_attribute_uvs)
{
// let position = new ParticlePositionAttribute("_Position");
if (init_attribute_uvs && geometry.getAttribute('storage_uv') === undefined)
{
const uv_storage_attr = this.build_uv_storage_attribute(geometry.getAttribute('position').count);
geometry.setAttribute('storage_uv', uv_storage_attr);
}
// position.init_from_geometry(geometry);
// this.attributes.push(position);
this.mesh = this.build_point_mesh(particle_count, material);
this.add(this.mesh);

// material.uniforms._Position.value = position.read.texture;

const points = new Points(geometry, material);
points.frustumCulled = false;
this.particles = points;

this.add(points);
this.attribute_writter_mesh = this.build_attribute_writter_mesh(particle_count);
this.attribute_writter_scene = new Scene();
this.attribute_writter_scene.add(this.attribute_writter_mesh);
}

add_texture_attribute(buffer_attribute)
{

this.attributes.push(buffer_attribute);
}

add_attribute(name, buffer_attribute)
add_update_attribute_array(name, array, item_size)
{
this.attribute_writter_mesh.geometry.setAttribute(name, new BufferAttribute(array, item_size, false));
}

add_attribute_array(name, array, item_size)
{
this.mesh.geometry.setAttribute(name, new InstancedBufferAttribute(array, item_size, false));
}

build_uv_storage_attribute(particle_count)
update()
{
const resolution = this.calculate_resolution(particle_count);
const uvs = new Float32Array(particle_count * 2);
for (let i = 0, j = 0; i < particle_count * 2; i += 2, j++)
this.mesh.material.update();
for (let i = 0; i < this.attributes.length; i++)
{
uvs[i] = ((j % resolution) / resolution) + (0.5 / resolution);
uvs[i + 1] = (Math.floor(j / resolution) / resolution) + (0.5 / resolution);
this.attributes[i].update(this.attribute_writter_scene);
}

return new BufferAttribute(uvs, 2);
}

calculate_resolution(particle_count)
dispose()
{
return Math.ceil(Math.sqrt(particle_count));
this.mesh.geometry.dispose();
this.mesh.material.dispose();
}

update()
build_point_mesh(instance_count = 1, material)
{
for (let i = 0; i < this.attributes.length; i++)
{
this.attributes[i].update();
}
const geo = new PlaneGeometry();

const instanced_geo = new InstancedBufferGeometry();
instanced_geo.setAttribute('position', geo.getAttribute('position'));
instanced_geo.index = geo.index;

instanced_geo.setAttribute('storage_uv', this.build_uv_storage_attribute(instance_count));
instanced_geo.instanceCount = instance_count;
const mesh = new Mesh(instanced_geo, material);
mesh.frustumCulled = false;
return mesh;
}

set_attribute_update_material(attribute_name, mat)
build_attribute_writter_mesh(particle_count)
{
for (let i = 0; i < this.attributes.length; i++)
const { width, height } = ParticleAttribute.calculate_resolution(particle_count);

const uvs = new Float32Array(particle_count * 3);

for (let i = 0; i < particle_count; i++)
{
if (this.attributes[i].name === attribute_name)
{
this.attributes[i].update_material = mat;
}
const x = i % width;
const y = Math.floor(i / width);
uvs[i * 3 + 0] = (x + 0.5) / width;
uvs[i * 3 + 1] = (y + 0.5) / height;
uvs[i * 3 + 2] = i;
}
const geo = new BufferGeometry();
geo.setAttribute('position', new BufferAttribute(uvs, 3, false));

const points = new Points(geo);
points.frustumCulled = false;
return points;
}

dispose()
build_uv_storage_attribute(particle_count)
{
this.particles.geometry.dispose();
this.particles.material.dispose();
const { width, height } = ParticleAttribute.calculate_resolution(particle_count);

const uvs = new Float32Array(particle_count * 2);

for (let i = 0; i < particle_count; i++)
{
const x = i % width;
const y = Math.floor(i / width);
uvs[i * 2 + 0] = (x + 0.5) / width;
uvs[i * 2 + 1] = (y + 0.5) / height;
}
return new InstancedBufferAttribute(uvs, 2, false);
}
}
51 changes: 30 additions & 21 deletions src/gpu_particles/ParticleAttribute.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import Graphics from '../Graphics';

import { Scene } from 'three';
import { NearestFilter } from 'three';
import { RGBAFormat } from 'three';
import { LinearEncoding } from 'three';
import { HalfFloatType } from 'three';
import { FloatType } from 'three';
import { WebGLRenderTarget } from 'three';
import { Points } from 'three';
import Capabilities from '../Capabilities';

export default class ParticleAttribute
{
constructor(attr_name)
constructor(attr_name, update_material)
{
this.read = undefined;
this.write = undefined;

this.name = attr_name;

this.update_material = undefined;

this.update_scene = new Scene();
this.update_material = update_material;
}

init_from_geometry(geometry)
Expand All @@ -35,23 +32,25 @@ export default class ParticleAttribute

build_RT(particle_count)
{
const resolution = this.calculate_resolution(particle_count);
const resolution = ParticleAttribute.calculate_resolution(particle_count);
const options = {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
encoding: LinearEncoding,
type: (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) ? HalfFloatType : FloatType,
type: Capabilities.fp_textures_available ? FloatType : HalfFloatType,
stencilBuffer: false,
depthBuffer: false
};

return new WebGLRenderTarget(resolution, resolution, options);
return new WebGLRenderTarget(resolution.width, resolution.height, options);
}

calculate_resolution(particle_count)
static calculate_resolution(particle_count)
{
return Math.ceil(Math.sqrt(particle_count));
const width = Math.min(particle_count, 512);
const height = Math.max(1, Math.ceil(particle_count / width));
return { width, height };
}

swap_RT()
Expand All @@ -61,23 +60,33 @@ export default class ParticleAttribute
this.write = tmp;
}

update()
update(attribute_writter_scene)
{
if (this.update_material)
{
Graphics.blit(this.read, this.write, this.update_material);
this.update_material.update();
attribute_writter_scene.children[0].material = this.update_material;
this.update_material.uniforms._MainTex.value = this.read.texture;
Graphics.render(attribute_writter_scene, undefined, this.write);
attribute_writter_scene.children[0].material = undefined;

this.swap_RT();
}
}

render_geometry_to_RT(geometry, material, RT)
store_geometry_attribute_in_RT(attribute, RT, storage_material, attribute_writter_scene)
{
attribute_writter_scene.children[0].geometry.setAttribute('data', attribute);
attribute_writter_scene.children[0].material = storage_material;

Graphics.render(attribute_writter_scene, undefined, RT);

attribute_writter_scene.children[0].geometry.deleteAttribute('data');
attribute_writter_scene.children[0].material = undefined;
}

get_texture()
{
const points = new Points(geometry, material);
points.frustumCulled = false;
// let scene = new Scene();
// scene.add( points );
this.update_scene.add(points);
Graphics.render(this.update_scene, undefined, RT);
// Graphics.render(scene, undefined, RT);
return this.read.texture;
}
}
32 changes: 26 additions & 6 deletions src/gpu_particles/ParticlePositionAttribute.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
import { BufferAttribute } from 'three';

import ParticleAttribute from '../gpu_particles/ParticleAttribute';
import PositionStorageMaterial from '../materials/gpu_particles/PositionStorageMaterial';
import ParticleStorageMaterial from '../materials/gpu_particles/ParticleStorageMaterial';

export default class ParticlePositionAttribute extends ParticleAttribute
{
constructor()
constructor(update_material)
{
super('_Position');
super('_Position', update_material);
}

init_from_geometry(geometry)
store_geometry(geometry, attribute_writter_scene)
{
const pos_attr = geometry.getAttribute('position');
this.read = this.build_RT(pos_attr.count);
this.write = this.build_RT(pos_attr.count);

const mat = new PositionStorageMaterial();
this.render_geometry_to_RT(geometry, mat, this.read);
const mat = new ParticleStorageMaterial();
this.store_geometry_attribute_in_RT(geometry.getAttribute('position'), this.read, mat, attribute_writter_scene);
}

store_positions(positions, attribute_writter_scene)
{
this.read = this.build_RT(positions.length);
this.write = this.build_RT(positions.length);

const arr = new Float32Array(positions.length * 4);

for (let i = 0; i < positions.length; i++)
{
arr[i * 4 + 0] = positions[i].x;
arr[i * 4 + 1] = positions[i].y;
arr[i * 4 + 2] = positions[i].z;
arr[i * 4 + 3] = (Math.random() * 2 - 1) * 10;
}
const mat = new ParticleStorageMaterial();
this.store_geometry_attribute_in_RT(new BufferAttribute(arr, 4), this.read, mat, attribute_writter_scene);
}
}
Loading

0 comments on commit 648bfaa

Please sign in to comment.