planeGeometry 变成了一个球体

planeGeometry turned into a sphere

我觉得我的逻辑在这里并不太糟糕。我正在尝试使用其 UV 坐标作为纬度和经度将 planeGeometry 转换为球体。本质上这是逻辑:

  1. 将uv坐标分别转换为lat/long
  2. 将 lat/long 改为弧度
  3. 转换为 x,y,z catesian 坐标

这是我正在试用的顶点着色器的代码:

    varying vec2 vUv;

    #define PI 3.14159265359

    void main() {
      vUv = uv;

      float lat = (uv.x - 0.5) * 90.0;
      float lon = abs((uv.y - 0.5) * 180.0);

      float latRad = lat * (PI / 180.0);
      float lonRad = lon * (PI / 180.0);

      float x = sin(latRad) * sin(lonRad);
      float y = cos(latRad);
      float z = cos(latRad) * sin(lonRad);

      gl_Position = projectionMatrix * modelViewMatrix * vec4(x,y,z, 0.5);
    }

感谢任何建议,我觉得我只是在逻辑上遗漏了一些小东西,但我相信大众。

编辑:

这个问题最终变得非常愚蠢,我只是为 planeGeometry 参数传递了错误的值。这里所有的解决方案都是有效的。

This is how THREE.js does it:

setFromSphericalCoords( radius, phi, theta ) {

    const sinPhiRadius = Math.sin( phi ) * radius;

    this.x = sinPhiRadius * Math.sin( theta );
    this.y = Math.cos( phi ) * radius;
    this.z = sinPhiRadius * Math.cos( theta );

    return this;
}

据此,您的 z 线似乎已切换。尝试:

float z = sin(latRad) * cos(lonRad);

我也不明白在声明位置向量时使用 0.5 的意义4。只需使用 1.0:vec4(x,y,z, 1.0);

最后,您通过度数使事情复杂化,然后在乘以 *90 再除以 /180 时又回到弧度。如果您已经有了 [0, 1] 范围,只需乘以 PI 和 PI * 2 相应地转换为弧度。

如果我想将平面变形为球体,我总是在着色器中使用 three.js 的球坐标实现:

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import { OrbitControls } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";
import { GUI } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/libs/lil-gui.module.min.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 1).setLength(6);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.update();
console.log(controls.getAzimuthalAngle())

let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0.25, 0.5, 1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

let u = {
    mixVal: {value: 0}
}

let g = new THREE.PlaneGeometry(2 * Math.PI, Math.PI, 100, 100);
let m = new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg"),
  onBeforeCompile: shader => {
    shader.uniforms.mixVal = u.mixVal;
    shader.vertexShader = `
        uniform float mixVal;
      vec3 fromSpherical(float radius, float phi, float theta){
        float sinPhiRadius = sin( phi ) * radius;

        float x = sinPhiRadius * sin( theta );
        float y = cos( phi ) * radius;
        float z = sinPhiRadius * cos( theta );

        return vec3(x, y, z);
      }
      ${shader.vertexShader}
    `.replace(
        `#include <begin_vertex>`,
      `#include <begin_vertex>
        float phi = (1. - uv.y) * PI;
        float theta = uv.x * PI * 2. + PI;
        float r = 1.;
        transformed = mix(position, fromSpherical(r, phi, theta), mixVal);
      `
    );
    //console.log(shader.vertexShader);
  }
});
let box = new THREE.Mesh(g, m);
scene.add(box);

let gui = new GUI();
gui.add(u.mixVal, "value", 0, 1).name("mixVal");

window.addEventListener("resize", onWindowResize);

renderer.setAnimationLoop(() => {
    renderer.render(scene, camera);
})

function onWindowResize() {

  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);

}
</script>