You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js中创建无缝自定义CubeMap?

问题:Three.js创建无缝CubeMap时的接缝与扭曲问题

我正在开发一个Three.js项目,目标是创建无缝CubeMap。目前用六个平面拼接成立方体,通过着色器在表面生成非纹理噪声,但立方体各面间已出现扭曲。

我在着色器中实现了网格逆变换来固定噪声位置,生成的类CubeMap对象初看无缝,但边缘存在细微扭曲;转换为法线贴图后光照效果变差,各面衔接处始终有接缝:

  • 正面底部边缘与底部顶部边缘存在扭曲
  • 放大后可见扭曲汇聚情况

我查阅了多种资料,未找到针对Three.js创建无缝CubeMap的全面教程;尝试过带噪声的球体加中心CubeCamera方案,仍存在接缝。

可运行代码示例

HTML代码

<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>

JavaScript代码

let camera,scene,mesh,renderer

//-----------noise functions
function permute(){
  return  `
  vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
  `
 }
function taylorInvSqrt(){
return     `
    vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
    `
}
function snoise(){
return `
      float snoise3D(vec3 v){ 
        const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
        const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);
      
        vec3 i  = floor(v + dot(v, C.yyy) );
        vec3 x0 =   v - i + dot(i, C.xxx) ;
      
        vec3 g = step(x0.yzx, x0.xyz);
        vec3 l = 1.0 - g;
        vec3 i1 = min( g.xyz, l.zxy );
        vec3 i2 = max( g.xyz, l.zxy );
      
        vec3 x1 = x0 - i1 + 1.0 * C.xxx;
        vec3 x2 = x0 - i2 + 2.0 * C.xxx;
        vec3 x3 = x0 - 1. + 3.0 * C.xxx;
      
        i = mod(i, 289.0 );
        vec4 p = permute( permute( permute( 
                   i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
                 + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 
                 + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
      
        float n_ = 1.0/7.0;
        vec3  ns = n_ * D.wyz - D.xzx;
      
        vec4 j = p - 49.0 * floor(p * ns.z *ns.z);
      
        vec4 x_ = floor(j * ns.z);
        vec4 y_ = floor(j - 7.0 * x_ );
      
        vec4 x = x_ *ns.x + ns.yyyy;
        vec4 y = y_ *ns.x + ns.yyyy;
        vec4 h = 1.0 - abs(x) - abs(y);
      
        vec4 b0 = vec4( x.xy, y.xy );
        vec4 b1 = vec4( x.zw, y.zw );
      
        vec4 s0 = floor(b0)*2.0 + 1.0;
        vec4 s1 = floor(b1)*2.0 + 1.0;
        vec4 sh = -step(h, vec4(0.0));
      
        vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
        vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
      
        vec3 p0 = vec3(a0.xy,h.x);
        vec3 p1 = vec3(a0.zw,h.y);
        vec3 p2 = vec3(a1.xy,h.z);
        vec3 p3 = vec3(a1.zw,h.w);
      
        vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
        p0 *= norm.x;
        p1 *= norm.y;
        p2 *= norm.z;
        p3 *= norm.w;
      
        vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
        m = m * m;
        return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 
                                      dot(p2,x2), dot(p3,x3) ) );
      }
      `
}

// -------- Create a custom shader 
const vertexShader = `
varying vec4 worldPosition;
uniform  int ignoreFront; //<---- this is just a flag to ignore calling the undoTransfroms function for the front face
uniform  mat4 rm;
uniform  vec3 undoPoition;

vec3 undoTransfroms(vec3 v, mat4 rm){
  vec4 j =  (rm*vec4(v,1.));
  j.z += undoPoition.z;
  j.y += undoPoition.y;
  j.x += undoPoition.x;
  return j.xyz;
}

void main() {
    worldPosition = modelMatrix * vec4(position, 1.0);
    vec3 newPosition = position;
    if(ignoreFront == 1){
      newPosition = undoTransfroms( position,  rm);
    }
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);

}
`;

