【问题标题】:map polar coordinates to webgl shader uv将极坐标映射到 webgl 着色器 uv
【发布时间】:2016-10-21 17:53:33
【问题描述】:

在我的 WebGL 着色器中,我想根据范围为 [0,2*PI) 的函数 (atan) 的输出来映射纹理的 U 值。但是 U 的范围(正如 texture2D 所期望的)是 [0,1]。所以我试图将一个开放区间映射到一个封闭区间。

这说明了问题: 水平红色渐变是 U 轴,当我的 atan 从 0 变为 2*PI 时,从 Red=1 变为 Red=0。但是 atan 将 2*PI 视为零,因此在渐变变黑后右侧有一条红色带。 (顶部和底部也有红色带,但这是与 V 值有关的类似问题,出于本问题的目的,我将忽略它)。

使用 three.js 显示顶点的能力查看此图像:

您可以看到最右边的顶点 (U=1) 是如何再次对应于 atan=0 而不是 2*PI 的红色。

关于如何完成此任务的任何建议?我不能强迫 atan 返回 2*PI。我不想平铺纹理。我可以以某种方式将 U 值映射到开区间吗?

我一直认为一定有一个简单的解决方案,但我已经尝试了所有我能想到的解决方案。

这是我的顶点着色器:

void main()
{

    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;

    // convert from uv to polar coords
    vec2 tempuv = uv;
    theta = (1.0-tempuv[1]) * PI;
    phi = PI * 2.0 * tempuv[0];

    // convert polar to cartesian. Theta is polar, phi is azimuth.
    x = sin(theta)*cos(phi);
    y = sin(theta)*sin(phi);
    z = cos(theta);

    // and convert back again to demonstrate problem.
    // problem: the phi above is [0,2*PI]. this phi is [0,2*PI)
    phi = atan2(y, x);
    if (phi < 0.0) {
        phi = phi + PI*2.0; 
    }
    if (phi > (2.0 * PI)) { // allow 2PI since we gen uv over [0,1]
        phi = phi - 2.0 * PI;
    }
    theta = acos(z);

    // now get uv in new chart.
    float newv = 1.0 - theta/PI;
    float newu = phi/(2.0 * PI);
    vec2 newuv = vec2(newu, newv);
    vUv = newuv;
}

这是我的片段着色器:

void main() {
    vec2 uv = vUv;
    gl_FragColor = vec4(1.0- uv[0],0.,0.,1.);
}

【问题讨论】:

  • 除以 2 * PI,并编写一个调用 atan 的函数,当 atan(x) > 2PI - precision 时,返回 1。
  • 这行不通,因为下一个小于 1.0 的 'u' 对应于下一个顶点,我们不想弄乱那个颜色。
  • phi = PI * 2.0 * tempuv[0] 替换为phi = PI * 2.0 * tempuv[0] *0.9999999;,这样您将无法访问2*PI

标签: three.js webgl


【解决方案1】:

正如您所提到的,看待问题的一种方法是,1 在边缘出现 0。但是另一种看待它的方式是,如果您将 uv 更改为从 0 到 2 而不是 0 到 1,然后使用 fract(uv) 您会多次遇到相同的问题,因为您有效地对函数进行采样,并且每个点只能选择一种颜色,而要正确映射它,您需要一些方法让每个点神奇地为顶点选择两种颜色,这些顶点需要一种颜色用于向左插值,另一种用于向右插值。

fract(uv * 2.) 为例

var vs = `
#define PI radians(180.)

attribute vec4 position;
attribute vec2 texcoord;

varying vec2 vUv;

void main() {
    gl_Position = position;

    // convert from uv to polar coords
    vec2 tempuv = fract(texcoord * 2.);

    float theta = (1.0-tempuv[1]) * PI;
    float phi = PI * 2.0 * tempuv[0];

    // convert polar to cartesian. Theta is polar, phi is azimuth.
    float x = sin(theta)*cos(phi);
    float y = sin(theta)*sin(phi);
    float z = cos(theta);

    // and convert back again to demonstrate problem.
    // problem: the phi above is [0,2*PI]. this phi is [0,2*PI)
    phi = atan(y, x);
    if (phi < 0.0) {
        phi = phi + PI * 2.0; 
    }
    if (phi > (2.0 * PI)) { // allow 2PI since we gen uv over [0,1]
        phi = phi - 2.0 * PI;
    }
    theta = acos(z);

    // now get uv in new chart.
    float newv = 1.0 - theta/PI;
    float newu = phi/(2.0 * PI);
    vec2 newuv = vec2(newu, newv);
    vUv = newuv;
}
`;

