在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
相关产品推荐
相关产品推荐

