как отрисовать тень, полученную путём перекрывания объектами источника света? GLSL

не могу разобраться с тенями. понимаю, как это сделать, но тем не менее попытки приводят к неудачным исходам. идея заключается в том, что мы от точки пересечения плоскости или фигуры и луча камеры посылаем ещё один луч от этой самой точки до источника света. но на выходе получается следующая картинка: введите сюда описание изображения

в случае, если посылать луч от источника света к точке пересечения, то получится уже лучше, но всё таки не то, что нужно. взгляните сами: введите сюда описание изображения

возможно, нужно перемещать базис и преобразовывать координаты сферы из вида камеры на базис с началом в точке пересечения луча камеры и фигур (то есть в моём случае только плоскости).

высылаю код shader.frag:

#version 130

uniform vec2 u_WeiHei;
uniform float u_time;

const float MAX_DIST = 99999.0;
const vec4 n_field = vec4(0.0, 1.0, 0.0, 1.0);

vec2 sphereIntersect(in vec3 ro, in vec3 rd, in vec3 ce, float ra) {
    vec3 co = ro - ce;
    float a = dot(rd, rd);
    float b = 2 * dot(rd, co);
    float c = dot(co, co) - ra * ra;
    float D = b * b - 4.0 * a * c;
    if (D < 0.0) return vec2(-1.0);
    D = sqrt(D);
    vec2 t = vec2((-b - D) / (2.0 * a), (-b + D) / (2.0 * a));
    return t;
}

float fieldIntersect(in vec3 ro, in vec3 rd) {
    float denom = dot(rd, n_field.xyz);
    //if (abs(denom) < 1e-6) return -1.0;
    float t = (-dot(n_field.xyz, ro) - n_field.w) / denom;
    return t;
}

void shadows(in vec3 intersect_point, in vec3 light, in vec3 sphere_origin, inout vec3 color) {
    vec3 rd = normalize(vec3(intersect_point - light));
    vec2 t_sphere = sphereIntersect(light, rd, sphere_origin, 1.0);
    if (t_sphere.x > 0.0) color *= 0.5;
}

vec3 castRay(vec3 ro, vec3 rd) {
    vec3 light = normalize(vec3(cos(u_time), 0.75, sin(u_time)));
    vec3 color;
    vec3 intersect_point; vec3 normal_line; float t_min = MAX_DIST;
    vec3 sphere_origin = vec3(0.0, 0.0, 0.0);

    vec2 t_sphere = sphereIntersect(ro, rd, sphere_origin, 1.0);
    float t_field = fieldIntersect(ro, rd);

    if (t_sphere.x > 0.0 && t_sphere.x < t_min) {
        t_min = t_sphere.x;
        intersect_point = ro + t_sphere.x * rd;
        normal_line = normalize(intersect_point - sphere_origin);
        color = vec3(1.0, 0.2, 0.1);
    }
    if (t_field > 0.0 && t_field < t_min) {
        t_min = t_field;
        intersect_point = ro + t_field * rd;
        normal_line = n_field.xyz;
        color = vec3(0.5, 0.5, 0.5);
    }
    if (t_min == MAX_DIST) return vec3(-1.0);
    else {
        shadows(intersect_point, light, sphere_origin, color);
        float diffuse = max(0.0, dot(light, normal_line)) * 0.5;
        vec3 reflected = rd - 2.0 * dot(normal_line, rd) * normal_line;
        float specular = pow(max(0.0, dot(reflected, light)), 32.0);
        color *= mix(diffuse, specular, 0.5);
        return color;
    }
}


vec3 traceRay(vec3 ro, vec3 rd){
    vec3 light = normalize(vec3(cos(u_time), 0.75, sin(u_time)));
    
    vec3 color = castRay(ro, rd); 
    if (color == vec3(-1.0)) return vec3(0.5, 0.8, 0.9);
    
    //if (castRay(ro, light) != vec3(-1.0)) color *= 0.5;
    return color;
}


void main() {
    vec2 coord_pix = (2 * gl_FragCoord.xy - u_WeiHei.xy) / u_WeiHei.y;

    vec3 rayOrigin = vec3(0.0, 0.0, -3.0);
    vec3 rayDirection = normalize(vec3(coord_pix.xy, 1.0));
    vec3 color = traceRay(rayOrigin, rayDirection);
    color.r = pow(color.r, 0.45);
    color.g = pow(color.g, 0.45);
    color.b = pow(color.b, 0.45); 
    gl_FragColor = vec4(color, 1.0);
}

а так же код запускаемой программы main.cpp:

#include <iostream>
#include <SFML/Graphics.hpp>

namespace CONSTS {
    unsigned int WEIGHT_WINDOW = 1280;
    unsigned int HEIGHT_WINDOW = 720;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(CONSTS::WEIGHT_WINDOW, 720), "RayTracing");
    window.setFramerateLimit(60);

    sf::VertexArray quad(sf::Quads, 4);
    quad[0].position = sf::Vector2f(0, 0);
    quad[1].position = sf::Vector2f(CONSTS::WEIGHT_WINDOW, 0);
    quad[2].position = sf::Vector2f(CONSTS::WEIGHT_WINDOW, CONSTS::HEIGHT_WINDOW);
    quad[3].position = sf::Vector2f(0, CONSTS::HEIGHT_WINDOW);
    
    sf::RenderTexture firstTexture;
    firstTexture.create(CONSTS::WEIGHT_WINDOW, CONSTS::HEIGHT_WINDOW);
    sf::Sprite firstTextureSprite = sf::Sprite(firstTexture.getTexture());

    sf::Shader shader_fragment;
    shader_fragment.loadFromFile("shader.frag", sf::Shader::Fragment);

    sf::Clock clock;

    shader_fragment.setUniform("u_WeiHei", sf::Vector2f((float)CONSTS::WEIGHT_WINDOW, (float)CONSTS::HEIGHT_WINDOW));

    while (window.isOpen()) {

        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) window.close();
        }
        shader_fragment.setUniform("u_time", clock.getElapsedTime().asSeconds());
        //firstTexture.draw(firstTextureSprite, &shader_fragment);
        window.draw(quad, &shader_fragment);
        window.display();
    }
}

Ответы (0 шт):