Transparency versus surface orientation

    The other day, turning a plastic cup in my hands, I noticed that the visible transparency of the plastic depends on the angle at which you look at the surface - if you look perpendicular to the surface, the background is clearly visible, and if you look along the surface, the material becomes almost opaque. This phenomenon interested me, and I decided to build a mathematical model.

    No sooner said than done. Under the cutout, the formula output, fragment shader code and a small demo.

    We assume that the material is optical homogeneous - its optical properties are independent of direction. Then the change in transparency is caused by different path lengths of the light beam in the thickness of the material.



    , where is the unit normal vector to the surface, is the unit direction vector to the observer.

    Let the apparent transparency of the material be determined by the opacity coefficient, which determines the mixing of the color of the material with the background color as follows:

    Let's see how the value depends on the thickness of the material. To do this, we break the material layer with a thickness and opacity coefficient into layers of the same thickness , each with an opacity coefficient . Let is the background color, is the color of the material, and is the color at the output of each layer.



    But also , and hence:


    Ie with increasing thickness, the apparent transparency of the material decreases exponentially.

    Let the thickness , then:


    So, the desired formula:



    The following is the fragment shader code that implements this formula:

    varying vec4 v_color;
    varying vec3 v_normal;
    varying vec3 v_eye;
    void main(void)
    {
        // Вектора нормали и направления на наблюдателя не нормализированы
        float cosPhi = dot(v_normal, v_eye) / sqrt( dot(v_normal, v_normal) * dot(v_eye, v_eye) );
        // Косинус берется по модулю, на случай если наблюдатель находится сзади поверхности
        // Чтобы избежать деления на нуль, значение косинуса принудительно ограничено снизу
        // 0.999 ^ 10000 = 4.5173346E-5
        float alpha = 1.0 - pow(1.0 - v_color.a, 1.0 / max(abs(cosPhi), 0.00001));
        gl_FragColor = vec4(vec3(v_color), alpha);
    };
    

    The result is this:


    Demo source code available at bitbucket.org

    Also popular now: