一.介绍
WEBGL_depth_texture
是一个webgl1的扩展插件,允许为FBO创建深度纹理,将深度写入纹理中,便于使用,通常 可以用来获取点击点的世界坐标,法线等。
这个扩展扩展了两个方法:
texImage2D(target, level, internalformat, width, height, border, format, type, pixels)
format
和internalformat
可以接受两个参数:gl.DEPTH_COMPONENT
和gl.DEPTH_STENCIL
type
可以接受三个参数:gl.UNSIGNED_SHORT
,gl.UNSIGNED_INT
, 和ext.UNSIGNED_INT_24_8_WEBGL
.pixels
可以接受[Uint16Array
] or a [Uint32Array
]
framebufferTexture2D(target, attachment, textarget, texture, level)
attachment
可以接受gl.DEPTH_STENCIL_ATTACHMENT
二 使用深度反算世界坐标
2.1 获取扩展
js
this._depthTextureExt = this._gl.getExtension("WEBGL_depth_texture");
2.2 创建深度(模板)纹理
js
Texture2D.createDepthStencilTexture=function({width,height,type,context}){
const gl=context._gl
return new Texture2D({
context,
width,
height,
type,
format:gl.DEPTH_STENCIL,
internalformat:gl.DEPTH_STENCIL
})
}
2.3 FBO创建深度(模板)关联对象
js
//将颜色附件绑定到FBO上
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.DEPTH_STENCIL_ATTACHMENT,
gl.TEXTURE_2D,
texture2D.texture,
0
);
2.4 创建屏幕四边形,将深度纹理展示
js
const uniformMap2 = {
u_viewProjectMatrix: orthoCamera.viewProjectMatrix,
u_modelMatrix: plane.modelMatrix,
u_texture: depthStentilTexture,
near:camera.nearDistance,
far:camera.farDistance
};
drawCommandPlane.uniformMap = uniformMap2;
drawCommandPlane.execute();
shader
c
const depthTextureFS = /*glsl*/ `
// #extension GL_EXT_draw_buffers : require
precision highp float;
uniform sampler2D u_texture;
uniform float near;
uniform float far;
varying vec2 v_uv;
float getViewZ(float depth)
{
float z = depth * 2.0 - 1.0; // back to NDC
return (2.0 * near * far) / (far + near - z * (far - near));
}
float linerDepth(float viewZ){
return (viewZ-near)/(far-near);
}
void main(){
vec4 depthStencil=texture2D(u_texture,v_uv);
// float viewZ=getViewZ(depthStencil.r);
// gl_FragColor=vec4(vec3(1.0-linerDepth(viewZ)),depthStencil.a);
gl_FragColor=vec4(vec3(1.0-depthStencil.r),depthStencil.a);
}
`;
2.5 点击获取世界坐标
js
document.getElementById("canvas").onclick = (e) => {
const x = e.offsetX;
const y = e.offsetY;
const pixels = new Uint8Array(4);
draw()
gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
console.log(pixels)
const depth = (255 - pixels[0]) / (255);
windowToWorldPosition(x/canvas.clientWidth,(1.0-y/canvas.clientHeight),depth)
};
function windowToWorldPosition(x,y,depth) {
//转换到ndc空间
const xNDC=x*2-1
const yNDC=y*2-1
const zNDC=depth*2-1
const worldPosition=camera.inverseProjectMatrix.multiplyVector4(new Vector4([xNDC,yNDC,zNDC,1.0]))
const elements=worldPosition.elements
console.log(elements[0]/elements[3],elements[1]/elements[3],elements[2]/elements[3])
}
点击获取一个结果为:-0.2748838665872577 0.8578270784183898 1.327160348557312
目测是正确的
在这个过程中,其实可以在2.4中shader将深度打包为RGBA值,同时子啊点击时获取到rgba在解包为深度值,精度更高!