Skip to content

Commit 86f557c

Browse files
committed
Improve depth(for picking) bits precision
1 parent 94ebcb3 commit 86f557c

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

src/framebuffer.ts

+31-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { vec3 } from "gl-matrix";
33

44

55
export class Framebuffer {
6+
public static glContextStencilBits = 0; //class static variable
67
public framebuffer: WebGLFramebuffer;
78
public renderbuffer: WebGLRenderbuffer;
89
public texture: WebGLTexture;
@@ -70,10 +71,17 @@ export class Framebuffer {
7071
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
7172
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
7273
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
73-
if (this._glVersion === 1) {
74+
75+
//If the stencil bit is 8 bits or more, set to record the depth texture with DEPTH24_STENCIL8 if available (24+8 expands to a total of 32 bits to record depth => needs confirmation)
76+
if(Framebuffer.glContextStencilBits >= 8) {
77+
//32bit(24+8) precision
78+
gl.texImage2D(gl.TEXTURE_2D, 0, gl["DEPTH24_STENCIL8"], width, height, 0, gl.DEPTH_STENCIL, gl["UNSIGNED_INT_24_8"], null);
79+
} else if (this._glVersion === 1) {
80+
//8bit precision
7481
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
7582
} else {
76-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
83+
//16bit precision
84+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
7785
}
7886
}
7987

@@ -109,6 +117,23 @@ export class Framebuffer {
109117
return result;
110118
}
111119

120+
public getPixelAsFloat(x: number, y: number): number {
121+
if (!this.isReady)
122+
return null;
123+
var result = new Uint8Array(4);
124+
this.gl.readPixels(x, y, 1, 1, this.gl.RGBA, this.gl.UNSIGNED_BYTE, result);
125+
return this.decodeFloatFromRGBA(result);
126+
}
127+
128+
public decodeFloatFromRGBA(rgba : Uint8Array):number {
129+
const rightVec4 = [1.0, 1/255.0, 1/65025.0, 1/16581375.0];
130+
const dot = (rgba[0] / 255.0) * rightVec4[0]
131+
+ (rgba[1] / 255.0) * rightVec4[1]
132+
+ (rgba[2] / 255.0) * rightVec4[2]
133+
+ (rgba[3] / 255.0) * rightVec4[3];
134+
return dot;
135+
}
136+
112137
public getDepth(x: number, y: number): number {
113138
if (!this.isReady)
114139
return null;
@@ -135,15 +160,12 @@ export class Framebuffer {
135160
const depths = this.getDepths(points);
136161

137162
return depths.map((depth, i) => {
138-
if (depth === 255) { // infinity (= nothing, no value)
139-
return null;
140-
}
141163

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

148170
return vec3.fromValues(xc, yc, zc);
149171
});
@@ -177,20 +199,13 @@ export class Framebuffer {
177199
}
178200

179201
const depth = this.getDepth(x, y);
180-
if (depth === 255) { // infinity
181-
return null;
182-
}
183202

184203
// convert values to clip space where x and y are [-1, 1] and z is [0, 1]
185204
const xc = x / this.width * 2.0 - 1.0;
186205
const yc = y / this.height * 2.0 - 1.0;
187-
const zc = (depth / 255.0 - 0.5) * 2.0;
188-
189-
const depthNear = Math.max(depth - 1, 0);
190-
const zcn = (depthNear / 255.0 - 0.5) * 2.0;
191-
192-
const depthFar = Math.min(depth + 1, 255);
193-
const zcf = (depthFar / 255.0 - 0.5) * 2.0;
206+
const zc = 0.5;
207+
const zcn = 0.0;
208+
const zcf = 1.0;
194209

195210
return {
196211
far: vec3.fromValues(xc, yc, zcf),

src/shaders/depth-reader.ts

+34-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,38 @@ export class DepthReader {
4141

4242
//fragment shader
4343
this.fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
44-
let fsCompiled = compile(this.fragmentShader, depth_fragment_shader);
44+
45+
const depth_fragment_shader_ws = `
46+
#ifdef GL_FRAGMENT_PRECISION_HIGH
47+
precision highp float;
48+
precision highp int;
49+
#else
50+
precision mediump float;
51+
precision mediump int;
52+
#define highp mediump
53+
#endif
54+
55+
varying vec2 position;
56+
uniform sampler2D texture;
57+
58+
vec4 packDepth(float depth)
59+
{
60+
// See Aras Pranckevičius' post Encoding Floats to RGBA
61+
// http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
62+
vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * depth;
63+
enc = fract(enc);
64+
enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);
65+
return enc;
66+
}
67+
68+
void main() {
69+
float depth = texture2D(texture, position).x;
70+
gl_FragColor = packDepth(texture2D(texture, position).r);
71+
//[original code] gl_FragColor = vec4(depth, depth, depth, 1.0);
72+
}
73+
`;
74+
75+
let fsCompiled = compile(this.fragmentShader, depth_fragment_shader_ws); //change for ws
4576
if (!fsCompiled) {
4677
throw new Error("Failed to compile depth reading fragment shader");
4778
}
@@ -92,9 +123,8 @@ export class DepthReader {
92123
gl.enableVertexAttribArray(this.vertAttrs);
93124
gl.vertexAttribPointer(this.vertAttrs, 2, gl.FLOAT, false, 0, 0);
94125

95-
96126
gl.clearColor(0, 0, 0, 0); //zero colour for no-values
97-
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
127+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
98128

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

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

117147
// free resources
118148
fb.delete();

src/viewer.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ export class Viewer {
376376
WebGLUtils.setupWebGL(this.canvas, (ctx, version) => {
377377
this.gl = ctx;
378378
this.glVersion = version;
379-
}, { preserveDrawingBuffer: true }, (err) => {
379+
}, { preserveDrawingBuffer: true, stencil: true}, (err) => {
380380
this.error(err);
381381
});
382382

@@ -386,6 +386,8 @@ export class Viewer {
386386
return;
387387
}
388388

389+
let stencilBits = this.gl.getParameter(this.gl.STENCIL_BITS);
390+
Framebuffer.glContextStencilBits = stencilBits;
389391

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

0 commit comments

Comments
 (0)