Купольная проекция изображения - PullRequest
1 голос
/ 15 февраля 2020

Я пытаюсь создать фрагментный шейдер GLSL, который проецирует изображение на купол. Входными данными будут текстура сэмплера 2D, высота и азимут.

Результат должен выглядеть следующим образом:

Высота между 0 и 90 градусами (в этом gif это между -90 и 90)

Valid XHTML.

Азимут от 0 до 360 градусов

Valid XHTML.

Прямо сейчас мой код выглядит так:

#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;

    }

}

Как вы можете видеть, есть две неправильные вещи: текстура отображается только частично (я знаю, что я как бы вырезал ее, что, безусловно, неправильно) и искажение также неверно. Любая помощь будет аппетитной.

1 Ответ

2 голосов
/ 15 февраля 2020

Проблема в том, что вы используете масштабированные координаты ультрафиолетового излучения для бокс-теста:

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;

Установите UV-координаты в диапазоне [-1,0, 1,0]:

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;
}
...