Samuel
Samuel

Reputation: 219

GLSL/THREE.js Math to turn a shape into a cube issue

I am making code to turn a sphere into a cube in a vertex shader, but it seems to turn into this weird shape, my logic was this:

enter image description here

The commented out code was the iterative version.

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

But then that turns this:

enter image description here

Into this:

enter image description here

Any help appreciated.

Upvotes: 3

Views: 223

Answers (1)

prisoner849
prisoner849

Reputation: 17596

Use THREE.BoxBufferGeometry() as a base, then add another buffer attribute with coodinates for a sphere formation, then interpolate (mix) those coordinates of the box and the sphere in the shader:

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>

If you want to get a cube from a sphere, you can clamp vertices to min and max vectors of a bounding box you need (but accuracy of this approach depends on the amount of vertices of the sphere):

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>

Upvotes: 6

Related Questions