Skip to content

Commit

Permalink
Improve depth(for picking) bits precision
Browse files Browse the repository at this point in the history
  • Loading branch information
tomlee-dev committed Mar 29, 2023
1 parent 94ebcb3 commit 8312f16
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 21 deletions.
47 changes: 31 additions & 16 deletions src/framebuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { vec3 } from "gl-matrix";


export class Framebuffer {
public static glContextStencilBits = 0; //class static variable
public framebuffer: WebGLFramebuffer;
public renderbuffer: WebGLRenderbuffer;
public texture: WebGLTexture;
Expand Down Expand Up @@ -70,10 +71,17 @@ export class Framebuffer {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
if (this._glVersion === 1) {

//If the stencil bit is 8 bits or more, set to record the depth texture with DEPTH24_STENCIL8 if available (24+4 expands to a total of 32 bits to record depth => needs confirmation)
if(Framebuffer.glContextStencilBits >= 8) {
//32bit(24+8) precision
gl.texImage2D(gl.TEXTURE_2D, 0, gl["DEPTH24_STENCIL8"], width, height, 0, gl.DEPTH_STENCIL, gl["UNSIGNED_INT_24_8"], null);
} else if (this._glVersion === 1) {
//8bit precision
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
} else {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
//16bit precision
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
}
}

Expand Down Expand Up @@ -109,6 +117,23 @@ export class Framebuffer {
return result;
}

public getPixelAsFloat(x: number, y: number): number {
if (!this.isReady)
return null;
var result = new Uint8Array(4);
this.gl.readPixels(x, y, 1, 1, this.gl.RGBA, this.gl.UNSIGNED_BYTE, result);
return this.decodeFloatFromRGBA(result);
}

public decodeFloatFromRGBA(rgba : Uint8Array):number {
const rightVec4 = [1.0, 1/255.0, 1/65025.0, 1/16581375.0];
const dot = (rgba[0] / 255.0) * rightVec4[0]
+ (rgba[1] / 255.0) * rightVec4[1]
+ (rgba[2] / 255.0) * rightVec4[2]
+ (rgba[3] / 255.0) * rightVec4[3];
return dot;
}

public getDepth(x: number, y: number): number {
if (!this.isReady)
return null;
Expand All @@ -135,15 +160,12 @@ export class Framebuffer {
const depths = this.getDepths(points);

return depths.map((depth, i) => {
if (depth === 255) { // infinity (= nothing, no value)
return null;
}

// convert values to clip space where x and y are [-1, 1] and z is [0, 1]
const point = points[i];
const xc = point.x / this.width * 2.0 - 1.0;
const yc = point.y / this.height * 2.0 - 1.0;
const zc = (depth / 255.0 - 0.5) * 2.0;
const zc = (depth-0.5) * 2.0;

return vec3.fromValues(xc, yc, zc);
});
Expand Down Expand Up @@ -177,20 +199,13 @@ export class Framebuffer {
}

const depth = this.getDepth(x, y);
if (depth === 255) { // infinity
return null;
}

// convert values to clip space where x and y are [-1, 1] and z is [0, 1]
const xc = x / this.width * 2.0 - 1.0;
const yc = y / this.height * 2.0 - 1.0;
const zc = (depth / 255.0 - 0.5) * 2.0;

const depthNear = Math.max(depth - 1, 0);
const zcn = (depthNear / 255.0 - 0.5) * 2.0;

const depthFar = Math.min(depth + 1, 255);
const zcf = (depthFar / 255.0 - 0.5) * 2.0;
const zc = 0.5;
const zcn = 0.0;
const zcf = 1.0;

return {
far: vec3.fromValues(xc, yc, zcf),
Expand Down
38 changes: 34 additions & 4 deletions src/shaders/depth-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,38 @@ export class DepthReader {

//fragment shader
this.fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
let fsCompiled = compile(this.fragmentShader, depth_fragment_shader);

const depth_fragment_shader_ws = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#define highp mediump
#endif
varying vec2 position;
uniform sampler2D texture;
vec4 packDepth(float depth)
{
// See Aras Pranckevičius' post Encoding Floats to RGBA
// http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * depth;
enc = fract(enc);
enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);
return enc;
}
void main() {
float depth = texture2D(texture, position).x;
gl_FragColor = packDepth(texture2D(texture, position).r);
//[original code] gl_FragColor = vec4(depth, depth, depth, 1.0);
}
`;

let fsCompiled = compile(this.fragmentShader, depth_fragment_shader_ws); //change for ws
if (!fsCompiled) {
throw new Error("Failed to compile depth reading fragment shader");
}
Expand Down Expand Up @@ -92,9 +123,8 @@ export class DepthReader {
gl.enableVertexAttribArray(this.vertAttrs);
gl.vertexAttribPointer(this.vertAttrs, 2, gl.FLOAT, false, 0, 0);


gl.clearColor(0, 0, 0, 0); //zero colour for no-values
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

// draw the quad (2 triangles, 6 vertices)
gl.drawArrays(gl.TRIANGLES, 0, 6);
Expand All @@ -112,7 +142,7 @@ export class DepthReader {
this.draw(tex);

// all components should be the same (therefore just using [0])
var depths = points.map(p => fb.getPixel(p.x, p.y)[0]) ;
var depths = points.map(p => fb.getPixelAsFloat(p.x, p.y)) ;

// free resources
fb.delete();
Expand Down
4 changes: 3 additions & 1 deletion src/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export class Viewer {
WebGLUtils.setupWebGL(this.canvas, (ctx, version) => {
this.gl = ctx;
this.glVersion = version;
}, { preserveDrawingBuffer: true }, (err) => {
}, { preserveDrawingBuffer: true, stencil: true}, (err) => {
this.error(err);
});

Expand All @@ -386,6 +386,8 @@ export class Viewer {
return;
}

let stencilBits = this.gl.getParameter(this.gl.STENCIL_BITS);
Framebuffer.glContextStencilBits = stencilBits;

// keep reference to the function in case it gets into zone. For example Angular uses
// NgZone forked from the root Zone to refresh data content. But we make heavy use of this
Expand Down

0 comments on commit 8312f16

Please sign in to comment.