我正在尝试编写一个着色器,它可以将立方体贴图/立方体纹理渲染为等矩形投影。
这是完成的主要部分,但我得到的脸之间的白色。
我的方法是:
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;
}
1条答案
按热度按时间xfb7svmp1#
着色器背后的数学看起来很合理。但是,您需要考虑处理子像素时纹理采样的行为。请查看您的源纹理:
当你穿过
Top
到Right
之间的边界时,采样器会尝试将洋红色和青色之间的所有纹理压缩到一个像素中。由于该区域有很多白色,所以压缩后的像素将主要是白色。请注意,在Top
到Front
之间不会发生这种情况,因为这两个面之间没有白色区域。(阅读:mipmapping以查看纹理在缩小时的行为)溶液:
1.通过关闭mipmapping,您可以对最近的完整像素进行采样,而不是尝试在它们之间进行混合。为此,您可以将纹理的缩小过滤器更改为线性过滤。
唯一的缺点是你可能会沿着边界得到一些硬边。
1.如果项目允许,您可以简单地将纹理分解为六个图像,然后使用Three.js提供的cubemap方法