0

I am trying to implement the atmospheric scattering shader found here: https://lightshaderdevlog.wordpress.com/

I have implemented the algorithm in GLSL and it looks good, however for some reason black artifacts render: https://i.stack.imgur.com/OZ6t1.jpg

img

It seems that the artifacts are only present when looking at specific camera angles and in those specific vertices.

Here is my code:

Vertex Shader

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

Fragment Shader

#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;
    }
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • this: [simplified atmospheric scattering GLSL shader](https://stackoverflow.com/a/19659648/2521214) might interest you. – Spektre Apr 12 '18 at 05:57
  • Spektre, I greatly admire your shaders, and I have indeed considered using this shader of yours. However, my program needs to able to have more than one planet with an atmosphere. I think this could either be solved either by having the shader consider multiple ellipsoids or have it act on a sphere model instead of a quad. The first option would be much easier, however it would greatly lack in performance. I did consider making this change myself, but I do not consider my understanding of how the shader works enough. it is a great work you have done on that shader, though. – Francisco Ayala Lebrun Apr 12 '18 at 15:27
  • I am using it in my astro program with both Earth and Mars together you just call it with uniform parameters of local planet. – Spektre Apr 12 '18 at 17:50
  • Yes, but I must be able to support two planets in close proximity, not just one at a time – Francisco Ayala Lebrun Apr 13 '18 at 01:50
  • In my engine it works like this 1. Z-sort objects (for different reasons) 2. in actual perspective range layer render objects in Z order. Each object first render geometry and than single quad covering either screen or object size with this shader (if it has atmosphere). In your case you would just need to change the geometry of the quad to match closely your planets (it can be done in fragment) but that is only needed for really close bodies which is instable in nature. If they are apart with gap bigger than their radiuses together quads are enough. – Spektre Apr 13 '18 at 07:05

1 Answers1

2

I have solved my issue. The problem was in the vertex shader's acos functions. For some reason, the dot product between the normal and the light and view directions was giving a value higher than one(Which I am not sure why it happened, given that all of these values should be normalized).

This was simply fixed by checking the value returned by the dot product, and selecting the minimum between it and the value of 1.

Here is the updated vertex shader code:

#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);
    }
  • Values less than -1.0 are no problem, acos only returns undefined when the value is more than 1.0 – Francisco Ayala Lebrun Apr 12 '18 at 16:19
  • 1
    @Rabbid76 that is not entirely true as GLSL floating precision got sometimes out of the range on some implementations even after simple operations. So even if `x` should be algebraicaly in range it sometimes is not... this matters for acos, and even normalizing of vectors sometimes produce vector size above `1` and I am sure there are more examples ... Neglecting such cases (singularities) is the usual cause of unexpected artifacts in shaders. – Spektre Apr 12 '18 at 19:53