使用GPU反算世界坐标

This commit is contained in:
tengge1 2019-11-22 22:21:24 +08:00
parent f3bd5373a9
commit 16b0ec7ba8
5 changed files with 43 additions and 92 deletions

View File

@ -1,86 +0,0 @@
import BaseEvent from './BaseEvent';
import DepthVertexShader from './shader/depth_vertex.glsl';
import DepthFragmentShader from './shader/depth_fragment.glsl';
/**
* 使用GPU进行碰撞
* @author tengge / https://github.com/tengge1
* @param {*} app 应用程序
*/
function GPUPickEvent(app) {
BaseEvent.call(this, app);
this.offsetX = 0;
this.offsetY = 0;
this.onMouseMove = this.onMouseMove.bind(this);
this.onAfterRender = this.onAfterRender.bind(this);
}
GPUPickEvent.prototype = Object.create(BaseEvent.prototype);
GPUPickEvent.prototype.constructor = GPUPickEvent;
GPUPickEvent.prototype.start = function () {
app.on(`mousemove.${this.id}`, this.onMouseMove);
app.on(`afterRender.${this.id}`, this.onAfterRender);
};
GPUPickEvent.prototype.stop = function () {
app.on(`mousemove.${this.id}`, null);
app.on(`afterRender.${this.id}`, null);
};
GPUPickEvent.prototype.onMouseMove = function (event) {
if (event.target !== app.editor.renderer.domElement) {
return;
}
this.offsetX = event.offsetX;
this.offsetY = event.offsetY;
};
GPUPickEvent.prototype.onAfterRender = function () {
let world = new THREE.Vector3();
return function () {
let { scene, camera, renderer } = app.editor;
const { width, height } = renderer.domElement;
if (this.init === undefined) {
this.init = true;
this.material = new THREE.ShaderMaterial({
vertexShader: DepthVertexShader,
fragmentShader: DepthFragmentShader
});
this.renderTarget = new THREE.WebGLRenderTarget(width, height);
this.pixel = new Uint8Array(4);
}
// 记录旧属性
const oldOverrideMaterial = scene.overrideMaterial;
const oldRenderTarget = renderer.getRenderTarget();
// 更换深度材质
scene.overrideMaterial = this.material;
renderer.setRenderTarget(this.renderTarget);
renderer.clear();
// 读取深度
this.pixel.set([0, 0, 0, 0]);
renderer.render(scene, camera);
renderer.readRenderTargetPixels(this.renderTarget, this.offsetX, height - this.offsetY, 1, 1, this.pixel);
let depth = (this.pixel[0] * 65535 + this.pixel[1] * 255 + this.pixel[2]) / 16777215 * 80 - 1;
world.set(
this.offsetX / width * 2 - 1,
- this.offsetY / height * 2 + 1,
depth
);
world.unproject(camera);
// 还原原来的属性
scene.overrideMaterial = oldOverrideMaterial;
renderer.setRenderTarget(oldRenderTarget);
};
}();
export default GPUPickEvent;

View File

@ -1,12 +1,14 @@
import BaseEvent from './BaseEvent';
import PickVertexShader from './shader/pick_vertex.glsl';
import PickFragmentShader from './shader/pick_fragment.glsl';
import DepthVertexShader from './shader/depth_vertex.glsl';
import DepthFragmentShader from './shader/depth_fragment.glsl';
import MeshUtils from '../utils/MeshUtils';
let maxHexColor = 1;
/**
* 使用GPU进行碰撞
* 使用GPU选取物体和计算鼠标世界坐标
* @author tengge / https://github.com/tengge1
*/
function GPUPickEvent() {
@ -48,6 +50,9 @@ GPUPickEvent.prototype.onMouseMove = function (event) {
this.offsetY = event.offsetY;
};
/**
* 由于需要较高性能所以尽量不要拆分函数
*/
GPUPickEvent.prototype.onAfterRender = function () {
if (!this.isIn) {
return;
@ -57,8 +62,13 @@ GPUPickEvent.prototype.onAfterRender = function () {
if (this.init === undefined) {
this.init = true;
this.depthMaterial = new THREE.ShaderMaterial({
vertexShader: DepthVertexShader,
fragmentShader: DepthFragmentShader
});
this.renderTarget = new THREE.WebGLRenderTarget(width, height);
this.pixel = new Uint8Array(4);
this.world = new THREE.Vector3();
}
// 记录旧属性
@ -66,6 +76,8 @@ GPUPickEvent.prototype.onAfterRender = function () {
const oldOverrideMaterial = scene.overrideMaterial;
const oldRenderTarget = renderer.getRenderTarget();
// ---------------------------- 1. 使用GPU判断选中的物体 -----------------------------------
scene.background = null; // 有背景图,可能导致提取的颜色不准
scene.overrideMaterial = null;
renderer.setRenderTarget(this.renderTarget);
@ -129,6 +141,29 @@ GPUPickEvent.prototype.onAfterRender = function () {
}
}
// ------------------------- 2. 使用GPU反算世界坐标 ----------------------------------
scene.overrideMaterial = this.material;
renderer.clear();
renderer.render(scene, camera);
renderer.readRenderTargetPixels(this.renderTarget, this.offsetX, height - this.offsetY, 1, 1, this.pixel);
let depth = (this.pixel[0] * 65535 + this.pixel[1] * 255 + this.pixel[2]) / 0xffffff;
if (this.pixel[3] === 0) {
depth = -depth;
}
this.world.set(
this.offsetX / width * 2 - 1,
- this.offsetY / height * 2 + 1,
depth
);
this.world.unproject(camera);
// console.log(`${this.pixel[0]}, ${this.pixel[1]}, ${this.pixel[2]}, ${depth}, ${this.world.x}, ${this.world.y}, ${this.world.z}`);
// 还原原来的属性
scene.background = oldBackground;
scene.overrideMaterial = oldOverrideMaterial;

View File

@ -1,12 +1,14 @@
precision highp float;
varying float depth;
void main() {
float hex = abs(depth) * 16777215.0;
float hex = abs(clamp(depth, -1.0, 1.0)) * 16777215.0; // 0xffffff
float r = floor(hex / 65535.0);
float g = floor((hex - r * 65535.0) / 255.0);
float b = floor(hex - r * 65535.0 - g * 255.0);
float a = sign(depth) >= 0.0 ? 1.0 : 0.0; // depth大于等于0为1.0小于0为0.0。
gl_FragColor = vec4(r / 255.0, g / 255.0, b / 255.0, 1.0);
gl_FragColor = vec4(r / 255.0, g / 255.0, b / 255.0, a);
}

View File

@ -1,8 +1,8 @@
precision highp float;
varying float depth;
void main() {
vec4 transformed = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * transformed;
depth = transformed.z;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
depth = gl_Position.z;
}