GLSL 大气散射不随变换缩放

Posted

技术标签:

【中文标题】GLSL 大气散射不随变换缩放【英文标题】:GLSL Atmospheric Scattering Not Scaling With Transformations 【发布时间】:2016-07-02 17:15:05 【问题描述】:

我正在尝试在 GLSL 4.10 版中实现大气散射。我正在调整来自这个 Shadertoy 着色器https://www.shadertoy.com/view/lslXDr 的着色器。我的程序中的气氛是由行星球体的缩放版本创建的。

我有实际的散射方程,但对于大多数相机位置,大气的内半径与球体的外半径不一致。我知道这是因为大气层的半径比行星球体大,但我似乎无法正确缩放。

我的问题在这里得到了最好的说明。模型在这些图片中按比例放大。可以看出,大气层内半径与行星(深蓝色球体)的半径不匹配。

在这里,模型被缩放和平移。大气层偏离了相机的中心,内部大气层仍未与行星对齐。

这里是顶点着色器,本质上是一个直通着色器

#version 410

in vec4 vPosition;
in vec3 vNormal;

out vec3 fPosition;
out mat3 m;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
   
    fPosition = vec3(vPosition);
    m = mat3(model);

    gl_Position = projection*view*model*vPosition;

还有片段着色器。

#version 410

uniform float time;
uniform vec3 camPosition;
uniform float fInnerRadius;
uniform float fOuterRadius;

in vec3 fPosition;
in mat3 m;

out vec4 FragColor;

const float PI = 3.14159265359;
const float degToRad = PI / 180.0;
const float MAX = 10000.0;

float K_R = 0.166;
const float K_M = 0.0025;
const float E = 14.3;
const vec3 C_R = vec3(0.3, 0.7, 1.0);
const float G_M = -0.85;

float SCALE_H = 4.0 / (fOuterRadius - fInnerRadius);
float SCALE_L = 1.0 / (fOuterRadius - fInnerRadius);

const int numOutScatter = 10;
const float fNumOutScatter = 10.0;
const int numInScatter = 10;
const float fNumInScatter = 10.0;

vec3 rayDirection(vec3 camPosition) 
    vec3 ray = m*fPosition - camPosition;
    float far = length(ray);
    return ray /= far;


vec2 rayIntersection(vec3 p, vec3 dir, float radius ) 
    float b = dot( p, dir );
    float c = dot( p, p ) - radius * radius;

    float d = b * b - c;
    if ( d < 0.0 ) 
        return vec2( MAX, -MAX );
    
    d = sqrt( d );

    float near = -b - d;
    float far = -b + d;

    return vec2(near, far);
 

// Mie
// g : ( -0.75, -0.999 )
//      3 * ( 1 - g^2 )               1 + c^2
// F = ----------------- * -------------------------------
//      2 * ( 2 + g^2 )     ( 1 + g^2 - 2 * g * c )^(3/2)
float miePhase( float g, float c, float cc ) 
    float gg = g * g;

    float a = ( 1.0 - gg ) * ( 1.0 + cc );

    float b = 1.0 + gg - 2.0 * g * c;
    b *= sqrt( b );
    b *= 2.0 + gg;  

    return 1.5 * a / b;


// Reyleigh
// g : 0
// F = 3/4 * ( 1 + c^2 )
float rayleighPhase( float cc ) 
    return 0.75 * ( 1.0 + cc );



float density(vec3 p) 
    return exp(-(length(p) - fInnerRadius) * SCALE_H);


float optic(vec3 p, vec3 q) 
    vec3 step = (q - p) / fNumOutScatter;
    vec3 v = p + step * 0.5;

    float sum = 0.0;
    for(int i = 0; i < numOutScatter; i++) 
        sum += density(v);
        v += step;
    
    sum *= length(step)*SCALE_L;
    return sum;


