test-skybox/CatalogSkybox.js

165 lines
4.1 KiB
JavaScript
Raw Normal View History

2024-08-14 08:48:59 +00:00
/**
* 天空盒子相关计算函数
*/
2024-08-13 10:56:27 +00:00
const CatalogSkybox = {
};
/**
* 由立方体盒子上的坐标(单位长度为2)转换为对应盒子面上的图片的坐标
*
* @param {number} x 立方盒子的x坐标,[-1,1]
* @param {number} y 立方盒子的y坐标,[-1,1]
* @param {number} z 立方盒子的z坐标,[-1,1]
* @returns {i,j,faceId} i为图片的x坐标(向右),[0,1],j为图片的y坐标(向下),[0,0]为图片的左上角
2024-08-14 08:48:59 +00:00
*
2024-08-13 10:56:27 +00:00
* faceId为图片的面id,[0,5]
2024-08-14 08:48:59 +00:00
*
* 0: mx, 1: my, 2: px, 3: py, 4: pz, 5: mz
2024-08-13 10:56:27 +00:00
*/
CatalogSkybox.cubeXyzToImgUV = function(x, y, z) {
let faceId, i, j;
2024-08-13 14:39:49 +00:00
if (x === -1) { // back,mx
j = (1.0 + y) / 2.0;
2024-08-13 10:56:27 +00:00
i = (1.0 + z) / 2.0;
faceId = 0;
2024-08-13 14:39:49 +00:00
} else if (y === -1) { // left,my
2024-08-13 10:56:27 +00:00
i = (1.0 + x) / 2.0;
2024-08-13 14:39:49 +00:00
j = (1.0 + z) / 2.0;
2024-08-13 10:56:27 +00:00
faceId = 1;
2024-08-13 14:39:49 +00:00
} else if (x === 1) { // front,px
2024-08-14 08:48:59 +00:00
j = (1.0 + y) / 2.0;
i = (1.0 - z) / 2.0;
2024-08-13 10:56:27 +00:00
faceId = 2;
2024-08-13 14:39:49 +00:00
} else if (y === 1) { // right,py
2024-08-13 10:56:27 +00:00
i = (1.0 + x) / 2.0;
2024-08-13 14:39:49 +00:00
j = (1.0 - z) / 2.0;
2024-08-13 10:56:27 +00:00
faceId = 3;
2024-08-14 08:48:59 +00:00
} else if (z === 1) { // top,pz
2024-08-13 10:56:27 +00:00
i = (1.0 + x) / 2.0;
2024-08-14 08:48:59 +00:00
j = (1.0 + y) / 2.0;
2024-08-13 10:56:27 +00:00
faceId = 4;
2024-08-14 08:48:59 +00:00
} else if (z === -1) { // bottom,mz
2024-08-13 10:56:27 +00:00
i = (1.0 - x) / 2.0;
2024-08-14 08:48:59 +00:00
j = (1.0 + y) / 2.0;
2024-08-13 10:56:27 +00:00
faceId = 5;
}
else {
throw new Error("x,y,z其中一个必须为1或-1");
}
if (i > 1) {
i = 1;
}
if (i < 0) {
i = 0;
}
if (j > 1) {
j = 1;
}
if (j < 0) {
j = 0;
}
return { i, j, faceId };
}
const Pi1_4 = Math.PI / 4.0;
const Pi3_4 = 3 * Math.PI / 4.0;
/**
* 天球坐标系坐标(赤经,赤纬)转换为立方体盒子(单位为2上的坐标
*
* @param {number} theta 赤经,弧度,[-PI,PI]
* @param {number} phi 赤纬,弧度,[-PI/2,PI/2]
*/
CatalogSkybox.sphere2CubeXyz = function (theta, phi) {
// 计算笛卡尔坐标单位长度为1的球表面
let x = Math.cos(theta) * Math.cos(phi);
let y = Math.sin(theta) * Math.cos(phi);
let z = Math.sin(phi);
const tanPhi = Math.tan(phi);
let cosThe;
// 将笛卡尔坐标映射到立方体盒子(单位为2)上
// x = 1,或 z = 1,-1
if (theta >= -Pi1_4 && theta < Pi1_4) {
cosThe = Math.cos(theta);
if (tanPhi > cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = 1;
}
else if (tanPhi < -cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = -1;
}
else {
y = y / Math.abs(x);
z = z / Math.abs(x);
x = 1;
}
}
// y = 1,或 z = 1,-1
else if (theta >= Pi1_4 && theta < Pi3_4) {
cosThe = Math.cos(theta - Math.PI / 2);
if (tanPhi > cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = 1;
}
else if (tanPhi < -cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = -1;
}
else {
x = x / Math.abs(y);
z = z / Math.abs(y);
y = 1;
}
}
// x = -1,或 z = 1,-1
else if (theta >= Pi3_4 || theta < -Pi3_4) {
cosThe = Math.cos(theta - Math.PI);
if (tanPhi > cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = 1;
}
else if (tanPhi < -cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = -1;
}
else {
y = y / Math.abs(x);
z = z / Math.abs(x);
x = -1;
}
}
// y = -1,或 z = 1,-1
else if (theta >= -Pi3_4 && theta < -Pi1_4) {
cosThe = Math.cos(theta + Math.PI / 2);
if (tanPhi > cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = 1;
}
else if (tanPhi < -cosThe) {
x = x / Math.abs(z);
y = y / Math.abs(z);
z = -1;
}
else {
x = x / Math.abs(y);
z = z / Math.abs(y);
y = -1;
}
}
return { x, y, z };
}
export default CatalogSkybox;