var fs = `
precision mediump float;
varying vec2 vUv;
void main() {
    vec2 uv = vUv;
    gl_FragColor = vec4(1.0- uv[0],0.,0.,1.);
}
`;

var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var bufferInfo = twgl.primitives.createPlaneBufferInfo(
  gl, 2, 2, 20, 20, m4.rotationX(Math.PI * .5));

twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);
body { margin: 0 }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>

将代码移动到片段着色器可以有效地解决它。

将代码移至片段着色器的示例

var vs = `
attribute vec4 position;
attribute vec2 texcoord;

varying vec2 vUv;

void main() {
    gl_Position = position;
    vUv = texcoord;
}
`;

var fs = `
precision mediump float;
varying vec2 vUv;

#define PI radians(180.)

void main() {

    // convert from uv to polar coords
    vec2 tempuv = vUv;

    float theta = (1.0-tempuv[1]) * PI;
    float phi = PI * 2.0 * tempuv[0];

    // convert polar to cartesian. Theta is polar, phi is azimuth.
    float x = sin(theta)*cos(phi);
    float y = sin(theta)*sin(phi);
    float z = cos(theta);

    // and convert back again to demonstrate problem.
    // problem: the phi above is [0,2*PI]. this phi is [0,2*PI)
    phi = atan(y, x);
    if (phi < 0.0) {
        phi = phi + PI * 2.0; 
    }
    if (phi > (2.0 * PI)) { // allow 2PI since we gen uv over [0,1]
        phi = phi - 2.0 * PI;
    }
    theta = acos(z);

    // now get uv in new chart.
    float newv = 1.0 - theta/PI;
    float newu = phi/(2.0 * PI);
    vec2 newuv = vec2(newu, newv);
    gl_FragColor = vec4(1.0- newuv[0],0.,0.,1.);
}
`;

var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var bufferInfo = twgl.primitives.createPlaneBufferInfo(
  gl, 2, 2, 20, 20, m4.rotationX(Math.PI * .5));

twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);
body { margin: 0 }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>

将其保留为顶点着色器的一种解决方案就是捏造数字,使它们介于 0.00005 和 0.99995 之间。

var vs = `
#define PI radians(180.)

attribute vec4 position;
attribute vec2 texcoord;

varying vec2 vUv;

void main() {
    gl_Position = position;

    // convert from uv to polar coords
    vec2 tempuv = texcoord * 0.9999 + 0.00005;

    float theta = (1.0-tempuv[1]) * PI;
    float phi = PI * 2.0 * tempuv[0];

    // convert polar to cartesian. Theta is polar, phi is azimuth.
    float x = sin(theta)*cos(phi);
    float y = sin(theta)*sin(phi);
    float z = cos(theta);

    // and convert back again to demonstrate problem.
    // problem: the phi above is [0,2*PI]. this phi is [0,2*PI)
    phi = atan(y, x);
    if (phi < 0.0) {
        phi = phi + PI * 2.0; 
    }
    if (phi > (2.0 * PI)) { // allow 2PI since we gen uv over [0,1]
        phi = phi - 2.0 * PI;
    }
    theta = acos(z);

    // now get uv in new chart.
    float newv = 1.0 - theta/PI;
    float newu = phi/(2.0 * PI);
    vec2 newuv = vec2(newu, newv);
    vUv = newuv;
}
`;

var fs = `
precision mediump float;
varying vec2 vUv;
void main() {
    vec2 uv = vUv;
    gl_FragColor = vec4(1.0- uv[0],0.,0.,1.);
}
`;

var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var bufferInfo = twgl.primitives.createPlaneBufferInfo(
  gl, 2, 2, 20, 20, m4.rotationX(Math.PI * .5));

twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);
body { margin: 0 }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>

这只是有效的,因为 texcoords 从 0 变为 1。如果它们从 0 变为 > 1(或小于 0),您将遇到与上述相同的问题,即某些顶点需要超过 1 种颜色。您基本上需要使用片段着色器解决方案

【讨论】:

  • 非常感谢!当然,这很明显:将代码移至片段着色器。早该想到的。
猜你喜欢
  • 2018-06-21
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多