【问题标题】:Scale BufferGeometry objects from their center从中心缩放 BufferGeometry 对象
【发布时间】:2018-01-18 02:49:43
【问题描述】:

我有大量随机放置的圆圈保存在单个缓冲区几何体中。我正在尝试根据来自它们中心的音频输入来缩放每一个,但它们会一直缩放到场景的中心。它们不会变得更大半径并保持静止,而是会变得更大,但也会远离中心。

uniform float scale;
varying vec3 color;
void main() 
{

  mat4 sPos = mat4(vec4(scale,0.0,0.0,0.0),
               vec4(0.0,scale,0.0,0.0),
               vec4(0.0,0.0,1.0,0.0),
               vec4(0.0,0.0,0.0,1.0));
  vec3 pos = position * normal;
  vec4 modelViewPosition = modelViewMatrix * sPos * vec4(pos, 1.0) ;

  gl_Position = projectionMatrix * modelViewPosition;
}

这就是我正在尝试的。是否有一个矩阵变换可以将原点移动到每个圆的中心而不会弄乱场景的其他方面?

小尺度值

更大的刻度值

【问题讨论】:

  • 因为你有three.js 标记,这是纯粹从three.js 的角度来看的评论。)你是否使用单个缓冲区,因为多个网格会降低性能?您是否考虑过使用实例 (InstancedBufferGeometry)?
  • 是的,使用 circlebuffergeometry 帧速率下降得很快(~100-250 网格)。我没有考虑过使用它,但我也不熟悉它。拥有约 3000 - 5000 个实例并保持 60fps 听起来合理吗?
  • circleBufferGeometry 评论的后续问题:您是在重复使用几何对象,还是为每个网格创建一个新的 circleBufferGeometry?我无法推测通过使用实例您会看到什么样的性能提升。您将失去单个绘制调用(一个网格)的效率,但获得多个网格的易用性。

标签: three.js shader glsles buffer-geometry


【解决方案1】:

添加正常的*比例

uniform float scale;
varying vec3 color;
void main() 
{
  vec3 pos = position + normal * scale;
  vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;

  gl_Position = projectionMatrix * modelViewPosition;
}

例子:

const vs = `
uniform float scale;
void main() 
{
  vec3 pos = position + normal * scale;
  vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
  gl_Position = projectionMatrix * modelViewPosition;
}
`;

const fs = `
void main() {
  gl_FragColor = vec4(1,0,0,1);
}
`;

const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;

const scene = new THREE.Scene();

const uniforms = {
  scale: { value: 1 },
};

var shaderMaterial = new THREE.ShaderMaterial({
  uniforms:       uniforms,
  vertexShader:   vs,
  fragmentShader: fs,
});

const geometry = new THREE.BufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const totalPoints = numPointsPerCircle * numCircles;
const positions = new Float32Array(totalPoints * 3);
const normals = new Float32Array(totalPoints * 3);

const radius = 10;
let off = 0;

for (let c = 0; c < numCircles; ++c) {
  const cx = rand(-100, 100);
  const cy = rand(-100, 100);
  for (let p = 0; p < numEdgePointsPerCircle; ++p) {
    const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
    const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
    positions[off + 0] = cx;
    positions[off + 1] = cy;
    positions[off + 2] = 0;
    positions[off + 3] = cx + Math.cos(a0) * radius;
    positions[off + 4] = cy + Math.sin(a0) * radius;
    positions[off + 5] = 0;
    positions[off + 6] = cx + Math.cos(a1) * radius;
    positions[off + 7] = cy + Math.sin(a1) * radius;
    positions[off + 8] = 0;
    normals[off + 0] = 0;        
    normals[off + 1] = 0;        
    normals[off + 2] = 0;
    normals[off + 3] = Math.cos(a0);        
    normals[off + 4] = Math.sin(a0);        
    normals[off + 5] = 0;
    normals[off + 6] = Math.cos(a1);        
    normals[off + 7] = Math.sin(a1);        
    normals[off + 8] = 0;
    off += 9;
  }
}
 
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));

const mesh = new THREE.Mesh( geometry, shaderMaterial );

scene.add(mesh);

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector("canvas"),
});

function rand(min, max) {
  return min + Math.random() * (max - min);
}

function resize(renderer, camera, force) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  if (force || canvas.width !== width || canvas.height !== height) {
    renderer.setSize(width, height, false);  // pass false so three doesn't mess up the css
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}

