@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
128 lines (95 loc) • 5.9 kB
Markdown
This tech is based on popular Megatexture technique developed for Rage by John Carmack, working at id software.
The key idea boils down to having a very large texture that is impractical to keep in memory, and only load and keep in memory pieces of it that are relevant to the current view.
There are 2 parts to this technique, offline and online.
The original texture is mip-mapped, down to the size of a single tile, for the sake of this document let it be 128x128 pixels. Each mip level is then cut into tiles and stored on disk. A good intuition for this is to think of each tile as a separate pixel, representing non-color data.
This is achieved by pre-rendering the scene with a special shader that records UV and mip-level information, which we call `Usage`, this is then analysed to build a list of used tiles (mip level, x, y) and by counting occurrence of this tile in the `Usage` texture we get a much more useful data structure.
Based on the `Usage` data, we populate our `Physical` texture of pages, and based on what's currently in the `Physical` texture - we populate the `Reference` texture that stores information about the entire mip pyramid, and for each tile contains a pointer to the `Physical` texture, where representative tile can be found.
## Considerations
### Filtering
Most papers suggest introducing a border into each tile of ~4 pixels.
4 pixel border on a 128x128 pixel tile is going to take up 12.1% of space, which is quite a lot.
We can do filtering in software, that is - sample individual pixels and perform interpolation inside the shader.
### Speeding up WebGL read-back
From [Babylon](https://github.com/BabylonJS/Babylon.js/blob/90dbafed8de9de752cdc399604f9c7c51116d630/src/Engines/engine.ts#L1772)
```ts
public _readPixelsAsync(x: number, y: number, w: number, h: number, format: number, type: number, outputBuffer: ArrayBufferView) {
if (this._webGLVersion < 2) {
throw new Error("_readPixelsAsync only work on WebGL2+");
}
let gl = <WebGL2RenderingContext>(this._gl as any);
const buf = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
gl.bufferData(gl.PIXEL_PACK_BUFFER, outputBuffer.byteLength, gl.STREAM_READ);
gl.readPixels(x, y, w, h, format, type, 0);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
if (!sync) {
return null;
}
gl.flush();
return this._clientWaitAsync(sync, 0, 10).then(() => {
gl.deleteSync(sync);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, outputBuffer);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
gl.deleteBuffer(buf);
return outputBuffer;
});
}
private _clientWaitAsync(sync: WebGLSync, flags = 0, intervalms = 10): Promise<void> {
const gl = <WebGL2RenderingContext>(this._gl as any);
return new Promise((resolve, reject) => {
const check = () => {
const res = gl.clientWaitSync(sync, flags, 0);
if (res == gl.WAIT_FAILED) {
reject();
return;
}
if (res == gl.TIMEOUT_EXPIRED) {
setTimeout(check, intervalms);
return;
}
resolve();
};
check();
});
}
```
more links:
https://forum.babylonjs.com/t/speeding-up-readpixels/12739
* [Shadertoy: Anisotropic filtering in a shader](https://www.shadertoy.com/view/4lXfzn)
* ["Adaptive Virtual Texture Rendering in Far Cry 4" by Ka Chen, Ubisoft. 2015](https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2015/presentations/Chen_Ka_AdaptiveVirtualTexture.pdf)
* "id Tech 5 Challenges: From Texture Virtualization to Massive Parallelization" by J.M.P. van Waveren, id Software, SIGGRAPH 2009
* "Using Virtual Texturing to Handle Massive Texture Data" by J.M.P. van Waveren, id Software, 2010
* "Software Virtual Textures" by J.M.P. van Waveren, id Software, 2012
* "Advanced Virtual Texture Topics" by Matrin Mittring, Crytek GmbH, SIGGRAPH 2008
* "Atlas Shrugged: Device-agnostic Radiance Megatextures" by Mark Magro et al, University of Malta, VISIGRAPP 2020
* ["Sparse virtual textures" by Nathan Gauër, 2022](https://studiopixl.com/2022-04-27/sparse-virtual-textures)
* "Terrain in Battlefield 3: A modern, complete and scalable system" by Mattias Widmark, EA Digital Illusions (DICE), GDC 2012
* "Virtual Texturing in Software and Hardware" by Juraj Obert (AMD) et al, SIGGRAPH 2012
* "Virtual Texturing" by Albert Julian Mayer, 2010
## WebGL fails
WebGL doesn't seem to support mipmaps on integer textures
## Anisotropic filtering:
```glsl
// R is viewport resolution
// p is UV
vec4 textureAniso(sampler2D T, vec2 R, vec2 p) {
mat2 J = inverse(mat2(dFdx(p),dFdy(p))); // dFdxy: pixel footprint in texture space
J = transpose(J)*J; // quadratic form
float d = determinant(J), t = J[0][0]+J[1][1], // find ellipse: eigenvalues, max eigenvector
D = sqrt(abs(t*t-4.*d)), // abs() fix a bug: in weird view angles 0 can be slightly negative
V = (t-D)/2., v = (t+D)/2., // eigenvalues. ( ATTENTION: not sorted )
M = 1./sqrt(V), m = 1./sqrt(v), l =log2(m*R.y); // = 1./radii^2
//if (M/m>16.) l = log2(M/16.*R.y); // optional
vec2 A = M * normalize(vec2( -J[0][1] , J[0][0]-V )); // max eigenvector = main axis
vec4 O = vec4(0);
for (float i = -7.5; i<8.; i++) // sample x16 along main axis at LOD min-radius
O += textureLod(T, p+(i/16.)*A, l);
return O/16.;
}
```