const fragmentShader = `
uniform vec3 center;
varying vec4 worldPosition;

${taylorInvSqrt()}
${permute()}
${snoise()}

void main() {
    float n = snoise3D(normalize(worldPosition.xyz-center));
    gl_FragColor = vec4(vec3(n), 1.0);
}
`;


//--------build mesh
function createPlaneMesh(x, y, z, rotationX, rotationY, rotationZ, uniforms) {
// Create a plane geometry
const planeGeometry = new THREE.PlaneGeometry(10, 10, 10, 10);
uniforms.center = {value:new THREE.Vector3(0,0,-5)}
const planeMaterial = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
});
// Create the plane mesh
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
// Set the position of the mesh
planeMesh.position.set(x, y, z);
// Set the rotation of the mesh
planeMesh.rotation.set(rotationX, rotationY, rotationZ);
return planeMesh;
}
//------------


function init(){
//-----------Basic setUp
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );
renderer.setClearColor( 'white' )

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 100 );
camera.position.z = 20;
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene = new THREE.Scene();

/********
- set creat mesh 
- set transfroms 
- set unifroms for undoing of transfoms
*********/
let widthHeight = 10

//------------front
let frontUnifrom = {ignoreFront:{value:0}}
let front = createPlaneMesh(0,0,0,0,0,0,frontUnifrom)
scene.add( front );
//-----------back
let bz    = -widthHeight
let bry   = Math.PI
var undorotationMatrix = new THREE.Matrix4();
undorotationMatrix.makeRotationY(-bry);
let backUnifrom = {rm:{value:undorotationMatrix},undoPoition:{value:new THREE.Vector3(bz*2,0,bz)},ignoreFront:{value:1}}
let back  = createPlaneMesh(0,0,bz,0,bry,0,backUnifrom)
scene.add( back );
//---------right
let rz    = -(widthHeight)/2;
let rx    =  (widthHeight)/2;
let rry   =  Math.PI/2;
var undorotationMatrix = new THREE.Matrix4();
undorotationMatrix.makeRotationY(-rry);
let rightUnifrom = {rm:{value:undorotationMatrix},undoPoition:{value:new THREE.Vector3(-rx,0,-rz)},ignoreFront:{value:1}}
let right = createPlaneMesh(rx,0,rz,0,rry,0,rightUnifrom)
scene.add( right );
//---------left
let lz    =  -(widthHeight)/2;
let lx    =  -(widthHeight)/2;
let lry   =  -Math.PI/2;
var undorotationMatrix = new THREE.Matrix4();
undorotationMatrix.makeRotationY(-lry);
let leftUnifrom = {rm:{value:undorotationMatrix},undoPoition:{value:new THREE.Vector3(-lx,0,-lz)},ignoreFront:{value:1}}
let left  = createPlaneMesh(lx,0,lz,0,lry,0,leftUnifrom)
scene.add( left );
//--------top
let tz    =  -(widthHeight)/2;
let ty    =  (widthHeight)/2;
let trx   =  -Math.PI/2;
var undorotationMatrix = new THREE.Matrix4();
undorotationMatrix.makeRotationX(-trx);
let topUnifrom = {rm:{value:undorotationMatrix},undoPoition:{value:new THREE.Vector3(0,-ty,-tz)},ignoreFront:{value:1}}
let top  = createPlaneMesh(0,ty,tz,trx,0,0,topUnifrom)
scene.add( top );
//---------bottom
let boz   =  -(widthHeight)/2;
let boy   =  -(widthHeight)/2;
let borx  =  Math.PI/2;
var undorotationMatrix = new THREE.Matrix4();
undorotationMatrix.makeRotationX(-borx);
let boUnifrom = {rm:{value:undorotationMatrix},undoPoition:{value:new THREE.Vector3(0,-boy,-boz)},ignoreFront:{value:1}}
let bo    = createPlaneMesh(0,boy,boz,borx,0,0,boUnifrom)
scene.add( bo );
}

// animation
init()

function animation( time ) {
    renderer.render( scene, camera );
}

内容的提问来源于stack exchange,提问作者Memory1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:02:34