function render(time) {
  time *= 0.001;  // seconds
  
  resize(renderer, camera);
  
  uniforms.scale.value = Math.sin(time) * 5;
  
  renderer.render(scene, camera);

  requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>

仅使用中心点的示例

const vs = `
uniform float scale;
void main() 
{
  vec3 pos = position + normal * scale;
  vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
  gl_Position = projectionMatrix * modelViewPosition;
}
`;

const fs = `
void main() {
  gl_FragColor = vec4(1,0,0,1);
}
`;

const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;

const scene = new THREE.Scene();

const uniforms = {
  scale: { value: 1 },
};

var shaderMaterial = new THREE.ShaderMaterial({
  uniforms:       uniforms,
  vertexShader:   vs,
  fragmentShader: fs,
});

const geometry = new THREE.BufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const totalPoints = numPointsPerCircle * numCircles;
const positions = new Float32Array(totalPoints * 3);
const normals = new Float32Array(totalPoints * 3);

const radius = 10;
let off = 0;

for (let c = 0; c < numCircles; ++c) {
  const cx = rand(-100, 100);
  const cy = rand(-100, 100);
  for (let p = 0; p < numEdgePointsPerCircle; ++p) {
    const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
    const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
    positions[off + 0] = cx;
    positions[off + 1] = cy;
    positions[off + 2] = 0;
    positions[off + 3] = cx;
    positions[off + 4] = cy;
    positions[off + 5] = 0;
    positions[off + 6] = cx;
    positions[off + 7] = cy;
    positions[off + 8] = 0;
    normals[off + 0] = 0;        
    normals[off + 1] = 0;        
    normals[off + 2] = 0;
    normals[off + 3] = Math.cos(a0);        
    normals[off + 4] = Math.sin(a0);        
    normals[off + 5] = 0;
    normals[off + 6] = Math.cos(a1);        
    normals[off + 7] = Math.sin(a1);        
    normals[off + 8] = 0;
    off += 9;
  }
}
 
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));

const mesh = new THREE.Mesh( geometry, shaderMaterial );

scene.add(mesh);

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector("canvas"),
});

function rand(min, max) {
  return min + Math.random() * (max - min);
}

function resize(renderer, camera, force) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  if (force || canvas.width !== width || canvas.height !== height) {
    renderer.setSize(width, height, false);  // pass false so three doesn't mess up the css
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}

function render(time) {
  time *= 0.001;  // seconds
  
  resize(renderer, camera);
  
  uniforms.scale.value = 10 + Math.sin(time) * 5;
  
  renderer.render(scene, camera);

  requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>

使用实例化的示例

const vs = `
attribute vec3 center;
uniform float scale;
void main() 
{
  vec3 pos = center + position * scale;
  vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
  gl_Position = projectionMatrix * modelViewPosition;
}
`;

const fs = `
void main() {
  gl_FragColor = vec4(1,0,0,1);
}
`;

const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;

const scene = new THREE.Scene();

const uniforms = {
  scale: { value: 1 },
};

var shaderMaterial = new THREE.ShaderMaterial({
  uniforms:       uniforms,
  vertexShader:   vs,
  fragmentShader: fs,
});

// Just need one circle and 1 center point per circle

const geometry = new THREE.InstancedBufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const centers = new Float32Array(numCircles * 3);
const positions = new Float32Array(numPointsPerCircle * 3);

const radius = 10;
let off = 0;

for (let c = 0; c < numCircles; ++c) {
  const cx = rand(-100, 100);
  const cy = rand(-100, 100);
  centers[c * 2 + 0] = cx;
  centers[c * 2 + 1] = cy;
}

for (let p = 0; p < numEdgePointsPerCircle; ++p) {
  const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
  const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
  positions[off + 0] = 0;        
  positions[off + 1] = 0;        
  positions[off + 2] = 0;
  positions[off + 3] = Math.cos(a0);        
  positions[off + 4] = Math.sin(a0);        
  positions[off + 5] = 0;
  positions[off + 6] = Math.cos(a1);        
  positions[off + 7] = Math.sin(a1);        
  positions[off + 8] = 0;
  off += 9;
}
 
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('center', new THREE.InstancedBufferAttribute(centers, 3));

const mesh = new THREE.Mesh( geometry, shaderMaterial );

scene.add(mesh);

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector("canvas"),
});

function rand(min, max) {
  return min + Math.random() * (max - min);
}

function resize(renderer, camera, force) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  if (force || canvas.width !== width || canvas.height !== height) {
    renderer.setSize(width, height, false);  // pass false so three doesn't mess up the css
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}

function render(time) {
  time *= 0.001;  // seconds
  
  resize(renderer, camera);
  
  uniforms.scale.value = 10 + Math.sin(time) * 5;
  
  renderer.render(scene, camera);

  requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>

【讨论】:

  • 这似乎不起作用。他们现在只是几乎不知不觉地扩展。查看了您编辑的帖子,建议保留对圆圈中心的参考。你认为我应该试一试吗?
  • 除非您的法线不好,否则添加法线应该可以。增加你的规模。你的圈子有多大?法线通常是单位向量,因此比例为 1 将增加 1 个单位。如果你的圈子大 1 个单位,那么它们的大小会翻倍。如果您的圆有 100 个单位大,则它们只会在比例为 1 时增长到 101。使用其他解决方案与将特定圆的所有位置设置为中心点相同,着色器将保持不变。如果你保持法线,你基本上会得到相同的结果。
  • 只是为了让它更混乱,尽管您可以使用实例化,这样您就不必重复每个圆的每个点的中心点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多