Three.js: пользовательские размеры для каждого квадра, используя InstancedBufferGeometry + ShaderMaterial - PullRequest
0 голосов
/ 21 декабря 2018

Я пытаюсь создать сцену, полную точек, каждая из которых имеет уникальную ширину и высоту.Тем не менее, я не могу понять, как попасть в вершинный шейдер, чтобы перемещать вершины для создания пользовательских размеров.Вот сцена:

// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);

// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
camera.position.set(0, 1, -10);

// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);

// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

/**
* Add the points
**/

var BA = THREE.BufferAttribute;
var IBA = THREE.InstancedBufferAttribute;
var geometry  = new THREE.InstancedBufferGeometry();

var n = 10000, // number of observations
    rootN = n**(1/2),
    cellSize = 20,
    translations = new Float32Array(n * 3),
    widths = new Float32Array(n),
    heights = new Float32Array(n),
    translationIterator = 0,
    widthIterator = 0,
    heightIterator = 0;
for (var i=0; i<n*3; i++) {
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  widths[widthIterator++] = Math.random() * 20;
  heights[heightIterator++] = Math.random() * 20;
}

// coordinates for template box
var size = 10,
    verts = [
  0, 0, 0, // lower left
  size, 0, 0, // lower right
  size, size, 0, // upper right
  0, size, 0, // upper left
]

var positionAttr = new BA(new Float32Array(verts), 3),
    translationAttr = new IBA(translations, 3, true, 1),
    widthAttr = new IBA(widths, 1, true, 1),
    heightAttr = new IBA(heights, 1, true, 1);

// we make two triangles but only use 4 distinct vertices in the object
// the second argument to THREE.BufferAttribute is the number of elements
// in the first argument per vertex
geometry.setIndex([0,1,2, 2,3,0])

geometry.addAttribute('position', positionAttr);
geometry.addAttribute('translation', translationAttr);
geometry.addAttribute('width', widthAttr);
geometry.addAttribute('height', heightAttr);

var material = new THREE.RawShaderMaterial({
  vertexShader: document.getElementById('vertex-shader').textContent,
  fragmentShader: document.getElementById('fragment-shader').textContent,
});
material.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
scene.add(mesh);

// render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// draw some geometries
var geometry = new THREE.TorusGeometry(10, 3, 16, 100);
var material = new THREE.MeshNormalMaterial({ color: 0xffff00 });

render();
html, body { width: 100vw; height: 100vh; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100vw; height: 100vh; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

uniform vec3 cameraPosition;

attribute vec3 position; // sets the blueprint's vertex positions
attribute vec3 translation; // x y translation offsets for an instance
attribute float width;
attribute float height;

void main() {
  // set point position
  vec3 pos = position + translation;
  vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_Position = projected;
}
</script>

<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;

void main() {
  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>

Мой вопрос: как я могу использовать атрибуты ширины и высоты, чтобы каждая точка имела заданные (относительные) пропорции, определяемые ее шириной изначения высоты?Могу ли я запросить индекс вершины, нарисованной в данном экземпляре?Любые предложения будут очень полезны!

1 Ответ

0 голосов
/ 22 декабря 2018

Ах, похоже, в вершинном шейдере можно сделать следующее:

void main() {
  // set point position
  vec3 pos = position + translation;
  pos.x = pos.x * width;
  pos.y = pos.y * height;

  vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_Position = projected;
}

Полный фрагмент:

// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);

// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
camera.position.set(0, 1, -10);

// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);

// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

/**
* Add the points
**/

var BA = THREE.BufferAttribute;
var IBA = THREE.InstancedBufferAttribute;
var geometry  = new THREE.InstancedBufferGeometry();

var n = 10000, // number of observations
    rootN = n**(1/2),
    cellSize = 20,
    translations = new Float32Array(n * 3),
    widths = new Float32Array(n),
    heights = new Float32Array(n),
    translationIterator = 0,
    widthIterator = 0,
    heightIterator = 0;
for (var i=0; i<n*3; i++) {
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  widths[widthIterator++] = Math.random() * 20;
  heights[heightIterator++] = Math.random() * 20;
}

// coordinates for template box
var size = 10,
    verts = [
  0, 0, 0, // lower left
  size, 0, 0, // lower right
  size, size, 0, // upper right
  0, size, 0, // upper left
]

var positionAttr = new BA(new Float32Array(verts), 3),
    translationAttr = new IBA(translations, 3, true, 1),
    widthAttr = new IBA(widths, 1, true, 1),
    heightAttr = new IBA(heights, 1, true, 1);

// we make two triangles but only use 4 distinct vertices in the object
// the second argument to THREE.BufferAttribute is the number of elements
// in the first argument per vertex
geometry.setIndex([0,1,2, 2,3,0])

geometry.addAttribute('position', positionAttr);
geometry.addAttribute('translation', translationAttr);
geometry.addAttribute('width', widthAttr);
geometry.addAttribute('height', heightAttr);

var material = new THREE.RawShaderMaterial({
  vertexShader: document.getElementById('vertex-shader').textContent,
  fragmentShader: document.getElementById('fragment-shader').textContent,
});
material.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
scene.add(mesh);

// render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// draw some geometries
var geometry = new THREE.TorusGeometry(10, 3, 16, 100);
var material = new THREE.MeshNormalMaterial({ color: 0xffff00 });

render();
html, body { width: 100vw; height: 100vh; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100vw; height: 100vh; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

uniform vec3 cameraPosition;

attribute vec3 position; // sets the blueprint's vertex positions
attribute vec3 translation; // x y translation offsets for an instance
attribute float width;
attribute float height;

void main() {
  // set point position
  vec3 pos = position + translation;
  vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_Position = projected;
}
</script>

<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;

void main() {
  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
...