Raycasting-Methode
Es ist sehr einfach, Objekte mit dem Raycaster auszuwählen, der mit three.js geliefert wird Der Code lautet wie folgt:
var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); function onMouseMove(event) { // 计算鼠标所在位置的设备坐标 // 三个坐标分量都是-1到1 mouse.x = event.clientX / window.innerWidth * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; } function pick() { // 使用相机和鼠标位置更新选取光线 raycaster.setFromCamera(mouse, camera); // 计算与选取光线相交的物体 var intersects = raycaster.intersectObjects(scene.children); }
[Verwandte Kursempfehlungen: JavaScript-Video-Tutorial]
Es verwendet Bounding-Box-Filterung, um den Projektionsstrahl und jedes dreieckige Oberflächenelement zu berechnen Schnittpunkt erreicht wird.
Wenn das Modell jedoch sehr groß ist, beispielsweise 400.000 Gesichter, ist die Auswahl von Objekten und die Berechnung der Position von Kollisionspunkten durch Durchqueren sehr langsam und die Benutzererfahrung wird schlecht sein.
Bei der Verwendung von GPU zum Auswählen von Objekten tritt dieses Problem jedoch nicht auf. Unabhängig von der Größe der Szene und des Modells können die Position des Objekts und der Schnittpunkt am Mauspunkt innerhalb eines Frames ermittelt werden.
Verwenden Sie die GPU, um Objekte auszuwählen
Die Implementierungsmethode ist sehr einfach:
1. Erstellen Sie ein Auswahlmaterial und ersetzen Sie das Material jedes Modells in die Szene mit verschiedenen Farben.
2. Lesen Sie die Pixelfarbe an der Mausposition und bestimmen Sie anhand der Farbe das Objekt an der Mausposition.
Spezifischer Implementierungscode:
1. Erstellen Sie ein ausgewähltes Material, durchlaufen Sie die Szene und ersetzen Sie jedes Modell in der Szene durch eine andere Farbe.
let maxHexColor = 1;// 更换选取材质 scene.traverseVisible(n => { if (!(n instanceof THREE.Mesh)) { return; } n.oldMaterial = n.material; if (n.pickMaterial) { // 已经创建过选取材质了 n.material = n.pickMaterial; return; } let material = new THREE.ShaderMaterial({ vertexShader: PickVertexShader, fragmentShader: PickFragmentShader, uniforms: { pickColor: { value: new THREE.Color(maxHexColor) } } }); n.pickColor = maxHexColor; maxHexColor++; n.material = n.pickMaterial = material; }); PickVertexShader: void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } PickFragmentShader: uniform vec3 pickColor;void main() { gl_FragColor = vec4(pickColor, 1.0); }
2. Zeichnen Sie die Szene auf WebGLRenderTarget, lesen Sie die Farbe der Mausposition und bestimmen Sie das ausgewählte Objekt.
let renderTarget = new THREE.WebGLRenderTarget(width, height); let pixel = new Uint8Array(4);// 绘制并读取像素 renderer.setRenderTarget(renderTarget); renderer.clear(); renderer.render(scene, camera); renderer.readRenderTargetPixels(renderTarget, offsetX, height - offsetY, 1, 1, pixel); // 读取鼠标所在位置颜色 // 还原原来材质,并获取选中物体 const currentColor = pixel[0] * 0xffff + pixel[1] * 0xff + pixel[2]; let selected = null; scene.traverseVisible(n => { if (!(n instanceof THREE.Mesh)) { return; } if (n.pickMaterial && n.pickColor === currentColor) { // 颜色相同 selected = n; // 鼠标所在位置的物体 } if (n.oldMaterial) { n.material = n.oldMaterial; delete n.oldMaterial; } });
Erklärung: offsetX und offsetY sind die Mausposition und height ist die Leinwandhöhe. Die Bedeutung der Zeile readRenderTargetPixels besteht darin, die Farbe des Pixels an der Mausposition (OffsetX, Höhe - OffsetY) mit einer Breite von 1 und einer Höhe von 1 auszuwählen.
Pixel ist Uint8Array(4), das jeweils vier RGBA-Farbkanäle speichert. Der Wertebereich jedes Kanals liegt zwischen 0 und 255.
Vollständiger Implementierungscode: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/event/GPUPickEvent.js
Zum Abrufen GPU verwenden Schnittpunkte Position
Die Implementierungsmethode ist ebenfalls sehr einfach:
1 Erstellen Sie ein Tiefen-Shader-Material und rendern Sie die Szenentiefe auf das WebGLRenderTarget.
2. Berechnen Sie die Tiefe der Mausposition und berechnen Sie die Schnittpunktposition basierend auf der Mausposition und -tiefe.
Spezifischer Implementierungscode:
1. Erstellen Sie ein Tiefen-Shader-Material, kodieren Sie die Tiefeninformationen auf eine bestimmte Weise und rendern Sie es an das WebGLRenderTarget.
Tiefenmaterial:
const depthMaterial = new THREE.ShaderMaterial({ vertexShader: DepthVertexShader, fragmentShader: DepthFragmentShader, uniforms: { far: { value: camera.far } } }); DepthVertexShader: precision highp float; uniform float far; varying float depth;void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); depth = gl_Position.z / far; } DepthFragmentShader: precision highp float; varying float depth;void main() { float hex = abs(depth) * 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, a); }
Wichtiger Hinweis:
a. gl_Position.z ist die Tiefe im Kameraraum, die linear ist und von cameraNear bis cameraFar reicht. Shadervariierende Variablen können direkt zur Interpolation verwendet werden.
b. Der Grund für gl_Position.z/far ist die Konvertierung des Werts in den Bereich von 0 bis 1 für eine einfache Ausgabe als Farbe.
c. Die Tiefe im Bildschirmbereich kann nicht verwendet werden. Nach der perspektivischen Projektion beträgt die Tiefe -1 bis 1, wobei die meisten davon sehr nahe bei 1 liegen (mehr als 0,9), nicht linear und nahezu unverändert Die Ausgabefarbe ist nahezu unverändert und sehr ungenau.
d. Erhalten Sie die Tiefenmethode im Fragment-Shader: Die Tiefe des Kameraraums ist gl_FragCoord.z, die Tiefe des Bildschirmraums ist gl_FragCoord.z /gl_FragCoord.w.
e. Die obigen Beschreibungen gelten alle für die perspektivische Projektion, gl_Position.w ist 1 und der Kameraraum und die Bildschirmraumtiefe sind gleich.
f. Um die Tiefe so genau wie möglich auszugeben, werden die drei Komponenten von RGB zur Ausgabe der Tiefe verwendet. Der Bereich von gl_Position.z/far liegt zwischen 0 und 1, multipliziert mit 0xffffff und wird in einen RGB-Farbwert umgewandelt. Die r-Komponente 1 repräsentiert 65535, die g-Komponente 1 repräsentiert 255 und die b-Komponente 1 repräsentiert 1.
Vollständiger Implementierungscode: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/event/GPUPickEvent.js
2. Lesen Sie die Mauspositionsfarbe , stellen Sie den gelesenen Farbwert auf den Wert der Kameraraumtiefe wieder her.
a. Zeichnen Sie die „verschlüsselte“ Tiefe auf das WebGLRenderTarget. Farblesemethode
let renderTarget = new THREE.WebGLRenderTarget(width, height); let pixel = new Uint8Array(4); scene.overrideMaterial = this.depthMaterial; renderer.setRenderTarget(renderTarget); renderer.clear(); renderer.render(scene, camera); renderer.readRenderTargetPixels(renderTarget, offsetX, height - offsetY, 1, 1, pixel);
Beschreibung: offsetX und offsetY sind die Mausposition und height ist die Leinwandhöhe. Die Bedeutung der Zeile readRenderTargetPixels besteht darin, die Farbe des Pixels an der Mausposition (OffsetX, Höhe - OffsetY) mit einer Breite von 1 und einer Höhe von 1 auszuwählen.
Pixel ist Uint8Array(4), das jeweils vier RGBA-Farbkanäle speichert. Der Wertebereich jedes Kanals liegt zwischen 0 und 255.
b. „Entschlüsseln“ Sie den „verschlüsselten“ Kameraraumtiefenwert, um den richtigen Kameraraumtiefenwert zu erhalten.
if (pixel[2] !== 0 || pixel[1] !== 0 || pixel[0] !== 0) { let hex = (this.pixel[0] * 65535 + this.pixel[1] * 255 + this.pixel[2]) / 0xffffff; if (this.pixel[3] === 0) { hex = -hex; } cameraDepth = -hex * camera.far; // 相机坐标系中鼠标所在点的深度(注意:相机坐标系中的深度值为负值)}
3. Basierend auf der Position der Maus auf dem Bildschirm und der Tiefe des Kameraraums werden die Koordinaten im Weltkoordinatensystem des Schnittpunkts interpoliert und zurückgerechnet.
let nearPosition = new THREE.Vector3(); // 鼠标屏幕位置在near处的相机坐标系中的坐标 let farPosition = new THREE.Vector3(); // 鼠标屏幕位置在far处的相机坐标系中的坐标 let world = new THREE.Vector3(); // 通过插值计算世界坐标 // 设备坐标 const deviceX = this.offsetX / width * 2 - 1; const deviceY = - this.offsetY / height * 2 + 1;// 近点 nearPosition.set(deviceX, deviceY, 1); // 屏幕坐标系:(0, 0, 1) nearPosition.applyMatrix4(camera.projectionMatrixInverse); // 相机坐标系:(0, 0, -far) // 远点 farPosition.set(deviceX, deviceY, -1); // 屏幕坐标系:(0, 0, -1) farPosition.applyMatrix4(camera.projectionMatrixInverse); // 相机坐标系:(0, 0, -near) // 在相机空间,根据深度,按比例计算出相机空间x和y值。 const t = (cameraDepth - nearPosition.z) / (farPosition.z - nearPosition.z); // 将交点从相机空间中的坐标,转换到世界坐标系坐标。 world.set( nearPosition.x + (farPosition.x - nearPosition.x) * t, nearPosition.y + (farPosition.y - nearPosition.y) * t, cameraDepth ); world.applyMatrix4(camera.matrixWorld);
Vollständiger Code: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/event/GPUPickEvent.js
Verwandte Anwendungen
Verwenden Sie die GPU, um Objekte auszuwählen und die Schnittpunktposition zu berechnen, was vor allem dann verwendet wird, wenn eine sehr hohe Leistung erforderlich ist. Zum Beispiel:
1. Der Hover-Effekt, wenn sich die Maus zum 3D-Modell bewegt.
2. Beim Hinzufügen eines Modells wird das Modell mit der Maus bewegt und der Effekt der Platzierung des Modells in der Szene wird in Echtzeit angezeigt.
3. Werkzeuge wie Distanzmessung und Flächenmessung können in Echtzeit angezeigt werden, während sich die Maus auf der Ebene bewegt, und die Länge und Fläche kann berechnet werden.
4. Die Szene und das Modell sind sehr groß, die Auswahlgeschwindigkeit der Raycasting-Methode ist sehr langsam und die Benutzererfahrung ist sehr schlecht.
Hier ist ein Bild der Verwendung der GPU zum Auswählen von Objekten und zum Erzielen eines Maus-Hover-Effekts. Der rote Rand ist der Auswahleffekt und der gelbe durchscheinende Effekt ist der Mauszeigereffekt.
Kannst du das nicht verstehen? Möglicherweise sind Sie mit den verschiedenen Projektionsvorgängen in three.js nicht vertraut. Die Projektionsoperationsformel in three.js ist unten angegeben.
Projektionsoperation in three.js
1. modelViewMatrix = camera.matrixWorldInverse * object.matrixWorld
2. viewMatrix = camera . MatrixWorldInverse
3. modelMatrix = object.matrixWorld
4. project = applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix )
5. unproject = applyMatrix4( camera .projectionMatrixInverse ).applyMatrix4( camera.matrixWorld )
6. gl_Position = projectionMatrix * modelViewMatrix * position
= projectMatrix * viewMatrix * modelMatrix * position
Referenz:
1. Vollständiger Implementierungscode: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/event /GPUPickEvent.js
2. Basierend auf einem Open-Source-3D-Szeneneditor auf three.js: https://github.com/tengge1/ShadowEditor
3. Verwenden von Shader zum Zeichnen von Tiefenwerten in OpenGL:https://stackoverflow.com/questions/6408851/draw-the -length-value-in-opengl-using-shaders
4. Holen Sie sich in glsl den echten Fragment-Shader-Tiefenwert: https://gamedev.stackexchange.com/questions/93055/getting-the-real -fragment- Depth-in-glsl
Dieser Artikel stammt aus der Spalte
JS-TutorialDas obige ist der detaillierte Inhalt vonthree.js verwendet GPU, um Objekte auszuwählen und Schnittpositionen zu berechnen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!