圆顶图像投影

Dome Image Projection

我正在尝试创建一个将图像投影到圆顶的 GLSL 片段着色器。输入将是 sampler2D 纹理、仰角和方位角。

结果应该类似于下面的 gif。

仰角在 0 到 90 度之间(在此 gif 中它在 -90 到 90 度之间)

.

方位角在 0 到 360 度之间

.

现在我的代码如下所示:

#ifdef GL_ES
precision mediump float;
#endif

uniform float u_time;
uniform vec2 u_resolution;
uniform sampler2D u_texture_0;
uniform sampler2D u_texture_1;

// INPUT
const float azimuth=0.;// clockwise 360 degree
const float altitude=90.;// 0-90 dregree -> 90 = center
const float scale=1.;

// CALC
const float PI=3.14159265359;
const float azimuthRad=azimuth*PI/180.;
const float altitudeNormalization=sin((1.-(altitude/90.)));

float box(in vec2 _st,in vec2 _size){
    _size=vec2(.5)-_size*.5;
    vec2 uv=smoothstep(_size,_size+vec2(.001),_st);
    uv*=smoothstep(_size,_size+vec2(.001),vec2(1.)-_st);
    return uv.x*uv.y;
}

mat2 rotate(float angle){
    return mat2(cos(angle),-sin(angle),sin(angle),cos(angle));
}

void main(){
    vec2 st=gl_FragCoord.xy/u_resolution;
    vec4 color = texture2D(u_texture_1,st); // set background grid

    vec2 vPos=st;

    float aperture=180.;
    float apertureHalf=.5*aperture*(PI/180.);
    float maxFactor=sin(apertureHalf);

    // to unit sphere -> -1 - 1
    vPos=vec2(2.*vPos-1.);

    float l=length(vPos);
    if(l<=1.){
        float x=maxFactor*vPos.x;
        float y=maxFactor*vPos.y;
        float n=length(vec2(x,y));
        float z=sqrt(1.-n*n);
        float r=atan(n,z)/PI;
        float phi=atan(y,x);
        float u=r*cos(phi)+.5;
        float v=r*sin(phi)+.5;
        vec2 uv=vec2(u,v);

        // translate
        vec2 translate=vec2(sin(azimuthRad),cos(azimuthRad));
        uv+=translate*altitudeNormalization;

        // rotate
        uv-=.5;
        uv=rotate(PI-azimuthRad)*uv;
        uv+=.5;

        // scale
        float size=.5*scale;

        float box=box(uv,vec2(.5*size));

        uv.x*=-1.;
        uv.y*=-1.;

        if(box>=.1){
            vec3 b=vec3(box);
            //    gl_FragColor=vec4(b,1.);
            //uv *= box;
            color += texture2D(u_texture_0,uv);
        }
        gl_FragColor= color;

    }

}

如您所见,有两点不对劲,纹理只显示了一部分(我知道我把它剪掉了,这肯定是错误的)和失真也是错误的。如果有任何帮助,我们将不胜感激。

问题是,您使用缩放的 uv 坐标进行框测试:

float size=.5*scale;

float box=box(uv,vec2(.5*size));

做贴图查找贴图的时候要考虑这个比例。此外,您错误地将 0.5 添加到 uv 坐标:

float u=r*cos(phi)+.5;
float v=r*sin(phi)+.5;

在[-1.0, 1.0]范围内设置uv坐标:

vec2 uv = vec2(r*cos(phi), r*sin(phi));

对其进行平移、旋转和缩放(例如const float scale = 8.0;):

// translate
vec2 translate = vec2(sin(azimuthRad), cos(azimuthRad));
uv += translate * altitudeNormalization;

// rotate
uv = rotate(PI-azimuthRad)*uv;

// scale
uv = uv * scale;

uv 坐标从范围 [-1.0, 1.0] 转换为 [0.0, 1.0] 并进行正确的盒子测试:

uv = uv * 0.5 + 0.5; 

vec2 boxtest = step(0.0, uv) * step(uv, vec2(1.0));

if (boxtest.x * boxtest.y > 0.0)
    color += texture2D(u_texture_0, uv);

主要片段着色器:

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution;
    vec4 color = texture2D(u_texture_1,st); // set background grid

    float aperture=180.;
    float apertureHalf=.5*aperture*(PI/180.);
    float maxFactor=sin(apertureHalf);

    // to unit sphere -> -1 - 1
    vec2 vPos = st * 2.0 - 1.0;

    float l=length(vPos);
    if(l<=1.){
        float x = maxFactor*vPos.x;
        float y = maxFactor*vPos.y;
        float n = length(vec2(x,y));
        float z = sqrt(1.-n*n);
        float r = atan(n,z)/PI;
        float phi = atan(y,x);
        float u = r*cos(phi);
        float v = r*sin(phi);
        vec2 uv = vec2(r*cos(phi), r*sin(phi));

        // translate
        vec2 translate = vec2(sin(azimuthRad), cos(azimuthRad));
        uv += translate * altitudeNormalization;

        // rotate
        uv = rotate(PI-azimuthRad)*uv;

        // scale
        uv = uv * scale;

        uv = uv * 0.5 + 0.5; 

        vec2 boxtest = step(0.0, uv) * step(uv, vec2(1.0));

        if (boxtest.x * boxtest.y > 0.0)
            color += texture2D(u_texture_0, uv);
    }

    gl_FragColor = color;
}