首页 > 解决方案 > GLSL/THREE.js 将形状变成立方体的数学问题

问题描述

我正在编写代码以在顶点着色器中将球体变成立方体,但它似乎变成了这种奇怪的形状,我的逻辑是这样的:

在此处输入图像描述

注释掉的代码是迭代版本。

vec3 p = position;

    if(true)
    {
      if(p.y<s&&p.y>-s){
       p.x = -(p.x-s);//p.x-=(p.x-s)*t*0.1;
      }
      if(p.x<s&&p.x>-s){
       p.y = -(p.y-s);//p.y-=(p.y-s)*t*0.1;
      }
    }
    gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );

但这会变成这样:

在此处输入图像描述

进入这个:

在此处输入图像描述

任何帮助表示赞赏。

标签: three.jsglsl

解决方案


THREE.BoxBufferGeometry()用作基础,然后添加另一个带有坐标的缓冲区属性以形成球体,然后在着色器中插入(混合)盒子和球体的坐标:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(1, 3, 5);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

scene.add(new THREE.GridHelper(10, 10));

var side = 2;
var rad = Math.sqrt(3) * 0.5 * side; // radius of the sphere is a half of cube's diagonal

var geom = new THREE.BoxBufferGeometry(side, side, side, 10, 10, 10);

var pos = geom.attributes.position;
var spherePos = []; // array of coordinates for the sphere formation
var vec3 = new THREE.Vector3(); // vector for re-use
for (let i = 0; i < pos.count; i++) {
  vec3.fromBufferAttribute(pos, i).setLength(rad); // create coordinate for the sphere formation
  spherePos.push(vec3.x, vec3.y, vec3.z);
}
geom.addAttribute("spherePos", new THREE.BufferAttribute(new Float32Array(spherePos), 3));

var mat = new THREE.ShaderMaterial({
  uniforms: {
    mixShapes: {
      value: 0
    }
  },
  vertexShader: `
  uniform float mixShapes;
  
  attribute vec3 spherePos;
  
  void main() {
    vec3 pos = mix(position, spherePos, mixShapes); // interpolation between shapes
    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  }
  `,
  fragmentShader: `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 1.0,1.0);
  }
  `,
  wireframe: true
});

var shape = new THREE.Mesh(geom, mat);
scene.add(shape);

var gui = new dat.GUI();
gui.add(mat.uniforms.mixShapes, "value", 0.0, 1.0).name("mixShapes");

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera)
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>

如果你想从一个球体中得到一个立方体,你可以将顶点夹在你需要的边界框的最小和最大向量上(但这种方法的准确性取决于球体的顶点数量):

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(1, 3, 5);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

scene.add(new THREE.GridHelper(10, 10));

var side = 2;
var rad = Math.sqrt(3) * 0.5 * side; // radius of the sphere is a half of cube's diagonal

var geom = new THREE.SphereBufferGeometry(rad, 36, 36);

var mat = new THREE.ShaderMaterial({
  uniforms: {
    mixShapes: {
      value: 0
    }
  },
  vertexShader: `
  uniform float mixShapes;
  
  attribute vec3 spherePos;
  
  void main() {
    vec3 pos = clamp(position, vec3(${-side * 0.5}), vec3(${side * 0.5})); // clamp to min and max vectors
    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  }
  `,
  fragmentShader: `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 1.0,1.0);
  }
  `,
  wireframe: true
});

var shape = new THREE.Mesh(geom, mat);
scene.add(shape);

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera)
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>


推荐阅读