Opengl 大气散射着色器中的伪影

Opengl 大气散射着色器中的伪影,opengl,libgdx,shader,light-scattering,Opengl,Libgdx,Shader,Light Scattering,我正在尝试实现此处找到的大气散射着色器: 我已经在GLSL中实现了该算法,它看起来不错,但是出于某些原因,黑色瑕疵呈现: 似乎只有在查看特定摄影机角度和这些特定顶点时,才会出现瑕疵 这是我的密码: 顶点着色器 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; layout (location = 2) in vec3 aNormal; // Vert

我正在尝试实现此处找到的大气散射着色器:

我已经在GLSL中实现了该算法,它看起来不错,但是出于某些原因,黑色瑕疵呈现:

似乎只有在查看特定摄影机角度和这些特定顶点时,才会出现瑕疵

这是我的密码:

顶点着色器

 #version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;

// Vertex
uniform vec3 star_pos;      // point light source position (Sun) [GCS]
uniform vec3 planet_pos;    // planet center position [GCS]
uniform float overglow;     // cos(angle) of terminator propagation\

uniform mat4 modelView;
uniform mat4 projection;
uniform float og_farPlaneDistance;
uniform float u_logarithmicDepthConstant;

out vec3 lpos;          // point light source position (Sun) [camera space]
out vec3 ppos;          // planet center position [camera space]
out vec3 pixel_pos;     // fragment position [camera space]
out vec3 pixel_nor;     // fragment surface normal
out vec2 pixel_txy;     // fragment surface texture coord
out float angleIncidence;
out vec4 colAtmosphere;
out vec3 lightDir;

const float PI = 3.14159265f;
const float transitionWidth = 0.1f;
const float fresnelExponent = 20f;


vec4 modelToClipCoordinates(vec4 position, mat4 modelViewPerspectiveMatrix, float depthConstant, float farPlaneDistance){
    vec4 clip = modelViewPerspectiveMatrix * position;

    clip.z = ((2.0 * log(depthConstant * clip.z + 1.0) / log(depthConstant * farPlaneDistance + 1.0)) - 1.0) * clip.w;
    return clip;
}

void main()
    {


    lpos=(modelView*vec4(star_pos,1.0)).xyz;
    ppos=(modelView*vec4(planet_pos,1.0)).xyz;

    lightDir =normalize(lpos-ppos);

    pixel_pos=vec3(modelView*vec4(aPos,1.0));
    pixel_nor=mat3(transpose(inverse(modelView))) * aNormal;
    pixel_txy=aTexCoord;

    vec3 viewDir = normalize(-pixel_pos);


    angleIncidence = acos(dot(lightDir, pixel_nor)) / PI;

    float shadeFactor = 0.1 * (1 - angleIncidence) + 0.9 * (1 - (clamp(angleIncidence, 0.5, 0.5 + transitionWidth) - 0.5) / transitionWidth);

    float angleToViewer = sin(acos(dot(pixel_nor, viewDir)));

    float perspectiveFactor = 0.3 + 0.2 * pow(angleToViewer, fresnelExponent) + 0.5 * pow(angleToViewer, fresnelExponent * 20);

    colAtmosphere = vec4(perspectiveFactor*shadeFactor);

    gl_Position = modelToClipCoordinates(vec4(aPos, 1.0), projection * modelView, u_logarithmicDepthConstant, og_farPlaneDistance);
    }
片段着色器

#version 330 core
out vec4 FragColor;

// Fragment
uniform vec3 star_pos;      // point light source position (Sun) [GCS]
uniform vec3 planet_pos;    // planet center position [GCS]
//uniform vec3 planet_r;        // planet radius
uniform float overglow;     // cos(angle) of terminator propagation

uniform sampler2D diffuseTex;
uniform sampler2D txratm;
in vec3 lpos;           // point light source position (Sun) [camera space]
in vec3 ppos;           // planet center position [camera space]
in vec3 pixel_pos;      // fragment position [camera space]
in vec3 pixel_nor;      // fragment surface normal
in vec2 pixel_txy;      // fragment surface texture coord
in float angleIncidence;
in vec4 colAtmosphere;
in vec3 lightDir;

void main()
    {
    float li;
    vec3 c,lt_dir,c0;
    vec4 c1;
    lt_dir=lightDir; // vector from fragment to point light source
    li=dot(pixel_nor,lt_dir)+overglow;
    if (li<0.0) {
        li=0.0;
    }
    if (li>1.0) {
        li=1.0;
    }

    vec2 gradientLevel = vec2(angleIncidence, 0.5);
    c1 = colAtmosphere * texture(txratm, gradientLevel) * 1.4;

    c0=texture(diffuseTex,pixel_txy).rgb * li;

    c = c1.a * c1.rgb + (vec3(1.0, 1.0, 1.0) - c1.a) * c0;;


    FragColor=vec4(c,1.0);
//  gl_FragDepth=0.0;
    }
