GLSL / THREE.js Math, чтобы превратить фигуру в проблему куба - PullRequest
3 голосов
/ 25 октября 2019

Я делаю код для превращения сферы в куб в вершинном шейдере, но, похоже, он превращается в эту странную форму, моя логика была такой:

enter image description here

Закомментированный код был итеративной версией.

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 );

Но тогда все получилось так:

enter image description here

В это:

enter image description here

Любая помощь приветствуется.

1 Ответ

6 голосов
/ 25 октября 2019

Используйте THREE.BoxBufferGeometry() в качестве базы, затем добавьте еще один атрибут буфера с coodinates для формирования сферы, затем интерполируйте (смешайте) эти координаты поля и сферы в шейдере:

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>
...