html 投影立方体贴图明暗器,边之间的白色

0qx6xfy6  于 2022-12-02  发布在  其他
关注(0)|答案(1)|浏览(124)

我正在尝试编写一个着色器,它可以将立方体贴图/立方体纹理渲染为等矩形投影。
这是完成的主要部分,但我得到的脸之间的白色。
我的方法是:
1.从UV开始([0,1]x[0,1])
1.转换为[-1,1]x[-1,1],然后转换为[-180,180]x[-90,90]
1.这些现在是长纬度,可以转换为3D(xyz)
1.获取它们所属的面以及它们在该面中的位置([-1,1]x[-1,1])
1.将此面位置变换为立方体纹理中的UV
一开始我以为第4步的输出是错误的,我是从纹理外部采样的,但即使在将面坐标乘以1/2后,我仍然得到白色。
参考:https://codepen.io/coutteausam/pen/jOKKYYy

float max3(vec3 v) {
  return max(max(v.x, v.y), v.z);
}

vec2 sample_cube_map_1(vec3 xyz, out float faceIndex) {
  xyz /= length(xyz);

  float m = max3(abs(xyz));

  if (abs(xyz.x) == m) {
    faceIndex = sign(xyz.x);
    return xyz.yz / abs(xyz.x);
  }

  if (abs(xyz.y) == m) {
    faceIndex = 2. * sign(xyz.y);
    return xyz.xz / abs(xyz.y);
  }

  if (abs(xyz.z) == m) {
    faceIndex = 3. * sign(xyz.z);
    return xyz.xy / abs(xyz.z);
  }

  faceIndex = 1.0;
  return vec2(0., 0.);
}

vec2 sample_cube_map(vec3 xyz) {
  float face;

  vec2 xy = sample_cube_map_1(xyz, face);

  xy = (xy + 1.) / 2.; // [-1,1] -> [0,1]

  xy.x = clamp(xy.x, 0., 1.);
  xy.y = clamp(xy.y, 0., 1.);

  if (face == 1.) {
    // front
    xy += vec2(1., 1.);
  }
  else if (face == -1.) {
    //back
    xy.x = 1. - xy.x;
    xy += vec2(3., 1.);
  }
  else if (face == 2.) {
    // right
    xy.x = 1. - xy.x;
    xy += vec2(2., 1.);
  }
  else if (face == -2.) {
    // left
    xy += vec2(0., 1.);
  }
  else if (face == 3.) {
    // top
    xy = vec2(xy.y, 1. - xy.x);
    xy += vec2(1., 2.);
  }
  else if (face == -3.) {
    // bottom
    xy = xy.yx;
    xy += vec2(1., 0.);
  }
  else {
    xy += vec2(1., 0.);
  }

  return xy / vec2(4., 3.); // [0,4]x[0,3] -> [0,1]x[0,1]
}

// projects
//   uv:([0,1] x [0,1])
// to
//   xy:([ -2, 2 ] x [ -1, 1 ])
vec2 uv_2_xy(vec2 uv) {
  return vec2(uv.x * 4. - 2., uv.y * 2. - 1.);
}

// projects
//   xy:([ -2, 2 ] x [ -1, 1 ])
// to
//   longlat: ([ -pi, pi ] x [-pi/2,pi/2])
vec2 xy_2_longlat(vec2 xy) {
  float pi = 3.1415926535897932384626433832795;
  return xy * pi / 2.;
}

vec3 longlat_2_xyz(vec2 longlat) {
  return vec3(cos(longlat.x) * cos(longlat.y), sin(longlat.x) * cos(longlat.y), sin(longlat.y));
}

vec3 uv_2_xyz(vec2 uv) {
  return longlat_2_xyz(xy_2_longlat(uv_2_xy(uv)));
}

vec3 roty(vec3 xyz, float alpha) {
  return vec3(cos(alpha) * xyz.x + sin(alpha) * xyz.z, xyz.y, cos(alpha) * xyz.z - sin(alpha) * xyz.x);
}

varying vec2 vUv;
uniform sampler2D image;
uniform float time;
void main() {
  vec3 xyz = uv_2_xyz(vUv);

  xyz = roty(xyz, time);

  vec2 uv = sample_cube_map(xyz);

  vec4 texturePixel = texture2D(image, vec2(clamp(uv.x, 0., 1.), clamp(uv.y, 0., 1.)));
  gl_FragColor = texturePixel;
}
xfb7svmp

xfb7svmp1#

着色器背后的数学看起来很合理。但是,您需要考虑处理子像素时纹理采样的行为。请查看您的源纹理:

当你穿过TopRight之间的边界时,采样器会尝试将洋红色和青色之间的所有纹理压缩到一个像素中。由于该区域有很多白色,所以压缩后的像素将主要是白色。请注意,在TopFront之间不会发生这种情况,因为这两个面之间没有白色区域。(阅读:mipmapping以查看纹理在缩小时的行为)

溶液:

1.通过关闭mipmapping,您可以对最近的完整像素进行采样,而不是尝试在它们之间进行混合。为此,您可以将纹理的缩小过滤器更改为线性过滤。

const imgTexture = new THREE.TextureLoader().load('https://i.imgur.com/tBzfYG5.png');
imgTexture.minFilter = THREE.LinearFilter;

唯一的缺点是你可能会沿着边界得到一些硬边。

1.如果项目允许,您可以简单地将纹理分解为六个图像,然后使用Three.js提供的cubemap方法

相关问题