vec3 inScatter(vec3 o, vec3 dir, vec2 e, vec3 l) 
    float len = (e.y - e.x) / fNumInScatter;
    vec3 step = dir * len;
    vec3 p = o + dir * e.x;
    vec3 v = p + dir * (len * 0.5);

    vec3 sum = vec3(0.0);
    for(int i = 0; i < numInScatter; i++) 
        vec2 f = rayIntersection(v, l, fOuterRadius);
        vec3 u = v + l * f.y;
        float n = (optic(p, v) + optic(v, u))*(PI * 4.0);
        sum += density(v)* exp(-n * ( K_R * C_R + K_M ));
        v += step;
    
    sum *= len * SCALE_L;
    float c = dot(dir, -l);
    float cc = c * c;
    return sum * ( K_R * C_R * rayleighPhase( cc ) + K_M * miePhase( G_M, c, cc ) ) * E;


void main (void)

    vec3 dir = rayDirection(vec3(camPosition.x, 0.0, camPosition.z));
    vec3 eye = vec3(camPosition.x, 0.0, camPosition.z);

    vec3 l = normalize(vec3(0.0, 0.0, 1.0));

    vec2 e = rayIntersection(eye, dir, fOuterRadius);
    if ( e.x > e.y ) 
        discard;
    
    vec2 f = rayIntersection(eye, dir, fInnerRadius);
    e.y = min(e.y, f.x);

    vec3 I = inScatter(eye, dir, e, l);

    FragColor = vec4(I, 1.0);

如果需要这里是吸引气氛的代码。绘制行星的代码在没有 scaleFactor 的情况下具有基本相同的转换。

void drawAtmosphere()

    glUseProgram(atmosphereShader);

    v = getViewMatrix();
    vec3 Position = getCameraPosition();

    float scaleFactor = 1.25;
    m = multiplymat4(translate(0.0, 0.0, -10), scale(fScale*scaleFactor));
    float fOuter = (fScale*scaleFactor);
    float fInner = fScale;

    glUniform1f(glGetUniformLocation(atmosphereShader, "fInnerRadius"), fInner);
    glUniform1f(glGetUniformLocation(atmosphereShader, "fOuterRadius"), fOuter);
    glUniform3f(glGetUniformLocation(atmosphereShader, "camPosition"), Position.x, Position.y, Position.z);
    glUniform1f(glGetUniformLocation(atmosphereShader, "time"), glfwGetTime());

    initMVP(atmosphereShader, m, v);

    glBindVertexArray (atmosphereVAO);
    glDrawArrays( GL_TRIANGLES, 0, planet.vertexNumber);
    glBindVertexArray(0);


感谢任何帮助或任何可以为我指明正确方向的内容。

【问题讨论】:

这段代码不是来自scratchapixel.com/lessons/…吗?也许如果你理解了理论,你可能会在你的代码中找到什么是行不通的...... 您可能会发现此post 有助于解决您的问题。 最近修复了着色器,它们大部分都可以工作。现在,当对象按比例放大时,它会破裂。编辑了我的问题以反映问题。 这看起来像是一个精度或溢出问题。 谢谢,看来这就是问题所在。现已修复 【参考方案1】:

发现问题是由于相机位置计算不正确,没有考虑到物体的模型空间造成的。我上传了代码here 的精简版。

希望这将帮助任何尝试实施 Sean O'Neil 的氛围代码的人。

【讨论】:

我正在使用您的代码,当地球处于 0.0.0 位置时,一切正常。如果我移动地球,它看起来大气是在 0.0.0 位置上以某种方式计算出来的。你知道怎么解决吗? 我已经很久没有处理这个问题了,但看起来我在某个时候修复了它。试试看这里 github.com/TJGreen0211/PySolar/blob/master/libs/starsystem/… 和这里 github.com/TJGreen0211/PySolar/blob/master/shaders/… 。我相信这是 getCameraPosition 函数的变化 我按照您的方式尝试过,但如果它不在 0.0.0 坐标上,它仍然无法正确显示。它比地球小或大,并且看起来像在 0.0.0 上一样按比例缩放。还有其他建议吗?提前致谢

以上是关于GLSL 大气散射不随变换缩放的主要内容,如果未能解决你的问题,请参考以下文章

UE4实现纹理不随物体缩放(WorldAlignedTexture)

Android ImageView 大小不随源图像缩放

QTabBar 选项卡大小不随样式表字体缩放

大气光散射实现

大气散射模型的推导过程

案例学习——Unity基于体绘制的大气散射shader