0
\$\begingroup\$

I have the following vertex and fragment shaders, adapted from this shader on shadertoy.

Vertex:

#version 330

layout (location = 0) in vec4 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec3 in_UV;

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

uniform vec3 cameraPosition;
uniform vec3 lightPosition;
uniform vec3 planetPosition;

out vec3 cameraPositionModel;
out vec3 lightPositionModel;
out vec3 positionModel;
out vec3 planetPositionModel;

void main()
{
    gl_Position = projection * view * model * vec4(in_position.xyz, 1.0);

    mat4 worldToModel = inverse(model);
    cameraPositionModel = vec3(worldToModel * vec4(cameraPosition, 1.0));
    lightPositionModel = vec3(worldToModel * vec4(lightPosition, 1.0));
    positionModel = in_position.xyz;//vec3(model * vec4(in_position.xyz, 1.0));
    planetPositionModel = vec3(worldToModel * vec4(planetPosition, 1.0));
}

Fragment:

#version 330

layout (location = 0) out vec4 color;

in vec3 cameraPositionModel;
in vec3 lightPositionModel;
in vec3 positionModel;
in vec3 planetPositionModel;

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

uniform float innerRadius;
uniform float outerRadius;

const float PI = 3.14159265359;

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

vec3 ray_direction(vec3 cam)
{
    vec3 ray = positionModel - cam;
    return normalize(ray);
}

vec2 ray_sphere_intersect(vec3 rayOrigin, vec3 rayDirection,
                          vec3 sphereOrigin, float sphereRadius)
{
    float a = dot(rayDirection, rayDirection);
    vec3 s0_r0 = rayOrigin - sphereOrigin;
    float b = 2.0 * dot(rayDirection, s0_r0);
    float c = dot(s0_r0, s0_r0) - (sphereRadius * sphereRadius);
    float disc = b * b - 4.0 * a * c;
    if (disc < 0.0) {
        return vec2(-1.0, -1.0);
    }else{
        return vec2(-b - sqrt(disc), -b + sqrt(disc)) / (2.0 * a);
    }
}

float mie_phase(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 ( 3.0 / 8.0 / PI ) * a / b;
}

float rayleigh_phase(float cc)
{
    return ( 3.0 / 16.0 / PI ) * ( 1.0 + cc );
}

float density(vec3 p, float ph)
{
    return exp(-max(length(p) - innerRadius, 0.0) / ph);
}

float optic(vec3 p, vec3 q, float ph)
{
    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, ph);
        v += step;
    }
    sum *= length(step);
    return sum;
}

vec3 in_scatter(vec3 o, vec3 dir, vec2 e, vec3 l)
{
    const float ph_ray = 0.05;
    const float ph_mie = 0.02;

    const vec3 k_ray = vec3( 3.8, 13.5, 33.1 );
    const vec3 k_mie = vec3( 21.0 );
    const float k_mie_ex = 1.1;

    vec3 sum_ray = vec3( 0.0 );
    vec3 sum_mie = vec3( 0.0 );

    float n_ray0 = 0.0;
    float n_mie0 = 0.0;

    float len = ( e.y - e.x ) / fNumInScatter;
    vec3 s = dir * len;
    vec3 v = o + dir * ( e.x + len * 0.5 );

    for (int i = 0; i < numInScatter; i++, v += s) {
        float d_ray = density( v, ph_ray ) * len;
        float d_mie = density( v, ph_mie ) * len;

        n_ray0 += d_ray;
        n_mie0 += d_mie;

        vec2 f = ray_sphere_intersect(v, l, vec3(0), outerRadius);
        vec3 u = v + l * f.y;

        float n_ray1 = optic(v, u, ph_ray);
        float n_mie1 = optic(v, u, ph_mie);

        vec3 att = exp(-(n_ray0 + n_ray1) * k_ray -
                       (n_mie0 + n_mie1) * k_mie * k_mie_ex);

        sum_ray += d_ray * att;
        sum_mie += d_mie * att;
    }

    float c  = dot( dir, -l );
    float cc = c * c;
    vec3 scatter =
        sum_ray * k_ray * rayleigh_phase( cc ) +
        sum_mie * k_mie * mie_phase( -0.78, c, cc );

    return 10.0 * scatter;
}

void main(void)
{
    vec3 lightDirection = normalize(lightPositionModel - positionModel);

    vec3 cameraDirection = -ray_direction(cameraPositionModel - positionModel);

    vec2 e = ray_sphere_intersect(cameraPositionModel,
                                  cameraDirection,
                                  vec3(0),
                                  outerRadius);

    vec2 f = ray_sphere_intersect(cameraPositionModel,
                                  cameraDirection,
                                  vec3(0),
                                  innerRadius);

    e.y = min(e.y, f.x);

    vec3 I = in_scatter(cameraPositionModel, cameraDirection, e, lightDirection);

    color = vec4(pow(I, vec3(1.0 / 2.2)), 1.0);
}

cameraPosition, lightPosition, and planetPosition are all provided in world coordinates. Two spheres are rendered. The first is a regular sphere with Phong shading. The second, slightly larger sphere using the above shader.

It results in an image like the following when the spheres are positioned at 0, 0, 0.

enter image description here

But if the planet is positioned differently, you can see how the atmosphere no longer lines up correctly with the shading on the inner sphere. I.e., the above shader isn't taking into consideration the correct placement of the planet/light/camera in reference to planet/light/camera.

enter image description here

enter image description here

My understanding of the vertex shader was that I had moved the planetPosition, lightPosition, and cameraPosition into the models local space, and then in the fragment shader all the calculations were being done in reference to the models space. But either this doesn't work when factoring in the eye-ray, or I haven't correctly updated the positions from world-space to model-space.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Can't test this or anything, but it seems to me there is a mistake in calculating the "cameraDirection" variable. You are calling a function that expects the position of the camera as an argument, but you are instead passing the camera-to-fragment vector to it. \$\endgroup\$
    – PepeOjeda
    Commented Jul 7, 2022 at 10:19

1 Answer 1

2
+250
\$\begingroup\$

Ironically, I don't have enough reputation to comment, but I can post an answer. I agree with @PepeOjeda, your calculation of cameraDirection doesn't make sense. I think you should be calling normalize where you're calling ray_direction. Separately, you're passing vec3(0) as the sphere position into ray_sphere_intersect, and I think you actually want to pass the sphere position (planetPositionModel I'm assuming) instead. Those are the only problems I see.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .