将极坐标映射到webgl着色器uv

时间:2016-08-20 22:40:40

标签: three.js webgl

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

这显示了问题: enter image description here 水平红色渐变是U轴,当我的atan从0变为2 * PI时,从Red = 1变为Red = 0。但atan将2 * PI视为零,因此在渐变变黑后右侧有一个红色条带。 (顶部和底部也有红色条带,但这与V值有关,我忽略了这个问题)。

使用three.js'查看此图片。显示顶点的能力: enter image description here

你可以看到最右边的顶点(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.);
}

1 个答案:

答案 0 :(得分:3)

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

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.如果它们从零变为&gt; 1(或小于0)你遇到与上面相同的问题,某些顶点需要多于1种颜色。您基本上需要使用片段着色器解决方案