749 lines
21 KiB
C++
749 lines
21 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "raytracer/raytracer_core.h"
|
|
#include "raytracer/raytracer_job.h"
|
|
#include "scene3d/mobject.h"
|
|
#include "scene3d/mlight.h"
|
|
#include "scene3d/mcamera.h"
|
|
#include "scene3d/scene.h"
|
|
#include "rand/rand.h"
|
|
#include "platform.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::Core;
|
|
using namespace GS::Raytrace;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
float Raytracer::Fresnel(const Vector4 &v, const Vector4 &np, float eta)
|
|
{
|
|
float const r0 = Math::Pow(1.0f - eta, 2.0f) / Math::Pow(1.0f + eta, 2.0f);
|
|
// Light vector and normal are assumed to be normalized.
|
|
return Types::Clamp <float> (r0 + (1.0f - r0) * Math::Pow(1 - Types::Abs(v.Dot(np)), 5.0f), 0.0f, 1.0f);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
float Raytracer::ShadowFeel(const Vector4 &s, const Vector4 &d, float l, int r)
|
|
{
|
|
float k_shadow = 1;
|
|
|
|
if (configuration.trace_transparency)
|
|
{
|
|
if (!r)
|
|
return k_shadow;
|
|
|
|
// Get closest hit.
|
|
Trace trace;
|
|
scene_shadow_tree.RaytraceScene(trace, s, d, l);
|
|
statistics.ray_count++;
|
|
statistics.tri_test += trace.tri_test;
|
|
|
|
if (trace.has_i && (trace.i_t > 0))
|
|
{
|
|
// Check opacity.
|
|
float opacity = SampleMaterialOpacity(trace);
|
|
|
|
// Early exit on fully opaque hit.
|
|
if (opacity == 1)
|
|
return 0;
|
|
k_shadow = 1 - opacity;
|
|
|
|
// Recurse.
|
|
Vector4 offset_pi = trace.s + trace.d * (trace.i_t + Units::Mm(1));
|
|
k_shadow *= ShadowFeel(offset_pi, trace.d, l - Vector4::Dist(trace.s, offset_pi), --r);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Any hit within range will do.
|
|
Trace trace(false);
|
|
scene_shadow_tree.RaytraceScene(trace, s, d, l);
|
|
|
|
if (trace.has_i && (trace.i_t > 0))
|
|
return 0; // Occluded.
|
|
}
|
|
|
|
return k_shadow;
|
|
}
|
|
void Raytracer::ComputeRadiance(Trace &trace, Color &o, Bounce &bounce)
|
|
{
|
|
bool use_fixed_function = trace.m->shader.IsEmpty();
|
|
bool blend_additive = trace.m->blendop == Material::Blend_Add;
|
|
|
|
// Evaluate material alpha.
|
|
float alpha;
|
|
if (use_fixed_function)
|
|
alpha = SampleMaterialOpacity(trace);
|
|
else alpha = SampleMaterialSink(trace, ShaderTree::SinkOpacity).x;
|
|
alpha *= trace.o->opacity;
|
|
|
|
// Compute direct lighting, if the material does not care about the alpha test, or if the material cares about it and its alpha is up to the threshold.
|
|
if (!(trace.m->renderword & Material::Render_AlphaTest) || alpha > trace.m->athreshold)
|
|
{
|
|
// Evaluate material glossiness.
|
|
float glossiness;
|
|
if (use_fixed_function)
|
|
glossiness = trace.m->glossiness;
|
|
else glossiness = SampleMaterialSink(trace, ShaderTree::SinkGlossiness).x;
|
|
|
|
// Evaluate light contribution.
|
|
Color l_diff(0, 0, 0), l_spec(0, 0, 0);
|
|
Vector4 offset_pi = trace.pi + trace.n * Units::Mm(1.f);
|
|
|
|
for (uint n = 0; n < lgt.GetCount(); ++n)
|
|
if (S3D::MLight *l = lgt[n].l)
|
|
{
|
|
Core::Light *light = (Core::Light *)l;
|
|
float k_shadow = 1.f;
|
|
|
|
if ((light->shadow != Core::Light::Shadow_None) && configuration.trace_shadow)
|
|
{
|
|
Vector4 d;
|
|
|
|
switch (light->model)
|
|
{
|
|
default:
|
|
case Core::Light::Model_Point:
|
|
d = light->GetMatrix().GetRow(3) - offset_pi;
|
|
break;
|
|
|
|
case Core::Light::Model_Linear:
|
|
d = light->GetMatrix().GetRow(2).Reversed() * light->clip_distance;
|
|
break;
|
|
}
|
|
|
|
if (d.Dot(trace.n) > 0)
|
|
{
|
|
float l = d.Len();
|
|
d /= l;
|
|
|
|
k_shadow = ShadowFeel(offset_pi, d, l, configuration.trace_shadow_transparency_max_recursion);
|
|
if (!k_shadow)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Compute contribution.
|
|
float k_d, k_s;
|
|
if (light->SampleEnergy(trace.pi, trace.n, &k_d, &k_s, &trace.d, glossiness))
|
|
{
|
|
l_diff += light->diffuse_color * light->diffuse_intensity * k_d * k_shadow;
|
|
l_spec += light->specular_color * light->specular_intensity * k_s * k_shadow;
|
|
}
|
|
}
|
|
|
|
// Compute indirect lighting.
|
|
Color l_indirect(0, 0, 0), ambient(0, 0, 0);
|
|
|
|
if (configuration.trace_gi && bounce.indirect)
|
|
{
|
|
bounce.indirect--;
|
|
|
|
Spread &mc = monte_carlo[Random::Rand(32)];
|
|
Matrix3 nm(Matrix3::FromOrthonormalBasis(trace.n));
|
|
Color l;
|
|
|
|
// divide by the number of bounce, to avoid full bounce each time.
|
|
int count_spread = mc.spread.GetCount();
|
|
if (configuration.indirect_gi_bounce - bounce.indirect != 0)
|
|
count_spread /= configuration.indirect_gi_bounce - bounce.indirect + 1;
|
|
count_spread = Types::Max(count_spread, 1);
|
|
|
|
for (int n = 0; n < count_spread; ++n)
|
|
{
|
|
Bounce ibounce;
|
|
|
|
ibounce.indirect = bounce.indirect;
|
|
ibounce.reflection = 0;
|
|
ibounce.refraction = 0;
|
|
|
|
Raytrace(RayGrid(offset_pi, mc.spread[n] * nm), l, ibounce);
|
|
l_indirect += l;
|
|
}
|
|
l_indirect /= (float)count_spread;
|
|
}
|
|
else
|
|
ambient = (configuration.gi_use_ambient || !configuration.trace_gi) ? scene->ambient_color * scene->ambient_intensity : Vector4(0.f, 0.f, 0.f);
|
|
|
|
// Compute ambient occlusion.
|
|
float ambient_occlusion = 1.0f;
|
|
|
|
if ((alpha >= 1.0f) && configuration.ao_activate)
|
|
{
|
|
Spread &mc = monte_carlo[Random::Rand(32)];
|
|
Matrix3 nm(Matrix3::FromOrthonormalBasis(trace.n));
|
|
|
|
float countouch = 0.0f;
|
|
float lengthmax = configuration.ao_length;
|
|
float divlengthmaxsq = 1.0f / lengthmax;
|
|
|
|
for (uint n = 0; n < mc.spread.GetCount(); ++n)
|
|
{
|
|
// Create the direction vector from the normal of the point with a bit of random.
|
|
Trace traceOcclusion;
|
|
Vector4 start(trace.pi + mc.spread[n] * nm * Units::Mm(1.f));
|
|
/*
|
|
nVector DirVect(mc.spread[n] * nm);
|
|
scene_tree.RaytraceScene(traceOcclusion, start, DirVect, lengthmax);
|
|
|
|
// check the raytrace pass if the alpha of the map and continue to raytrace then
|
|
float alphaOcclusion = 0.0f;
|
|
float current_length = 0.0f;
|
|
|
|
while(alphaOcclusion < 1.0f && current_length < lengthmax &&
|
|
traceOcclusion.has_i && (traceOcclusion.i_t > 0.0f))
|
|
{
|
|
current_length += traceOcclusion.i_t;
|
|
|
|
// Compute intersection point and fetch material.
|
|
traceOcclusion.pi = traceOcclusion.s + traceOcclusion.d * traceOcclusion.i_t;
|
|
traceOcclusion.m = traceOcclusion.g->material_table[traceOcclusion.g->pol[traceOcclusion.ip].material];
|
|
|
|
bool use_fixed_functionOcclusion = trace.m->shader_tree == NULL ? true : false;
|
|
|
|
// Evaluate material alpha.
|
|
float TempAlphaOcclusion = 0.0f;
|
|
|
|
if (use_fixed_functionOcclusion)
|
|
TempAlphaOcclusion = SampleMaterialOpacity(traceOcclusion);
|
|
else TempAlphaOcclusion = SampleMaterialSink(traceOcclusion, nShaderTree::SinkOpacity).x;
|
|
alphaOcclusion += TempAlphaOcclusion*traceOcclusion.o->opacity;
|
|
|
|
if(alphaOcclusion < 1.0f && current_length < lengthmax)
|
|
scene_tree.RaytraceScene(traceOcclusion, traceOcclusion.pi + DirVect* Mm(1), DirVect, lengthmax - current_length);
|
|
}
|
|
|
|
if(alphaOcclusion > 1.0f)
|
|
alphaOcclusion = 1.0f;
|
|
|
|
if (alphaOcclusion > 0)
|
|
countouch += (1.0f - Types::Clamp(current_length * divlengthmaxsq, 0.0f, 1.0f))* alphaOcclusion;
|
|
*/
|
|
scene_tree.RaytraceScene(traceOcclusion, start, mc.spread[n] * nm, lengthmax);
|
|
|
|
if (traceOcclusion.has_i && (traceOcclusion.i_t > 0.0f))
|
|
countouch += 1.0f - Types::Clamp(traceOcclusion.i_t * divlengthmaxsq, 0.0f, 1.0f);
|
|
}
|
|
|
|
if (countouch > 0.0f)
|
|
ambient_occlusion = 1.0f - countouch / mc.spread.GetCount();
|
|
ambient_occlusion = Types::Clamp(ambient_occlusion);
|
|
}
|
|
|
|
// Sample attributes.
|
|
Color diffuse, specular, self;
|
|
|
|
if (use_fixed_function)
|
|
{
|
|
// Gather attributes.
|
|
diffuse = SampleMaterialAttribute(trace, Channel_Diffuse);
|
|
specular = SampleMaterialAttribute(trace, Channel_Specular);
|
|
self = SampleMaterialAttribute(trace, Channel_SelfIllum);
|
|
|
|
// Vertex color.
|
|
if (trace.m->GetChannelStage(Channel_Light))
|
|
{
|
|
Color color = SampleMaterialAttribute(trace, Channel_Light);
|
|
diffuse *= color;
|
|
specular *= color;
|
|
}
|
|
else if (trace.m->renderword & Material::Render_VertexColor)
|
|
{
|
|
Color color = SampleGeometryAttribute(trace, GeometryVertexColor);
|
|
diffuse *= color;
|
|
specular *= color;
|
|
}
|
|
|
|
// Environment mapping.
|
|
if (trace.m->GetChannelStage(Channel_Reflection))
|
|
{
|
|
Color color = SampleMaterialAttribute(trace, Channel_Reflection);
|
|
switch (trace.m->GetChannelStage(Channel_Reflection)->op)
|
|
{
|
|
case Material::Operator_Multiply:
|
|
diffuse *= color;
|
|
break;
|
|
|
|
case Material::Operator_Default:
|
|
case Material::Operator_Add:
|
|
diffuse += color;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
diffuse = SampleMaterialSink(trace, ShaderTree::SinkDiffuse);
|
|
specular = SampleMaterialSink(trace, ShaderTree::SinkSpecular);
|
|
self = SampleMaterialSink(trace, ShaderTree::SinkConstant);
|
|
}
|
|
|
|
// Final color.
|
|
o = ((diffuse * (l_diff + l_indirect + ambient* ambient_occlusion)) + specular * l_spec + self) /** alpha*/; // Don't multiply the alpha, because there is real raytracing for the refraction after.
|
|
}
|
|
else
|
|
{
|
|
alpha = 0;
|
|
o.Set(0, 0, 0);
|
|
}
|
|
|
|
// Apply fog.
|
|
if (scene->fog_far > 0)
|
|
{
|
|
float kfog = Types::Clamp((trace.td - scene->fog_near) / (scene->fog_far - scene->fog_near));
|
|
o = o * (1.f - kfog) + scene->fog_color * kfog;
|
|
}
|
|
|
|
// Trace reflected and transmitted rays as required.
|
|
float krefl = alpha;
|
|
|
|
float eta = trace.m->irefraction;
|
|
if (trace.ir == trace.m->irefraction)
|
|
eta = 1.0f;
|
|
|
|
if (((alpha < 1) || blend_additive) && bounce.refraction)
|
|
{
|
|
float n = trace.ir / eta;
|
|
|
|
if (configuration.fresnel_activate)
|
|
krefl = Fresnel(trace.d, trace.n.FaceForward(trace.d), n);
|
|
|
|
float c1 = -trace.n.FaceForward(trace.d).Dot(trace.d);
|
|
float w = n * Types::Abs(c1);
|
|
float c2 = Math::Sqrt(1 + (w - n) * (w + n));
|
|
|
|
Vector4 rtransmit = (trace.d * n) + trace.n.FaceForward(trace.d) * (w - c2);
|
|
rtransmit = rtransmit.Normalized();
|
|
Vector4 offset_pi = trace.pi + rtransmit * Units::Mm(1.f);
|
|
|
|
if (c2 < 0)
|
|
krefl = 1.0f; // Full reflection, we are inside the matter and by an angle where it is physically impossible (as Snell-Descartes law) to have refraction.
|
|
|
|
if ((1.0f - krefl) > 0.0f)
|
|
{
|
|
bounce.refraction--;
|
|
|
|
Color b;
|
|
float save_ir = trace.ir;
|
|
trace.ir = eta;
|
|
Raytrace(RayGrid(offset_pi, rtransmit), b, bounce, &trace);
|
|
trace.ir = save_ir;
|
|
|
|
if (blend_additive)
|
|
o += b;
|
|
else o = o * krefl + b * (1 - krefl);
|
|
|
|
bounce.refraction++;
|
|
}
|
|
}
|
|
|
|
// Reflection.
|
|
float material_reflection = SampleMaterialSink(trace, ShaderTree::SinkReflection).x;
|
|
if (configuration.trace_reflection && material_reflection && bounce.reflection)
|
|
{
|
|
if (krefl > 0.0f)
|
|
{
|
|
bounce.reflection--;
|
|
|
|
Vector4 nf = trace.n.FaceForward(trace.d).Normalized();
|
|
float c1 = -nf.Dot(trace.d);
|
|
Vector4 rreflect = trace.d + (nf * 2.f * Types::Abs(c1));
|
|
|
|
Vector4 offset_pi = trace.pi + rreflect * Units::Mm(1.f) ;
|
|
|
|
Color b;
|
|
float save_ir = trace.ir;
|
|
trace.ir = eta;
|
|
Raytrace(RayGrid(offset_pi, rreflect), b, bounce, &trace);
|
|
trace.ir = save_ir;
|
|
|
|
o += b * (krefl * material_reflection);
|
|
|
|
bounce.reflection++;
|
|
}
|
|
}
|
|
|
|
// Note: Isn't doing this here getting rid of HDR informations?
|
|
o = o.Clamped(Vector4(0, 0, 0), Vector4(1, 1, 1));
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Raytracer::PrimaryRay(const RayGrid &ray, Color &o)
|
|
{
|
|
Bounce bounce;
|
|
bounce.indirect = configuration.indirect_gi_bounce;
|
|
bounce.reflection = configuration.trace_reflection_max_recursion;
|
|
bounce.refraction = configuration.trace_refraction_max_recursion;
|
|
Raytrace(ray, o, bounce);
|
|
}
|
|
void Raytracer::Raytrace(const RayGrid &ray, Color &o, Bounce &bounce, Trace *previous_trace)
|
|
{
|
|
Trace trace;
|
|
|
|
if (previous_trace)
|
|
{
|
|
trace.ir = previous_trace->ir;
|
|
trace.td = previous_trace->td;
|
|
}
|
|
|
|
scene_tree.RaytraceScene(trace, ray.p[0], ray.d[0]);
|
|
statistics.ray_count++;
|
|
statistics.tri_test += trace.tri_test;
|
|
|
|
// Shade result.
|
|
if (trace.has_i)
|
|
{
|
|
// Compute intersection point.
|
|
trace.pi = trace.s + trace.d * trace.i_t;
|
|
|
|
// Compute intersection normal.
|
|
Vector4 normal_sink = SampleMaterialSink(trace, ShaderTree::SinkNormal);
|
|
trace.o->GetMatrix().ApplyRotation(&trace.n, &normal_sink);
|
|
trace.n.Normalize();
|
|
if (trace.backface)
|
|
trace.n = trace.n.Reversed();
|
|
|
|
// Integrate the newly traveled distance.
|
|
trace.td += trace.i_t;
|
|
|
|
// Gather radiance.
|
|
ComputeRadiance(trace, o, bounce);
|
|
}
|
|
else
|
|
o = scene->background_color;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Raytracer::Render(Picture &output, uint w, uint h)
|
|
{
|
|
if (!w || !h)
|
|
return false;
|
|
|
|
uint logical_h = h;
|
|
viewport.Set((float)w, (float)h);
|
|
|
|
if (configuration.interlaced)
|
|
{
|
|
if (h & 1)
|
|
__ERR__(__LOG_E__ << "Interlaced frame height must be a multiple of 2.", false)
|
|
if (configuration.interlaced_trace_half_frame)
|
|
h /= 2;
|
|
}
|
|
|
|
Camera *camera = scene->current_camera;
|
|
if (!camera)
|
|
return false;
|
|
|
|
// Create destination picture.
|
|
output.AllocAs(w, h);
|
|
|
|
// Allocate output hdr buffer.
|
|
Array <Color> hdr(w * h);
|
|
if (!hdr)
|
|
__ERR__(__LOG_E__<< "Failed to allocate floating point frame buffer.\n", false)
|
|
|
|
// Reset statistics.
|
|
render_clock = scene->GetClock()->Getf();
|
|
statistics.Reset();
|
|
|
|
// Progress structure.
|
|
Progress progress;
|
|
|
|
progress.start_clock = Platform::Get().GetClock();
|
|
progress.instance = this;
|
|
progress.progress = 0;
|
|
progress.buffer = hdr;
|
|
progress.w = 0;
|
|
progress.h = 0;
|
|
progress.done = false;
|
|
|
|
// Create virtual screen.
|
|
Benchmark bench(true);
|
|
scene_tree.ResetStats();
|
|
|
|
Vector4 screen[4], wscreen[4];
|
|
|
|
float hw, hh, ar = ((camera->aspect_ratio == -1.f) ? 1.f : camera->aspect_ratio);
|
|
|
|
if (camera->aspect_ratio_ref_yaxis)
|
|
{
|
|
hw = ((float)w / (float)logical_h) / ar;
|
|
hh = 1;
|
|
}
|
|
else
|
|
{
|
|
hw = 1;
|
|
hh = ((float)logical_h / ar) / (float)w;
|
|
}
|
|
|
|
screen[0].Set(-hw, hh, camera->zoom_factor);
|
|
screen[1].Set(hw, hh, camera->zoom_factor);
|
|
screen[2].Set(hw, -hh, camera->zoom_factor);
|
|
screen[3].Set(-hw, -hh, camera->zoom_factor);
|
|
|
|
camera->GetMatrix().Apply(wscreen, screen, 4);
|
|
|
|
// Interpolate across world screen and trace.
|
|
Vector4 dt_l, pt_l, dt_r, pt_r;
|
|
|
|
dt_l = (wscreen[3] - wscreen[0]) / (float)logical_h;
|
|
pt_l = wscreen[0];
|
|
dt_r = (wscreen[2] - wscreen[1]) / (float)logical_h;
|
|
pt_r = wscreen[1];
|
|
|
|
// Interlace.
|
|
if (configuration.interlaced && configuration.interlaced_trace_half_frame)
|
|
{
|
|
if (!interlace_even)
|
|
{
|
|
pt_l += dt_l;
|
|
pt_r += dt_r;
|
|
}
|
|
dt_l *= 2.f;
|
|
dt_r *= 2.f;
|
|
}
|
|
|
|
const Vector4 &s = camera->GetMatrix().GetRow(3);
|
|
|
|
// Rendering.
|
|
abort = false;
|
|
progress.description = "Rendering (1/2)";
|
|
|
|
#define __JobTileSize 32
|
|
|
|
// Split rendering in tiles.
|
|
AutoList <ASync::Job *> job_list;
|
|
ASync::JobGroup group;
|
|
|
|
for (uint y = 0; y < h; y += __JobTileSize)
|
|
for (uint x = 0; x < w; x += __JobTileSize)
|
|
{
|
|
RaytraceJob *job = new RaytraceJob;
|
|
job_list.Add(job);
|
|
|
|
job->core = this;
|
|
|
|
job->start_height = y;
|
|
job->end_height = y + __JobTileSize < h ? y + __JobTileSize : h;
|
|
job->start_width = x;
|
|
job->end_width = x + __JobTileSize < w ? x + __JobTileSize : w;
|
|
|
|
job->s = s;
|
|
job->dt_l = dt_l; job->pt_l = pt_l;
|
|
job->dt_r = dt_r; job->pt_r = pt_r;
|
|
|
|
job->hdr = hdr;
|
|
job->pitch = w;
|
|
|
|
Platform::Get().job_manager->EnqueueJob(job, &group);
|
|
}
|
|
|
|
while (!Platform::Get().job_manager->JoinGroup(&group, false))
|
|
if (hook)
|
|
{
|
|
// progress.progress = 1.f - (float)group.GetJobCount() / job_list.GetCount();
|
|
hook->RaytracerProgress(progress);
|
|
}
|
|
|
|
job_list.Clear();
|
|
/*
|
|
// Split anti-aliasing in tiles.
|
|
progress.description = "Anti-aliasing (2/2)";
|
|
|
|
for (uint y = 1; y < (h - 1); y += __JobTileSize)
|
|
for (uint x = 1; x < (w - 1); x += __JobTileSize)
|
|
{
|
|
nAntialiasJob *job = new nAntialiasJob;
|
|
job_list.Add(job);
|
|
|
|
job->core = this;
|
|
|
|
job->start_height = y;
|
|
job->end_height = y + __JobTileSize < (h - 1) ? y + __JobTileSize : (h - 1);
|
|
job->start_width = x;
|
|
job->end_width = x + __JobTileSize < (w - 1) ? x + __JobTileSize : (w - 1);
|
|
|
|
job->s = s;
|
|
job->dt_l = dt_l; job->pt_l = pt_l;
|
|
job->dt_r = dt_r; job->pt_r = pt_r;
|
|
|
|
job->hdr = hdr;
|
|
job->pitch = w;
|
|
|
|
Platform::Get().job_manager->EnqueueJob(job, &group);
|
|
}
|
|
|
|
// Join anti-aliasing job group.
|
|
while (!Platform::Get().job_manager->JoinGroup(&group, false))
|
|
if (hook)
|
|
{
|
|
// progress.progress = 1.f - (float)group.GetJobCount() / job_list.GetCount();
|
|
hook->RaytracerProgress(progress);
|
|
}
|
|
|
|
job_list.Clear();
|
|
*/
|
|
bench.Stop();
|
|
__LOG__ << "Raytracing done. Took " << bench.GetMs() << " ms. Ray/s = " << (scene_tree.ray_count * 1000) / bench.GetMs() << "\n";
|
|
|
|
// HDR conversion to standard 32 bit RGBA.
|
|
#pragma omp parallel
|
|
{
|
|
#pragma omp for schedule(dynamic) nowait
|
|
for (uint v = 0; v < h; ++v)
|
|
{
|
|
uint *o_rgb = ((uint *)output.GetData()) + w * v;
|
|
Color *o_hdr = hdr + w * v;
|
|
|
|
for (uint u = 0; u < w; ++u)
|
|
o_rgb[u] =
|
|
((uint)(Types::Clamp(o_hdr[u].w) * 255) << 24) +
|
|
((uint)(Types::Clamp(o_hdr[u].x) * 255) << 16) +
|
|
((uint)(Types::Clamp(o_hdr[u].y) * 255) << 8) +
|
|
((uint)(Types::Clamp(o_hdr[u].z) * 255));
|
|
}
|
|
}
|
|
// ...
|
|
|
|
// Backup current frame if interlaced and wait for the next half-frame.
|
|
if (configuration.interlaced)
|
|
{
|
|
if (interlace_half_frame.isValid())
|
|
{
|
|
// If the frame is valid compose to output.
|
|
if ((interlace_half_frame.GetWidth() != w) || (interlace_half_frame.GetHeight() != h))
|
|
__LOG_E__ << "Unexpected frame dimension change during interlaced sequence rendering.\n";
|
|
|
|
else
|
|
{
|
|
Picture half_frame(output);
|
|
|
|
if (output.AllocAs(w, logical_h))
|
|
{
|
|
// Select even and odd frames based on current parity.
|
|
Picture *even = interlace_even ? &half_frame : &interlace_half_frame,
|
|
*odd = interlace_even ? &interlace_half_frame : &half_frame;
|
|
|
|
// Compose.
|
|
uint *p_even = (uint *)even->GetData(),
|
|
*p_odd = (uint *)odd->GetData(),
|
|
*p_output = (uint *)output.GetData();
|
|
|
|
if (configuration.interlaced_trace_half_frame)
|
|
for (uint v = 0; v < h; ++v)
|
|
{
|
|
Memory::Copy(p_output, p_even, w * 4);
|
|
p_even += w;
|
|
p_output += w;
|
|
|
|
Memory::Copy(p_output, p_odd, w * 4);
|
|
p_odd += w;
|
|
p_output += w;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (interlace_even)
|
|
p_even += w;
|
|
else p_odd += w;
|
|
|
|
for (uint v = 0; v < h; ++v)
|
|
{
|
|
Memory::Copy(p_output, p_even, w * 4);
|
|
p_even += w * 2;
|
|
p_output += w;
|
|
|
|
Memory::Copy(p_output, p_odd, w * 4);
|
|
p_odd += w * 2;
|
|
p_output += w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Drop buffer, it has been committed to output.
|
|
interlace_half_frame.Free();
|
|
}
|
|
else
|
|
{
|
|
// Buffer the current output and drop it. No save is to be done yet.
|
|
interlace_half_frame.Clone(output);
|
|
output.Free();
|
|
}
|
|
}
|
|
|
|
// Done, switch interlace parity.
|
|
interlace_even = !interlace_even;
|
|
viewport.Set(1, 1);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Raytracer::StartInterlacedSequence()
|
|
{
|
|
interlace_even = configuration.interlace_even;
|
|
interlace_half_frame.Free();
|
|
}
|
|
void Raytracer::Abort()
|
|
{ abort = true; }
|
|
void Raytracer::SetConfiguration(const Configuration &config)
|
|
{
|
|
configuration = config;
|
|
for (int n = 0; n < 32; ++n)
|
|
monte_carlo[n].Initialize(configuration.gi_sample, configuration.gi_sample, Units::Deg(configuration.ao_angle)); // 64 evaluations per ray.
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Raytracer::SetScene(const GS::S3D::Scene *s)
|
|
{
|
|
if (!gf)
|
|
__ERR__(__LOG_E__ << "No graphic resource factory to set raytracer scene.\n", false)
|
|
|
|
Free();
|
|
|
|
// Grab scene and shadow scene.
|
|
scene = s;
|
|
if (!scene_tree.SetScene(*gf, s) || !scene_shadow_tree.SetScene(*gf, s, true))
|
|
return false;
|
|
|
|
// Grab lights, reset caches.
|
|
SharedList <S3D::MLight *> lights;
|
|
s->GetItemListByType(lights);
|
|
|
|
if (!lgt.Allocate(lights.GetCount()))
|
|
__ERR__(__LOG_E__ << "Failed to allocate raytracer light array.\n", false)
|
|
|
|
uint lgt_count = 0;
|
|
ListForeachPtr(S3D::MLight *, l, lights)
|
|
{
|
|
lgt[lgt_count].l = l->isActive() ? l : NULL;
|
|
lgt[lgt_count].g = NULL;
|
|
lgt_count++;
|
|
}
|
|
return true;
|
|
}
|
|
void Raytracer::Free()
|
|
{
|
|
scene_tree.Free();
|
|
scene_shadow_tree.Free();
|
|
|
|
lgt.Free();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
Raytracer::Raytracer(ResourceFactory *f) : gf(f)
|
|
{
|
|
SetConfiguration(configuration);
|
|
viewport.Set(1, 1);
|
|
hook = NULL;
|
|
}
|