Jar*_*red 0 picking mouse-picking three.js
我正在渲染一个包含精灵的采摘场景。当我的光标靠近精灵时,它注册为一种颜色并被“选中”。放大小精灵时,此不可见的边框会变大。
打开控制台以查看实时打印的ID。将光标移近或移远,移至大和小的精灵。您会看到精灵在不可见的边框上被选中。对于常规几何图形,仅对于精灵,不会发生此行为。
这很奇怪,因为我正在渲染renderer.readRenderTargetPixels实际看到的东西。
如何摆脱看不见的边界,以进行更准确的拾取?
var renderer, scene, camera, controls;
var particles, uniforms;
var PARTICLE_SIZE = 50;
var raycaster, intersects;
var mouse, INTERSECTED;
var pickingTexture;
var numOfVertices;
init();
animate();
function init() {
container = document.getElementById('container');
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 150;
//
var geometry1 = new THREE.BoxGeometry(200, 200, 200, 4, 4, 4);
var vertices = geometry1.vertices;
numOfVertices = vertices.length;
var positions = new Float32Array(vertices.length * 3);
var colors = new Float32Array(vertices.length * 3);
var sizes = new Float32Array(vertices.length);
var vertex;
var color = new THREE.Color();
for (var i = 0, l = vertices.length; i < l; i++) {
vertex = vertices[i];
vertex.toArray(positions, i * 3);
color.setHex(i + 1);
color.toArray(colors, i * 3);
sizes[i] = PARTICLE_SIZE * 0.5;
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));
//
var material = new THREE.ShaderMaterial({
uniforms: {
// texture: {type: "t", value: THREE.ImageUtils.loadTexture("../textures/circle.png")}
texture: {type: "t", value: THREE.ImageUtils.loadTexture("../textures/disc.png")}
},
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
depthTest: false,
transparent: false
// alphaTest: 0.9
});
//
particles = new THREE.Points(geometry, material);
scene.add(particles);
//
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff);
container.appendChild(renderer.domElement);
//
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
//
//
window.addEventListener('resize', onWindowResize, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
// defaults are on the right (except minFilter)
var options = {
format: THREE.RGBAFormat, // THREE.RGBAFormat
type: THREE.UnsignedByteType, // THREE.UnsignedByteType
anisotropy: 1, // 1
magFilter: THREE.LinearFilter, // THREE.LinearFilter
minFilter: THREE.LinearFilter, // THREE.LinearFilter
depthBuffer: true, // true
stencilBuffer: true // true
};
pickingTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, options);
pickingTexture.texture.generateMipmaps = false;
controls = new THREE.OrbitControls(camera, container);
controls.damping = 0.2;
controls.enableDamping = false;
}
function onDocumentMouseMove(e) {
// event.preventDefault();
mouse.x = e.clientX;
mouse.y = e.clientY;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
render();
}
function render() {
pick();
renderer.render(scene, camera);
}
function pick() {
renderer.render(scene, camera, pickingTexture);
//create buffer for reading single pixel
var pixelBuffer = new Uint8Array(4);
//read the pixel under the mouse from the texture
renderer.readRenderTargetPixels(pickingTexture, mouse.x, pickingTexture.height - mouse.y, 1, 1, pixelBuffer);
//interpret the pixel as an ID
var id = ( pixelBuffer[0] << 16 ) | ( pixelBuffer[1] << 8 ) | ( pixelBuffer[2] );
if (id <= numOfVertices) console.log(id);
}Run Code Online (Sandbox Code Playgroud)
body {
color: #ffffff;
background-color: #000000;
margin: 0px;
overflow: hidden;
}Run Code Online (Sandbox Code Playgroud)
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform sampler2D texture;
varying vec3 vColor;
void main() {
// solid squares of color
gl_FragColor = vec4( vColor, 1.0 );
}
</script>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<div id="container"></div>Run Code Online (Sandbox Code Playgroud)
问题是您所使用的设备的devicePixelRatio!= 1.0和three.js大小差不多。
因为你打电话 renderer.setPixelRatio现在魔术在幕后发生。您画布的大小不是您要求的大小,而是根据three.js代码中隐藏的某些公式得出的其他大小。
所以,会发生什么。您的画布是一种尺寸,但是渲染目标是另一种尺寸。您的着色器使用gl_PointSize绘制其点。该尺寸以设备像素为单位。由于渲染目标的大小不同,因此渲染目标中点的大小与屏幕上的点不同。
删除对的呼叫render.setPixelRatio,它将开始工作。
IMO解决此问题的正确方法是使用devicePixelRatio自己,因为这样您可以100%看到所有发生的事情。幕后没有发生任何魔术。
所以,
摆脱容器并直接使用画布
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)设置画布以100vw用于宽度,100vh高度和主体margin: 0;
canvas { width: 100vw; height: 100vh; display: block; }
body { margin: 0; }
Run Code Online (Sandbox Code Playgroud)
这将使您的画布自动拉伸以填充窗口。
使用浏览器拉伸画布的大小选择其drawingBuffer应为的大小并乘以devicePixelRatio。假设您实际上要支持设备像素比率。无需重复执行两次,因此在DRY之后,只需在onWindowResize中进行即可。
canvas = document.getElementById("c");
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
canvas: canvas,
});
pickingTexture = new THREE.WebGLRenderTarget(1, 1, options);
onWindowResize();
...
function onWindowResize() {
var width = canvas.clientWidth * window.devicePixelRatio;
var height = canvas.clientHeight * window.devicePixelRatio;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false); // YOU MUST PASS FALSE HERE otherwise three.js will muck with the CSS
pickingTexture.setSize(width, height);
}
Run Code Online (Sandbox Code Playgroud)将鼠标坐标转换为设备坐标
renderer.readRenderTargetPixels(
pickingTexture,
mouse.x * window.devicePixelRatio,
pickingTexture.height - mouse.y * window.devicePixelRatio,
1, 1, pixelBuffer);
Run Code Online (Sandbox Code Playgroud)这是解决方案
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)
canvas { width: 100vw; height: 100vh; display: block; }
body { margin: 0; }
Run Code Online (Sandbox Code Playgroud)
canvas = document.getElementById("c");
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
canvas: canvas,
});
pickingTexture = new THREE.WebGLRenderTarget(1, 1, options);
onWindowResize();
...
function onWindowResize() {
var width = canvas.clientWidth * window.devicePixelRatio;
var height = canvas.clientHeight * window.devicePixelRatio;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false); // YOU MUST PASS FALSE HERE otherwise three.js will muck with the CSS
pickingTexture.setSize(width, height);
}
Run Code Online (Sandbox Code Playgroud)
注意其他一些事情。
我猜您真的想将采摘纹理清除为零而不是白色。这样,0 =那里什么都没有,其他什么都=这里的东西。
renderer.setClearColor(0);
renderer.render(scene, camera, pickingTexture);
renderer.setClearColor(0xFFFFFF);
Run Code Online (Sandbox Code Playgroud)不知道是什么id <= numOfVertices意思
因此,鉴于现在清除为零,代码只是
if (id) console.log(id);
Run Code Online (Sandbox Code Playgroud)我没有在初始化时设置渲染器尺寸,pickingTexture尺寸或相机外观。
为什么重复我自己。onWindowResize已经设置好了
调整画布大小以使其大小匹配时,需要调整pickingTexture渲染目标的大小。
我删除了对window.innerWidth和的大多数引用window.innerHeight
我将删除所有这些代码,但是我不想为该示例更改更多代码。使用window.innerWidth将代码绑定到窗口。例如,如果您想在非全尺寸窗口中使用代码,可以假设您创建了编辑器。您必须更改代码。
以一种可以在更多情况下工作的方式编写代码并不难,所以为什么以后要自己做更多的工作。
我没有选择的其他解决方案
您可以调用render.setPixelRatio,然后使用以下命令设置pickingTexture渲染目标的大小window.devicePixelRatio
我没有选择此解决方案,因为您必须猜测three.js在后台做了什么。您的猜测今天可能是正确的,但明天可能是错误的。如果您告诉three.js 做一些width by height应该做的事情似乎更好width by height而不做其他事情。同样,您必须猜测three.js何时应用pixelRatio以及何时不应用。正如您在上面注意到的那样,它不会将其应用于渲染目标的大小,并且因为它不知道目标是什么而无法应用。您是否正在为渲染选择目标?要获得全屏效果?为了捕获?非全屏效果?由于无法知道,因此无法为您应用pixelRatio。这发生在Three.js代码中。它在某些地方应用pixelRatio,而其他地方则不应用。你只是猜测。如果您从未设置pixelRatio,该问题将消失。
您可以传入devicePixelRatio着色器
<script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
uniform float devicePixelRatio; // added
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) ) * devicePixelRatio;
gl_Position = projectionMatrix * mvPosition;
}
</script>
Run Code Online (Sandbox Code Playgroud)
当然,您需要devicePixelRatio穿上制服。
我可能会选择此解决方案。较小的问题是,如果pickingTexture分辨率与画布的后缓冲区不同,则可能会出现1个错误。在这种情况下,如果画布是2x,pickingTexture则画布中每4个像素中的3个就不存在pickingTexture。不过,根据您的应用程序可能没问题。您不能选择1/2像素,至少不能用鼠标来选择。
我可能不会选择此解决方案的另一个原因是,它使问题弹出了其他地方。lineWidth是一个,gl_FragCoord是另一个。视口和剪刀设置也是如此。最好使渲染目标尺寸与画布匹配,以使所有内容都相同,而不是采用越来越多的解决方法,并且必须记住在哪里使用一种尺寸与另一种尺寸。明天我开始使用PointsMaterial。它也与devicePixelRatio有关。通过不打电话,renderer.setPixelRatio那些问题就消失了。
| 归档时间: |
|
| 查看次数: |
621 次 |
| 最近记录: |