| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- //
- // Copyright (c) 2017, THUNDERBEAST GAMES LLC All rights reserved
- // Copyright (c) 2015 Dmitry Sovetov
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include <Atomic/Core/StringUtils.h>
- #include "EmbreeScene.h"
- #include "SceneBaker.h"
- #include "Photons.h"
- #include "BakeMesh.h"
- namespace AtomicGlow
- {
- PhotonMap::PhotonMap(BakeMesh* bakeMesh, int width, int height) :
- bakeMesh_(bakeMesh),
- width_(width),
- height_(height)
- {
- if (width_ > 0 && height_ > 0)
- {
- photons_ = new Photon[width_ * height_];
- for (int i = 0; i < width_ * height_; i++)
- {
- photons_[i].Reset();
- }
- }
- }
- PhotonMap::~PhotonMap()
- {
- }
- void PhotonMap::SavePng(const String& filename) const
- {
- Image image(bakeMesh_->GetContext());
- image.SetSize(width_, height_, 3);
- image.Clear(Color::BLACK);
- for( int y = 0; y < height_; y++ )
- {
- for( int x = 0; x < width_; x++ )
- {
- const Photon& photon = photons_[y * width_ + x];
- int count;
- Color gathered = Color::BLACK;
- for (count = 0; count < PHOTON_TRI_MAX; count++)
- {
- if (photon.tris_[count] == PHOTON_TRI_INVALID)
- break;
- gathered += photon.gathered_[count];
- }
- if (!count)
- continue;
- gathered.r_ *= (1.0f / float(count));
- gathered.g_ *= (1.0f / float(count));
- gathered.b_ *= (1.0f / float(count));
- gathered.a_ *= (1.0f / float(count));
- image.SetPixel(x, y, gathered);
- }
- }
- image.SavePNG(filename);
- }
- void PhotonMap::Gather(int radius )
- {
- for( int y = 0; y < height_; y++ )
- {
- for( int x = 0; x < width_; x++ )
- {
- Photon& photon = photons_[y * width_ + x];
- for (int i = 0; i < PHOTON_TRI_MAX; i++)
- {
- if (photon.tris_[i] == PHOTON_TRI_INVALID)
- break;
- photon.gathered_[i] = Gather(photon.tris_[i], x, y, radius);
- }
- }
- }
- /*
- if (true)
- {
- String filename = ToString("/Users/jenge/Temp/PhotonMap%i.png", bakeMesh_->GetGeomID());
- SavePng(filename);
- }
- */
- }
- Color PhotonMap::Gather( int triIndex, int x, int y, int radius ) const
- {
- Color color = Color::BLACK;
- int photons = 0;
- for( int j = y - radius; j <= y + radius; j++ )
- {
- for( int i = x - radius; i <= x + radius; i++ )
- {
- if( i < 0 || j < 0 || i >= width_ || j >= height_ )
- {
- continue;
- }
- const Photon& photon = photons_[j * width_ + i];
- int idx = photon.MapTriIndex(triIndex);
- if (idx == PHOTON_TRI_INVALID)
- {
- continue;
- }
- float distance = sqrtf( static_cast<float>( (x - i) * (x - i) + (y - j) * (y - j) ) );
- if( distance > radius ) {
- continue;
- }
- if (photon.photons_[idx])
- {
- color += photon.color_[idx];
- photons += photon.photons_[idx];
- }
- }
- }
- if( photons == 0 )
- {
- return Color::BLACK;
- }
- float divisor = 1.0f / static_cast<float>( photons );
- color.r_ *= divisor;
- color.g_ *= divisor;
- color.b_ *= divisor;
- color.a_ *= divisor;
- return color;
- }
- Photons::Photons( SceneBaker* sceneBaker, int passCount, int maxDepth, float energyThreshold, float maxDistance )
- : sceneBaker_( SharedPtr<SceneBaker>(sceneBaker) ),
- passCount_( passCount ),
- maxDepth_( maxDepth ),
- energyThreshold_( energyThreshold ),
- maxDistance_( maxDistance ),
- photonCount_( 0 )
- {
- }
- int Photons::Emit( const Vector<SharedPtr<BakeLight>>& bakeLights )
- {
- // TODO: depending on performance, photon emission may need to be threaded
- for( int j = 0; j < passCount_; j++ )
- {
- Vector<SharedPtr<BakeLight>>::ConstIterator itr = bakeLights.Begin();
- while (itr != bakeLights.End())
- {
- BakeLight* bakeLight = *itr;
- if( !bakeLight->GetPhotonEmitter() )
- {
- itr++;
- continue;
- }
- EmitPhotons( bakeLight );
- itr++;
- }
- }
- return photonCount_;
- }
- void Photons::EmitPhotons( const BakeLight* light )
- {
- PhotonEmitter* emitter = light->GetPhotonEmitter();
- Vector3 direction;
- Vector3 position;
- for( int i = 0, n = emitter->GetPhotonCount(); i < n; i++ )
- {
- // Emit photon
- emitter->Emit( sceneBaker_, position, direction );
- // Calculate light cutoff
- float cut = 1.0f;
- if( const LightCutoff* cutoff = light->GetCutoffModel() )
- {
- cut = cutoff->GetCutoffForDirection( direction );
- }
- if( cut <= 0.0f )
- {
- continue;
- }
- Trace( light->GetAttenuationModel(), position, direction, light->GetColor() * light->GetIntensity() * cut, 0 );
- }
- }
- void Photons::Trace(const LightAttenuation* attenuation, const Vector3& position, const Vector3& direction, const Color& color, int depth , unsigned lastGeomID, unsigned lastPrimID)
- {
- // Maximum depth or energy threshold exceeded
- if( depth > maxDepth_ || color.Luma() < energyThreshold_ )
- {
- return;
- }
- // clean this mess up
- RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
- float maxDist = attenuation ? maxDistance_ : LIGHT_LARGE_DISTANCE;
- lightRay_.SetupRay(position, direction, .001f, maxDist);
- RTCRay& ray = lightRay_.rtcRay_;
- rtcIntersect(scene, ray);
- // The photon didn't hit anything
- if (ray.geomID == RTC_INVALID_GEOMETRY_ID)
- {
- return;
- }
- // self hit
- if (ray.geomID == lastGeomID && ray.primID == lastPrimID)
- {
- return;
- }
- Vector3 hitPosition = position + (direction * ray.tfar);
- Vector3 hitNormal(ray.Ng[0], ray.Ng[1], ray.Ng[2]);
- hitNormal.Normalize();
- // energy attenuation after a photon has passed the traced segment
- float att = 1.0f;
- if( attenuation )
- {
- att = attenuation->Calculate( (position - hitPosition).Length() );
- }
- // energy after reflection
- float influence = LightInfluence::GetLambert( direction, hitNormal ) * att;
- if (influence <= 0.0f)
- {
- return;
- }
- BakeMesh* bakeMesh = sceneBaker_->GetEmbreeScene()->GetBakeMesh(ray.geomID);
- Color hitColor;
- Vector3 bary(ray.u, ray.v, 1.0f-ray.u-ray.v);
- // TODO: alpha mask support?
- if (!bakeMesh || !bakeMesh->GetUV0Color(ray.primID, bary, hitColor))
- {
- return;
- }
- // final photon color
- hitColor.r_ *= (color.r_ * influence);
- hitColor.g_ *= (color.g_ * influence);
- hitColor.b_ *= (color.b_ * influence);
- Vector2 st;
- bakeMesh->GetST(ray.primID, 1, bary, st);
- // store photon energy
- Store( bakeMesh->GetPhotonMap(), (int) ray.primID, hitColor, st );
- // keep tracing
- Vector3 dir;
- Vector3::GetRandomHemisphereDirection(dir, hitNormal);
- Trace( attenuation, hitPosition, dir, hitColor, depth + 1, ray.geomID, ray.primID );
- }
- void Photons::Store(PhotonMap* photonmap, int triIdx, const Color& color, const Vector2& uv )
- {
- if( !photonmap ) {
- return;
- }
- if (!photonmap->AddPhoton(triIdx, uv, color))
- {
- return;
- }
- photonCount_++;
- }
- }
|