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

在Three.js中实现立方体拉伸Shader以生成立方体贴图

实现Three.js天空盒式立方体贴图的正确方法

你的核心问题是混淆了局部面UV和天空盒方向向量的作用:普通立方体的vUv是每个面独立的0-1坐标,所以Shader会在每个面单独渲染;而天空盒效果需要基于相机指向的方向来计算颜色/采样纹理,而非依赖面的局部坐标。

为什么直接替换vUv会失效?

  • 用gl_Position直接传递的是裁剪空间坐标,范围是[-w, w],未做归一化处理的话,数值范围不符合颜色计算的需求,导致单色。
  • mvPosition是视图空间的顶点位置,直接使用时没有归一化得到方向向量,同样无法提供正确的采样依据。

正确的顶点着色器写法

我们需要传递从相机指向立方体表面的方向向量,有两种常用实现方式:

方式1:传递世界空间方向向量

// vertex-shader
varying vec3 vWorldDirection;

void main() {
    // 将顶点转换到世界空间
    vec4 worldPosition = modelMatrix * vec4(position, 1.0);
    // 计算从相机到顶点的方向并归一化
    vWorldDirection = normalize(worldPosition.xyz - cameraPosition);
    
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

方式2:传递视图空间方向向量(更简洁)

// vertex-shader
varying vec3 vViewDirection;

void main() {
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
    // 视图空间中,mvPosition.xyz就是从相机出发到顶点的向量,直接归一化得到方向
    vViewDirection = normalize(mvPosition.xyz);
    
    gl_Position = projectionMatrix * mvPosition;
}

片元着色器如何使用?

以模拟天空+太阳的效果为例,使用视图空间方向向量的片元着色器:

// fragment-shader
varying vec3 vViewDirection;

// 可自定义的参数
uniform vec3 sunDir; // 太阳方向(视图空间)
uniform vec3 skyColor; // 天空底色
uniform vec3 sunColor; // 太阳颜色

void main() {
    // 计算视线方向与太阳方向的夹角
    float sunDot = dot(normalize(vViewDirection), normalize(sunDir));
    // 强化太阳的高光效果
    float sunIntensity = pow(clamp(sunDot, 0.0, 1.0), 16.0);
    
    // 混合天空与太阳颜色
    vec3 finalColor = mix(skyColor, sunColor, sunIntensity);
    gl_FragColor = vec4(finalColor, 1.0);
}

如果要采样立方体贴图,只需要在片元着色器中使用textureCube函数:

// fragment-shader
varying vec3 vViewDirection;
uniform samplerCube skybox;

void main() {
    gl_FragColor = textureCube(skybox, vViewDirection);
}

关键原理总结

天空盒的本质是基于相机视角的方向采样,而非立方体每个面的局部纹理映射。通过传递归一化的方向向量,Shader可以将整个立方体渲染成包裹相机的连续天空效果,而非六个独立的面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 07:33:34