#版本330核心
out vec4 FragColor;
//碎片
统一的vec3星_pos;//点光源位置(太阳)[GCS]
均匀vec3行星_pos;//行星中心位置[GCS]
//均匀vec3行星行星半径
均匀浮动过辉光;//终止传播角
均匀采样;二维扩散;
均匀采样器2D txratm;
在vec3 LPO中;//点光源位置(太阳)[相机空间]
在vec3-ppos中;//行星中心位置[相机空间]
在vec3像素_pos;//碎片位置[相机空间]
在vec3像素_或;//碎片表面法线
在vec2像素_txy;//碎片表面结构坐标
在浮动角度入射;
在大气中;
在vec3-lightDir中;
void main()
{
浮力;
vec3 c,lt_dir,c0 ;;
vec4-c1;
lt_dir=lightDir;//从片段到点光源的向量
li=点(像素_nor,lt_dir)+过辉;
如果(li1.0){
li=1.0;
}
vec2梯度水平=vec2(角入射,0.5);
c1=colAtmosphere*纹理(txratm,梯度级别)*1.4;
c0=纹理(扩散纹理,像素_txy).rgb*li;
c=c1.a*c1.rgb+(vec3(1.0,1.0,1.0)-c1.a)*c0;;
FragColor=vec4(c,1.0);
//gl_FragDepth=0.0;
}

我已经解决了我的问题。问题出在顶点着色器的acos函数中。出于某种原因,法线方向、灯光方向和视图方向之间的点积给出的值高于1(考虑到所有这些值都应该标准化,我不确定为什么会发生这种情况)

通过检查点积返回的值,并在它和值1之间选择最小值,可以简单地解决这个问题

以下是更新的顶点着色器代码:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;

// Vertex
uniform vec3 star_pos;      // point light source position (Sun) [GCS]
uniform vec3 planet_pos;    // planet center position [GCS]
uniform float overglow;     // cos(angle) of terminator propagation\

uniform mat4 modelView;
uniform mat4 projection;
uniform float og_farPlaneDistance;
uniform float u_logarithmicDepthConstant;

out vec3 lpos;          // point light source position (Sun) [camera space]
out vec3 ppos;          // planet center position [camera space]
out vec3 pixel_pos;     // fragment position [camera space]
out vec3 pixel_nor;     // fragment surface normal
out vec2 pixel_txy;     // fragment surface texture coord
out float angleIncidence;
out vec4 colAtmosphere; //color of the atmosphere
out vec3 lightDir; //direction of light in camera space

const float PI = 3.14159265;
const float transitionWidth = 0.1; //How prominent the atmosphere is
const float fresnelExponent = 20;


vec4 modelToClipCoordinates(vec4 position, mat4 modelViewPerspectiveMatrix, float depthConstant, float farPlaneDistance){
    vec4 clip = modelViewPerspectiveMatrix * position;

    clip.z = ((2.0 * log(depthConstant * clip.z + 1.0) / log(depthConstant * farPlaneDistance + 1.0)) - 1.0) * clip.w;
    return clip;
}

void main()
    {


    lpos=(modelView*vec4(star_pos,1.0)).xyz;
    ppos=(modelView*vec4(planet_pos,1.0)).xyz;

    lightDir =normalize(lpos-ppos);

    pixel_pos=vec3(modelView*vec4(aPos,1.0));
    pixel_nor=normalize(mat3(transpose(inverse(modelView))) * aNormal);
    pixel_txy=aTexCoord;

    vec3 viewDir = normalize(-pixel_pos);

    float dotProd = dot(lightDir, pixel_nor);
    dotProd = min(dotProd,1.0);

    angleIncidence = acos(dotProd) / PI;

    float shadeFactor = 0.1 * (1 - angleIncidence) + 0.9 * (1 - (clamp(angleIncidence, 0.5, 0.5 + transitionWidth) - 0.5) / transitionWidth);

    float dotProd2 = dot(pixel_nor, viewDir);
    dotProd2 = min(dotProd2,1.0);

    float angleToViewer = sin(acos(dotProd2));

    float perspectiveFactor = 0.3 + 0.2 * pow(angleToViewer, fresnelExponent) + 0.5 * pow(angleToViewer, fresnelExponent * 20);

    colAtmosphere = vec4(perspectiveFactor*shadeFactor);

    gl_Position = modelToClipCoordinates(vec4(aPos, 1.0), projection * modelView, u_logarithmicDepthConstant, og_farPlaneDistance);
    }

我已经解决了我的问题。问题出在顶点着色器的acos函数中。出于某种原因,法线方向、灯光方向和视图方向之间的点积给出的值高于1(考虑到所有这些值都应该标准化,我不确定为什么会发生这种情况)

通过检查点积返回的值,并在它和值1之间选择最小值,可以简单地解决这个问题

以下是更新的顶点着色器代码:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;

// Vertex
uniform vec3 star_pos;      // point light source position (Sun) [GCS]
uniform vec3 planet_pos;    // planet center position [GCS]
uniform float overglow;     // cos(angle) of terminator propagation\

uniform mat4 modelView;
uniform mat4 projection;
uniform float og_farPlaneDistance;
uniform float u_logarithmicDepthConstant;

