как отрисовать тень, полученную путём перекрывания объектами источника света? 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();
}
}