out vec3 lpos;          // point light source position (Sun) [camera space]
out vec3 ppos;          // planet center position [camera space]
out vec3 pixel_pos;     // fragment position [camera space]
out vec3 pixel_nor;     // fragment surface normal
out vec2 pixel_txy;     // fragment surface texture coord
out float angleIncidence;
out vec4 colAtmosphere; //color of the atmosphere
out vec3 lightDir; //direction of light in camera space

const float PI = 3.14159265;
const float transitionWidth = 0.1; //How prominent the atmosphere is
const float fresnelExponent = 20;


vec4 modelToClipCoordinates(vec4 position, mat4 modelViewPerspectiveMatrix, float depthConstant, float farPlaneDistance){
    vec4 clip = modelViewPerspectiveMatrix * position;

    clip.z = ((2.0 * log(depthConstant * clip.z + 1.0) / log(depthConstant * farPlaneDistance + 1.0)) - 1.0) * clip.w;
    return clip;
}

void main()
    {


    lpos=(modelView*vec4(star_pos,1.0)).xyz;
    ppos=(modelView*vec4(planet_pos,1.0)).xyz;

    lightDir =normalize(lpos-ppos);

    pixel_pos=vec3(modelView*vec4(aPos,1.0));
    pixel_nor=normalize(mat3(transpose(inverse(modelView))) * aNormal);
    pixel_txy=aTexCoord;

    vec3 viewDir = normalize(-pixel_pos);

    float dotProd = dot(lightDir, pixel_nor);
    dotProd = min(dotProd,1.0);

    angleIncidence = acos(dotProd) / PI;

    float shadeFactor = 0.1 * (1 - angleIncidence) + 0.9 * (1 - (clamp(angleIncidence, 0.5, 0.5 + transitionWidth) - 0.5) / transitionWidth);

    float dotProd2 = dot(pixel_nor, viewDir);
    dotProd2 = min(dotProd2,1.0);

    float angleToViewer = sin(acos(dotProd2));

    float perspectiveFactor = 0.3 + 0.2 * pow(angleToViewer, fresnelExponent) + 0.5 * pow(angleToViewer, fresnelExponent * 20);

    colAtmosphere = vec4(perspectiveFactor*shadeFactor);

    gl_Position = modelToClipCoordinates(vec4(aPos, 1.0), projection * modelView, u_logarithmicDepthConstant, og_farPlaneDistance);
    }

这个:你可能会感兴趣。斯佩克特,我非常欣赏你的着色器,我确实考虑过使用你的着色器。然而,我的程序需要能够有不止一个有大气层的行星。我认为这可以通过让着色器考虑多个椭球或者使它作用于球体模型而不是四边形来解决。第一个选项会容易得多,但是它的性能会非常差。我确实考虑过自己做这个改变,但是我不认为我对着色器的工作原理有足够的了解。这是一个伟大的工作,你已经做了这个着色器,虽然。我在我的astro程序中使用它与地球和火星一起,你只需称之为本地行星的统一参数。是的,但我必须能够支持两个行星在近距离,而不是一次一个在我的引擎它的工作原理是这样的1。Z排序对象(出于不同原因)2。在实际透视范围中,层按Z顺序渲染对象。每个对象首先渲染几何体,然后使用该着色器(如果有大气)覆盖屏幕或对象大小的单个四边形。在你的情况下,你只需要改变四元星的几何结构,使其与你的行星紧密匹配(可以在碎片中完成),但这只适用于本质上不稳定的非常接近的天体。如果它们之间的间距大于它们的半径,那么四角体就足够了。这:可能会引起你的兴趣。Spektre,我非常欣赏你的着色器,我确实考虑过使用你的着色器。然而,我的程序需要能够有不止一个有大气层的行星。我认为这可以通过让着色器考虑多个椭球或者使它作用于球体模型而不是四边形来解决。第一个选项会容易得多,但是它的性能会非常差。我确实考虑过自己做这个改变,但是我不认为我对着色器的工作原理有足够的了解。这是一个伟大的工作,你已经做了这个着色器,虽然。我在我的astro程序中使用它与地球和火星一起,你只需称之为本地行星的统一参数。是的,但我必须能够支持两个行星在近距离,而不是一次一个在我的引擎它的工作原理是这样的1。Z排序对象(出于不同原因)2。在实际透视范围中,层按Z顺序渲染对象。每个对象首先渲染几何体,然后使用该着色器(如果有大气)覆盖屏幕或对象大小的单个四边形。在你的情况下,你只需要改变四元星的几何结构,使其与你的行星紧密匹配(可以在碎片中完成),但这只适用于本质上不稳定的非常接近的天体。如果它们之间的间距大于它们的半径,那么四分之一就足够了。小于-1.0的值没有问题,acos仅在值大于1时返回undefined。0@Rabbid76这并不是完全正确的,因为在一些实现中,GLSL浮点精度有时甚至在简单操作之后也超出范围。所以,即使x在代数范围内,有时也不是。。。这对ACO很重要,甚至向量的规格化有时也会产生大于
1
的向量大小,我相信还有更多的例子。。。忽略这些情况(奇点)是着色器中意外瑕疵的常见原因。acos on表示,小于-1.0的值没有问题