Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
135 kB · 3527 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528// clang-format off/* This file is based on OpenSceneGraph's src/osgShadow/ViewDependentShadowMap.cpp. * Where applicable, any changes made are covered by OpenMW's GPL 3 license, not the OSGPL. * The original copyright notice is listed below. */
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2011 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details.*/
#include "mwshadowtechnique.hpp"
#include <osgShadow/ShadowedScene>#include <osg/CullFace>#include <osg/Geometry>#include <osg/io_utils>#include <osg/Depth>#include <osg/ClipControl>
#include <sstream>#include <vector>
#include "glextensions.hpp"#include "shadowsbin.hpp"#include "lightmanager.hpp"
// NOLINTBEGIN(readability-identifier-naming)
namespace {
using namespace osgShadow;using namespace SceneUtil;
#define dbl_max std::numeric_limits<double>::max()
//////////////////////////////////////////////////////////////////// fragment shader//#if 0static const char fragmentShaderSource_withBaseTexture[] = "uniform sampler2D baseTexture; \n" "uniform sampler2DShadow shadowTexture; \n" " \n" "void main(void) \n" "{ \n" " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" " vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy ); \n" " color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture, gl_TexCoord[1] ).r ); \n" " gl_FragColor = color; \n" "} \n";#elsestatic const char fragmentShaderSource_withBaseTexture[] = "uniform sampler2D baseTexture; \n" "uniform int baseTextureUnit; \n" "uniform sampler2DShadow shadowTexture0; \n" "uniform int shadowTextureUnit0; \n" " \n" "void main(void) \n" "{ \n" " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" " vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy ); \n" " color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r ); \n" " gl_FragColor = color; \n" "} \n";
static const char fragmentShaderSource_withBaseTexture_twoShadowMaps[] = "uniform sampler2D baseTexture; \n" "uniform int baseTextureUnit; \n" "uniform sampler2DShadow shadowTexture0; \n" "uniform int shadowTextureUnit0; \n" "uniform sampler2DShadow shadowTexture1; \n" "uniform int shadowTextureUnit1; \n" " \n" "void main(void) \n" "{ \n" " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" " vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy ); \n" " float shadow0 = shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r; \n" " float shadow1 = shadow2DProj( shadowTexture1, gl_TexCoord[shadowTextureUnit1] ).r; \n" " color *= mix( colorAmbientEmissive, gl_Color, shadow0*shadow1 ); \n" " gl_FragColor = color; \n" "} \n";#endif
std::string debugVertexShaderSource = "void main(void){gl_Position = gl_Vertex; gl_TexCoord[0]=gl_MultiTexCoord0;}";std::string debugFragmentShaderSource = "uniform sampler2D texture; \n" " \n" "void main(void) \n" "{ \n"#if 1 " float f = texture2D(texture, gl_TexCoord[0].xy).r; \n" " \n" " f = 256.0 * f; \n" " float fC = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fS = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fH = floor( f ) / 256.0; \n" " \n" " fS *= 0.5; \n" " fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); \n" " \n" " vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), \n" " abs( fC - 0.333333 ), \n" " abs( fC - 0.666667 ) ); \n" " \n" " rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); \n" " \n" " float fMax = max( max( rgb.r, rgb.g ), rgb.b ); \n" " fMax = 1.0 / fMax; \n" " \n" " vec3 color = fMax * rgb; \n" " \n" " gl_FragColor = vec4( fS + fH * color, 1 ); \n"#else " gl_FragColor = texture2D(texture, gl_TexCoord[0].xy); \n"#endif "} \n";
std::string debugFrustumVertexShaderSource = "varying float depth; uniform mat4 transform; void main(void){gl_Position = transform * gl_Vertex; depth = gl_Position.z / gl_Position.w;}";std::string debugFrustumFragmentShaderSource = "varying float depth; \n" " \n" "void main(void) \n" "{ \n"#if 1 " float f = depth; \n" " \n" " f = 256.0 * f; \n" " float fC = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fS = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fH = floor( f ) / 256.0; \n" " \n" " fS *= 0.5; \n" " fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); \n" " \n" " vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), \n" " abs( fC - 0.333333 ), \n" " abs( fC - 0.666667 ) ); \n" " \n" " rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); \n" " \n" " float fMax = max( max( rgb.r, rgb.g ), rgb.b ); \n" " fMax = 1.0 / fMax; \n" " \n" " vec3 color = fMax * rgb; \n" " \n" " gl_FragColor = vec4( fS + fH * color, 1 ); \n"#else " gl_FragColor = vec4(0.0, 0.0, 1.0, 0.0); \n"#endif "} \n";
template<class T>class RenderLeafTraverser : public T{public:
RenderLeafTraverser() { }
void traverse(const osgUtil::RenderStage* rs) { traverse(static_cast<const osgUtil::RenderBin*>(rs)); }
void traverse(const osgUtil::RenderBin* renderBin) { const osgUtil::RenderBin::RenderBinList& rbl = renderBin->getRenderBinList(); for(osgUtil::RenderBin::RenderBinList::const_iterator itr = rbl.begin(); itr != rbl.end(); ++itr) { traverse(itr->second.get()); }
const osgUtil::RenderBin::RenderLeafList& rll = renderBin->getRenderLeafList(); for(osgUtil::RenderBin::RenderLeafList::const_iterator itr = rll.begin(); itr != rll.end(); ++itr) { handle(*itr); }
const osgUtil::RenderBin::StateGraphList& rgl = renderBin->getStateGraphList(); for(osgUtil::RenderBin::StateGraphList::const_iterator itr = rgl.begin(); itr != rgl.end(); ++itr) { traverse(*itr); }
}
void traverse(const osgUtil::StateGraph* stateGraph) { const osgUtil::StateGraph::ChildList& cl = stateGraph->_children; for(osgUtil::StateGraph::ChildList::const_iterator itr = cl.begin(); itr != cl.end(); ++itr) { traverse(itr->second.get()); }
const osgUtil::StateGraph::LeafList& ll = stateGraph->_leaves; for(osgUtil::StateGraph::LeafList::const_iterator itr = ll.begin(); itr != ll.end(); ++itr) { handle(itr->get()); } }
inline void handle(const osgUtil::RenderLeaf* renderLeaf) { this->operator()(renderLeaf); }};
/////////////////////////////////////////////////////////////////////////////////////////////////// VDSMCameraCullCallback//class VDSMCameraCullCallback : public osg::NodeCallback{ public:
VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope);
void operator()(osg::Node*, osg::NodeVisitor* nv) override;
osg::RefMatrix* getProjectionMatrix() { return _projectionMatrix.get(); } osgUtil::RenderStage* getRenderStage() { return _renderStage.get(); }
protected:
MWShadowTechnique* _vdsm; osg::ref_ptr<osg::RefMatrix> _projectionMatrix; osg::ref_ptr<osgUtil::RenderStage> _renderStage; osg::Polytope _polytope;};
VDSMCameraCullCallback::VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope): _vdsm(vdsm), _polytope(polytope){}
void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv){ osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv); osg::Camera* camera = node->asCamera(); OSG_INFO<<"VDSMCameraCullCallback::operator()(osg::Node* "<<camera<<", osg::NodeVisitor* "<<cv<<")"<<std::endl;
#if 1 if (!_polytope.empty()) { OSG_INFO<<"Pushing custom Polytope"<<std::endl;
osg::CullingSet& cs = cv->getProjectionCullingStack().back();
cs.setFrustum(_polytope); if (camera && camera->getViewport()) { cs.setCullingMask(cs.getCullingMask() | osg::CullSettings::SMALL_FEATURE_CULLING); cs.setSmallFeatureCullingPixelSize(camera->getSmallFeatureCullingPixelSize()); cs.setPixelSizeVector(osg::CullingSet::computePixelSizeVector( *camera->getViewport(), camera->getProjectionMatrix(), camera->getViewMatrix())); }
cv->pushCullingSet(); }#endif // bin has to go inside camera cull or the rendertexture stage will override it cv->pushStateSet(_vdsm->getOrCreateShadowsBinStateSet()); if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } cv->popStateSet();#if 1 if (!_polytope.empty()) { OSG_INFO<<"Popping custom Polytope"<<std::endl; cv->popCullingSet(); }#endif
_renderStage = cv->getCurrentRenderBin()->getStage();
OSG_INFO<<"VDSM second : _renderStage = "<<_renderStage<<std::endl;
if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { // make sure that the near plane is computed correctly. cv->computeNearPlane();
osg::Matrixd projection = *(cv->getProjectionMatrix());
OSG_INFO<<"RTT Projection matrix "<<projection<<std::endl;
osg::Matrix::value_type left, right, bottom, top, zNear, zFar; osg::Matrix::value_type epsilon = 1e-6; if (fabs(projection(0,3))<epsilon && fabs(projection(1,3))<epsilon && fabs(projection(2,3))<epsilon ) { projection.getOrtho(left, right, bottom, top, zNear, zFar);
OSG_INFO<<"Ortho zNear="<<zNear<<", zFar="<<zFar<<std::endl; } else { projection.getFrustum(left, right, bottom, top, zNear, zFar);
OSG_INFO<<"Frustum zNear="<<zNear<<", zFar="<<zFar<<std::endl; }
OSG_INFO<<"Calculated zNear = "<<cv->getCalculatedNearPlane()<<", zFar = "<<cv->getCalculatedFarPlane()<<std::endl;
zNear = osg::maximum(zNear, cv->getCalculatedNearPlane()); zFar = osg::minimum(zFar, cv->getCalculatedFarPlane());
cv->setCalculatedNearPlane(zNear); cv->setCalculatedFarPlane(zFar);
cv->clampProjectionMatrix(projection, zNear, zFar);
//OSG_INFO<<"RTT zNear = "<<zNear<<", zFar = "<<zFar<<std::endl; OSG_INFO<<"RTT Projection matrix after clamping "<<projection<<std::endl;
camera->setProjectionMatrix(projection); }
_projectionMatrix = cv->getProjectionMatrix();}
} // namespace
MWShadowTechnique::ComputeLightSpaceBounds::ComputeLightSpaceBounds() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN){ setCullingMode(osg::CullSettings::VIEW_FRUSTUM_CULLING);}
void MWShadowTechnique::ComputeLightSpaceBounds::reset(){ osg::CullStack::reset(); _bb = osg::BoundingBox();}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Node& node){ if (isCulled(node)) return;
// push the culling mode. pushCurrentMask();
traverse(node);
// pop the culling mode. popCurrentMask();}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Group& node){ apply(static_cast<osg::Node&>(node));}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Drawable& drawable){ if (isCulled(drawable)) return;
// push the culling mode. pushCurrentMask();
updateBound(drawable.getBoundingBox());
// pop the culling mode. popCurrentMask();}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Geometry& drawable){ apply(static_cast<osg::Drawable&>(drawable));}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Billboard&){ OSG_INFO << "Warning Billboards not yet supported" << std::endl; return;}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Projection&){ // projection nodes won't affect a shadow map so their subgraphs should be ignored return;}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Transform& transform){ if (isCulled(transform)) return;
// push the culling mode. pushCurrentMask();
// absolute transforms won't affect a shadow map so their subgraphs should be ignored. if (transform.getReferenceFrame() == osg::Transform::RELATIVE_RF) { osg::RefMatrix* matrix = createOrReuseMatrix(*getModelViewMatrix()); transform.computeLocalToWorldMatrix(*matrix, this); pushModelViewMatrix(matrix, transform.getReferenceFrame());
traverse(transform);
popModelViewMatrix(); }
// pop the culling mode. popCurrentMask();}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::MatrixTransform& transform){ apply(static_cast<osg::Transform&>(transform));}
void MWShadowTechnique::ComputeLightSpaceBounds::apply(osg::Camera&){ // camera nodes won't affect a shadow map so their subgraphs should be ignored return;}
void MWShadowTechnique::ComputeLightSpaceBounds::updateBound(const osg::BoundingBox& bb){ if (!bb.valid()) return;
const osg::Matrix& matrix = *getModelViewMatrix() * *getProjectionMatrix();
update(bb.corner(0) * matrix); update(bb.corner(1) * matrix); update(bb.corner(2) * matrix); update(bb.corner(3) * matrix); update(bb.corner(4) * matrix); update(bb.corner(5) * matrix); update(bb.corner(6) * matrix); update(bb.corner(7) * matrix);}
void MWShadowTechnique::ComputeLightSpaceBounds::update(const osg::Vec3& v){ if (v.z()<-1.0f) { //OSG_NOTICE<<"discarding("<<v<<")"<<std::endl; return; } float x = v.x(); if (x<-1.0f) x = -1.0f; if (x>1.0f) x = 1.0f; float y = v.y(); if (y<-1.0f) y = -1.0f; if (y>1.0f) y = 1.0f; _bb.expandBy(osg::Vec3(x, y, v.z()));}
/////////////////////////////////////////////////////////////////////////////////////////////////// LightData//MWShadowTechnique::LightData::LightData(MWShadowTechnique::ViewDependentData* vdd): _viewDependentData(vdd), directionalLight(false){}
void MWShadowTechnique::LightData::setLightData(osg::RefMatrix* lm, const Light* l, const osg::Matrixd& modelViewMatrix){ lightMatrix = lm; light = l;
lightPos = light->getPosition(); directionalLight = (light->getPosition().w()== 0.0); if (directionalLight) { lightPos3.set(0.0, 0.0, 0.0); // directional light has no destinct position lightDir.set(-lightPos.x(), -lightPos.y(), -lightPos.z()); lightDir.normalize(); OSG_INFO<<" Directional light, lightPos="<<lightPos<<", lightDir="<<lightDir<<std::endl; if (lightMatrix.valid() && lightMatrix->operator==(osg::Matrixf(modelViewMatrix))) { OSG_INFO<<" Light matrix "<<*lightMatrix<<std::endl; osg::Matrix lightToLocalMatrix(*lightMatrix * osg::Matrix::inverse(modelViewMatrix) ); lightDir = osg::Matrix::transform3x3( lightDir, lightToLocalMatrix ); lightDir.normalize(); OSG_INFO<<" new LightDir ="<<lightDir<<std::endl; } }#if 0 else { OSG_INFO<<" Positional light, lightPos="<<lightPos<<std::endl; lightDir = light->getDirection(); lightDir.normalize(); if (lightMatrix.valid()) { OSG_INFO<<" Light matrix "<<*lightMatrix<<std::endl; osg::Matrix lightToLocalMatrix(*lightMatrix * osg::Matrix::inverse(modelViewMatrix) ); lightPos = lightPos * lightToLocalMatrix; lightDir = osg::Matrix::transform3x3( lightDir, lightToLocalMatrix ); lightDir.normalize(); OSG_INFO<<" new LightPos ="<<lightPos<<std::endl; OSG_INFO<<" new LightDir ="<<lightDir<<std::endl; } lightPos3.set(lightPos.x()/lightPos.w(), lightPos.y()/lightPos.w(), lightPos.z()/lightPos.w()); }#endif}
/////////////////////////////////////////////////////////////////////////////////////////////////// ShadowData//MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData* vdd): _viewDependentData(vdd), _textureUnit(0), _sm_i(0){
const ShadowSettings* settings = vdd->getViewDependentShadowMap()->getShadowedScene()->getShadowSettings();
bool debug = settings->getDebugDraw();
// set up the texture _texture = new osg::Texture2D;
osg::Vec2s textureSize = debug ? osg::Vec2s(512,512) : settings->getTextureSize(); _texture->setTextureSize(textureSize.x(), textureSize.y());
if (debug) { _texture->setInternalFormat(GL_RGB); } else { _texture->setInternalFormat(GL_DEPTH_COMPONENT); _texture->setShadowComparison(true); _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE); }
_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
// the shader clips sampled coordinates, so no need for border _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_EDGE); _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_EDGE);
// set up the camera _camera = new osg::Camera; _camera->setName("ShadowCamera"); _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);#ifndef __APPLE__ // workaround shadow issue on macOS, https://gitlab.com/OpenMW/openmw/-/issues/6057 _camera->setImplicitBufferAttachmentMask(0, 0);#endif //_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); _camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
//_camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); //_camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
// Now we are using Depth Clamping, we want to not cull things on the wrong side of the near plane. // When the near and far planes are computed, OSG always culls anything on the wrong side of the near plane, even if it's told not to. // Even if that weren't an issue, the near plane can't go past any shadow receivers or the depth-clamped fragments which ended up on the near plane can't cast shadows on those receivers. // Unfortunately, this change will make shadows have less depth precision when there are no casters outside the view frustum. // TODO: Find a better solution. E.g. detect when there are no casters outside the view frustum, write a new cull visitor that does all the wacky things we'd need it to. _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
// Enable small feature culling on shadow cascades so sub-pixel clutter is not drawn _camera->setCullingMode(_camera->getCullingMode() | osg::CullSettings::SMALL_FEATURE_CULLING); _camera->setSmallFeatureCullingPixelSize(2.0f);
// set viewport _camera->setViewport(0,0,textureSize.x(),textureSize.y());
if (debug) { // clear just the depth buffer _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// render after the main camera _camera->setRenderOrder(osg::Camera::POST_RENDER);
// attach the texture and use it as the color buffer. //_camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get()); _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get()); } else { // clear the depth and colour bufferson each clear. _camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// set the camera to render before the main camera. _camera->setRenderOrder(osg::Camera::PRE_RENDER);
// tell the camera to use OpenGL frame buffer object where supported. _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
// attach the texture and use it as the color buffer. _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get()); //_camera->attach(osg::Camera::COLOR_BUFFER, _texture.get()); }}
void MWShadowTechnique::ShadowData::releaseGLObjects(osg::State* state) const{ OSG_INFO<<"MWShadowTechnique::ShadowData::releaseGLObjects"<<std::endl; _texture->releaseGLObjects(state); _camera->releaseGLObjects(state);}
void MWShadowTechnique::ShadowData::updateTextureSize(){ const ShadowSettings* settings = _viewDependentData->getViewDependentShadowMap()->getShadowedScene()->getShadowSettings();
const osg::Vec2s textureSize = settings->getDebugDraw() ? osg::Vec2s(512, 512) : settings->getTextureSize(); if (_texture->getTextureWidth() == textureSize.x() || _texture->getTextureHeight() == textureSize.y()) return;
_texture->setTextureSize(textureSize.x(), textureSize.y()); _texture->dirtyTextureObject(); _camera->dirtyAttachmentMap();
_camera->setViewport(0, 0, textureSize.x(), textureSize.y());}
/////////////////////////////////////////////////////////////////////////////////////////////////// Frustum//MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar): useCustomClipSpace(false), corners(8), faces(6), edges(12){ projectionMatrix = *(cv->getProjectionMatrix()); modelViewMatrix = *(cv->getModelViewMatrix());
OSG_INFO<<"Projection matrix "<<projectionMatrix<<std::endl;
if (cv->getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { osg::Matrix::value_type zNear = osg::maximum<osg::Matrix::value_type>(cv->getCalculatedNearPlane(),minZNear); osg::Matrix::value_type zFar = osg::minimum<osg::Matrix::value_type>(cv->getCalculatedFarPlane(),maxZFar); if (zFar < 0) zFar = minZNear; zNear = std::min(zNear, zFar);
cv->clampProjectionMatrix(projectionMatrix, zNear, zFar);
OSG_INFO<<"zNear = "<<zNear<<", zFar = "<<zFar<<std::endl; OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl; }}
void SceneUtil::MWShadowTechnique::Frustum::setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride){ useCustomClipSpace = true; customClipSpace = clipCornersOverride;}
void SceneUtil::MWShadowTechnique::Frustum::init(){ osg::Matrixd clipToWorld; clipToWorld.invert(modelViewMatrix * projectionMatrix);
if (useCustomClipSpace) { corners.clear(); // Add corners in the same order OSG expects them for (int i : {0, 1, 5, 4, 2, 3, 7, 6}) { corners.push_back(customClipSpace.corner(i)); } } else { corners[0].set(-1.0, -1.0, -1.0); corners[1].set(1.0, -1.0, -1.0); corners[2].set(1.0, -1.0, 1.0); corners[3].set(-1.0, -1.0, 1.0); corners[4].set(-1.0, 1.0, -1.0); corners[5].set(1.0, 1.0, -1.0); corners[6].set(1.0, 1.0, 1.0); corners[7].set(-1.0, 1.0, 1.0); }
// transform frustum corners from clipspace to world coords, and compute center for(Vertices::iterator itr = corners.begin(); itr != corners.end(); ++itr) { *itr = (*itr) * clipToWorld;
OSG_INFO<<" corner "<<*itr<<std::endl; }
// compute eye point eye = osg::Vec3d(0.0,0.0,0.0) * osg::Matrix::inverse(modelViewMatrix);
// compute center and the frustumCenterLine centerNearPlane = (corners[0]+corners[1]+corners[5]+corners[4])*0.25; centerFarPlane = (corners[3]+corners[2]+corners[6]+corners[7])*0.25; center = (centerNearPlane+centerFarPlane)*0.5; frustumCenterLine = centerFarPlane-centerNearPlane; frustumCenterLine.normalize();
OSG_INFO<<" center "<<center<<std::endl;
faces[0].push_back(0); faces[0].push_back(3); faces[0].push_back(7); faces[0].push_back(4);
faces[1].push_back(1); faces[1].push_back(5); faces[1].push_back(6); faces[1].push_back(2);
faces[2].push_back(0); faces[2].push_back(1); faces[2].push_back(2); faces[2].push_back(3);
faces[3].push_back(4); faces[3].push_back(7); faces[3].push_back(6); faces[3].push_back(5);
faces[4].push_back(0); faces[4].push_back(4); faces[4].push_back(5); faces[4].push_back(1);
faces[5].push_back(2); faces[5].push_back(6); faces[5].push_back(7); faces[5].push_back(3);
edges[0].push_back(0); edges[0].push_back(1); // corner points on edge edges[0].push_back(2); edges[0].push_back(4); // faces on edge
edges[1].push_back(1); edges[1].push_back(2); // corner points on edge edges[1].push_back(2); edges[1].push_back(1); // faces on edge
edges[2].push_back(2); edges[2].push_back(3); // corner points on edge edges[2].push_back(2); edges[2].push_back(5); // faces on edge
edges[3].push_back(3); edges[3].push_back(0); // corner points on edge edges[3].push_back(2); edges[3].push_back(0); // faces on edge
edges[4].push_back(0); edges[4].push_back(4); // corner points on edge edges[4].push_back(0); edges[4].push_back(4); // faces on edge
edges[5].push_back(1); edges[5].push_back(5); // corner points on edge edges[5].push_back(4); edges[5].push_back(1); // faces on edge
edges[6].push_back(2); edges[6].push_back(6); // corner points on edge edges[6].push_back(1); edges[6].push_back(5); // faces on edge
edges[7].push_back(3); edges[7].push_back(7); // corner points on edge edges[7].push_back(5); edges[7].push_back(0); // faces on edge
edges[8].push_back(4); edges[8].push_back(5); // corner points on edge edges[8].push_back(3); edges[8].push_back(4); // faces on edge
edges[9].push_back(5); edges[9].push_back(6); // corner points on edge edges[9].push_back(3); edges[9].push_back(1); // faces on edge
edges[10].push_back(6);edges[10].push_back(7); // corner points on edge edges[10].push_back(3);edges[10].push_back(5); // faces on edge
edges[11].push_back(7); edges[11].push_back(4); // corner points on edge edges[11].push_back(3); edges[11].push_back(0); // faces on edge}
/////////////////////////////////////////////////////////////////////////////////////////////////// ViewDependentData//MWShadowTechnique::ViewDependentData::ViewDependentData(MWShadowTechnique* vdsm): _viewDependentShadowMap(vdsm){ OSG_INFO<<"ViewDependentData::ViewDependentData()"<<this<<std::endl; for (auto& perFrameStateset : _stateset) perFrameStateset = new osg::StateSet;}
void MWShadowTechnique::ViewDependentData::releaseGLObjects(osg::State* state) const{ for(ShadowDataList::const_iterator itr = _shadowDataList.begin(); itr != _shadowDataList.end(); ++itr) { (*itr)->releaseGLObjects(state); }}
/////////////////////////////////////////////////////////////////////////////////////////////////// MWShadowTechnique//MWShadowTechnique::MWShadowTechnique(): ShadowTechnique(), _enableShadows(false), _debugHud(nullptr), _castingPrograms{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }{ _shadowRecievingPlaceholderStateSet = new osg::StateSet; mSetDummyStateWhenDisabled = false;}
MWShadowTechnique::MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::CopyOp& copyop): ShadowTechnique(vdsm,copyop) , _castingPrograms(vdsm._castingPrograms){ _shadowRecievingPlaceholderStateSet = new osg::StateSet; _enableShadows = vdsm._enableShadows; mSetDummyStateWhenDisabled = vdsm.mSetDummyStateWhenDisabled;}
MWShadowTechnique::~MWShadowTechnique(){ if (_shadowsBin != nullptr) osgUtil::RenderBin::removeRenderBinPrototype(_shadowsBin);}
void MWShadowTechnique::init(){ if (!_shadowedScene) return;
OSG_INFO<<"MWShadowTechnique::init()"<<std::endl;
createShaders();
_dirty = false;}
void MWShadowTechnique::cleanSceneGraph(){ OSG_INFO<<"MWShadowTechnique::cleanSceneGraph()"<<std::endl;}
void MWShadowTechnique::enableShadows(){ _enableShadows = true;}
void MWShadowTechnique::disableShadows(bool setDummyState){ _enableShadows = false; mSetDummyStateWhenDisabled = setDummyState;}
void SceneUtil::MWShadowTechnique::enableDebugHUD(){ _debugHud = new DebugHUD(getShadowedScene()->getShadowSettings()->getNumShadowMapsPerLight());}
void SceneUtil::MWShadowTechnique::disableDebugHUD(){ _debugHud = nullptr;}
void SceneUtil::MWShadowTechnique::setSplitPointUniformLogarithmicRatio(double ratio){ _splitPointUniformLogRatio = ratio;}
void SceneUtil::MWShadowTechnique::setSplitPointDeltaBias(double bias){ _splitPointDeltaBias = bias;}
void SceneUtil::MWShadowTechnique::setPolygonOffset(float factor, float units){ _polygonOffsetFactor = factor; _polygonOffsetUnits = units;
if (_polygonOffset) { _polygonOffset->setFactor(factor); _polygonOffset->setUnits(units); }}
void MWShadowTechnique::setMaximumShadowMapDistance(float distance){ if (_shadowedScene && _shadowedScene->getShadowSettings()) _shadowedScene->getShadowSettings()->setMaximumShadowMapDistance(distance);
osg::ref_ptr<osg::Uniform> newDistance = new osg::Uniform("maximumShadowMapDistance", distance); for (auto& list : _uniforms) { for (auto& u : list) { if (u->getName() == "maximumShadowMapDistance") u = newDistance; } }}
void SceneUtil::MWShadowTechnique::setShadowFadeStart(float shadowFadeStart){ _shadowFadeStart = shadowFadeStart;
osg::ref_ptr<osg::Uniform> newFadeStart = new osg::Uniform("shadowFadeStart", shadowFadeStart); for (auto& list : _uniforms) { for (auto& u : list) { if (u->getName() == "shadowFadeStart") u = newFadeStart; } }}
void SceneUtil::MWShadowTechnique::enableFrontFaceCulling(){ _useFrontFaceCulling = true;
if (_shadowCastingStateSet) { _shadowCastingStateSet->setAttribute(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); }}
void SceneUtil::MWShadowTechnique::disableFrontFaceCulling(){ _useFrontFaceCulling = false;
if (_shadowCastingStateSet) { _shadowCastingStateSet->removeAttribute(osg::StateAttribute::CULLFACE); _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); }}
void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager){ // This can't be part of the constructor as OSG mandates that there be a trivial constructor available
osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader("shadowcasting.vert"); std::string useGPUShader4 = SceneUtil::getGLExtensions().isGpuShader4Supported ? "1" : "0"; for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc) { auto& program = _castingPrograms[alphaFunc - GL_NEVER]; program = shaderManager.getProgram(castingVertexShader, shaderManager.getShader("shadowcasting.frag", { { "alphaFunc", std::to_string(alphaFunc) }, { "alphaToCoverage", "0" }, { "adjustCoverage", "1" }, { "useGPUShader4", useGPUShader4 } })); }}
MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/){ return new ViewDependentData(this);}
MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(osgUtil::CullVisitor* cv){ std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex); ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv); if (itr!=_viewDependentDataMap.end()) return itr->second.get();
osg::ref_ptr<ViewDependentData> vdd = createViewDependentData(cv); _viewDependentDataMap[cv] = vdd; return vdd.release();}
void SceneUtil::MWShadowTechnique::copyShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs){ // Prepare for rendering shadows using the shadow map owned by rhs.
// To achieve this i first copy all data that is not specific to this cv's camera and thus read-only, // trusting openmw and osg won't overwrite that data before this frame is done rendering. // This works due to the double buffering of CullVisitors by osg, but also requires that cull passes are serialized (relative to one another). // Then initialize new copies of the data that will be written with view-specific data // (the stateset and the texgens).
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap; auto* stateset = lhs->getStateSet(cv.getTraversalNumber()); stateset->clear(); lhs->_lightDataList = rhs->_lightDataList; lhs->_numValidShadows = rhs->_numValidShadows; lhs->_cascadeSplits = rhs->_cascadeSplits;
ShadowDataList& sdl = lhs->getShadowDataList(); ShadowDataList previous_sdl; previous_sdl.swap(sdl); for (const auto& rhs_sd : rhs->getShadowDataList()) { osg::ref_ptr<ShadowData> lhs_sd;
if (previous_sdl.empty()) { OSG_INFO << "Create new ShadowData" << std::endl; lhs_sd = new ShadowData(lhs); } else { OSG_INFO << "Taking ShadowData from from of previous_sdl" << std::endl; lhs_sd = previous_sdl.front(); previous_sdl.erase(previous_sdl.begin()); } lhs_sd->_camera = rhs_sd->_camera; lhs_sd->_textureUnit = rhs_sd->_textureUnit; lhs_sd->_texture = rhs_sd->_texture; lhs_sd->_sm_i = rhs_sd->_sm_i; sdl.push_back(lhs_sd); }
copyShadowStateSettings(cv, lhs);
if (lhs->_numValidShadows > 0) { prepareStateSetForRenderingShadow(*lhs, cv.getTraversalNumber()); if (osgUtil::RenderStage* renderStage = cv.getCurrentRenderStage()) renderStage->setStateSet(lhs->getStateSet(cv.getTraversalNumber())); }}
void SceneUtil::MWShadowTechnique::setCustomFrustumCallback(CustomFrustumCallback* cfc){ _customFrustumCallback = cfc;}
void SceneUtil::MWShadowTechnique::copyShadowStateSettings(osgUtil::CullVisitor& cv, ViewDependentData* vdd){ assignCascadeSplits(vdd->_cascadeSplits, vdd->_uniforms[cv.getTraversalNumber()%2]); for (const auto& sd : vdd->getShadowDataList()) { assignValidRegionSettings(cv, sd->_camera, sd->_sm_i, vdd->_uniforms[cv.getTraversalNumber()%2]); assignShadowStateSettings(cv, sd->_camera, sd->_sm_i, vdd->_uniforms[cv.getTraversalNumber()%2]); }}
void MWShadowTechnique::update(osg::NodeVisitor& nv){ OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<<std::endl; _shadowedScene->osg::Group::traverse(nv);}
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv){
if (!_enableShadows) { if (mSetDummyStateWhenDisabled) { osg::ref_ptr<osg::StateSet> dummyState = new osg::StateSet();
ShadowSettings* settings = getShadowedScene()->getShadowSettings(); int baseUnit = settings->getBaseShadowTextureUnit(); int endUnit = baseUnit + settings->getNumShadowMapsPerLight(); for (int i = baseUnit; i < endUnit; ++i) { dummyState->setTextureAttribute(i, _fallbackShadowMapTexture, osg::StateAttribute::ON); dummyState->addUniform(new osg::Uniform(("shadowTexture" + std::to_string(i - baseUnit)).c_str(), i)); } dummyState->addUniform(new osg::Uniform("cascadeSplits", osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f)));
cv.pushStateSet(dummyState); }
_shadowedScene->osg::Group::traverse(cv);
if (mSetDummyStateWhenDisabled) cv.popStateSet();
return; }
OSG_INFO<<std::endl<<std::endl<<"MWShadowTechnique::cull(osg::CullVisitor&"<<&cv<<")"<<std::endl;
if (!_shadowCastingStateSet) { OSG_INFO<<"Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows."<<std::endl; _shadowedScene->osg::Group::traverse(cv); return; }
ViewDependentData* vdd = getViewDependentData(&cv);
if (!vdd) { OSG_INFO<<"Warning, now ViewDependentData created, unable to create shadows."<<std::endl; _shadowedScene->osg::Group::traverse(cv); return; } Uniforms& vddUniforms = vdd->_uniforms[cv.getTraversalNumber() % 2];
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()<<std::endl;
osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
// check whether this main views projection is perspective or orthographic bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 && viewProjectionMatrix(1,3)==0.0 && viewProjectionMatrix(2,3)==0.0;
double minZNear = 0.0; double maxZFar = dbl_max;
if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { double left, right, top, bottom; if (orthographicViewFrustum) { viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); } else { viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); } OSG_INFO<<"minZNear="<<minZNear<<", maxZFar="<<maxZFar<<std::endl; }
// set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride()); }
// 1. Traverse main scene graph auto* shadowReceiverStateSet = vdd->getStateSet(cv.getTraversalNumber()); shadowReceiverStateSet->clear(); if (osgUtil::RenderStage* renderStage = cv.getCurrentRenderStage()) renderStage->setStateSet(shadowReceiverStateSet);
cullShadowReceivingScene(&cv);
if (cv.getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { OSG_INFO<<"Just done main subgraph traversak"<<std::endl; // make sure that the near plane is computed correctly so that any projection matrix computations // are all done correctly. cv.computeNearPlane(); } // clamp the minZNear and maxZFar to those provided by ShadowSettings maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar); if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
//OSG_NOTICE<<"maxZFar "<<maxZFar<<std::endl;
// Workaround for absurdly huge viewing distances where OSG would otherwise push the near plane out. cv.setNearFarRatio(minZNear / maxZFar);
Frustum frustum(&cv, minZNear, maxZFar); if (_customFrustumCallback) { OSG_INFO << "Calling custom frustum callback" << std::endl; osgUtil::CullVisitor* sharedFrustumHint = nullptr; _customClipSpace.init(); _customFrustumCallback->operator()(cv, _customClipSpace, sharedFrustumHint); frustum.setCustomClipSpace(_customClipSpace); if (sharedFrustumHint) { // user hinted another view shares its frustum std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex); auto itr = _viewDependentDataMap.find(sharedFrustumHint); if (itr != _viewDependentDataMap.end()) { OSG_INFO << "User provided a valid shared frustum hint, re-using previously generated shadow map" << std::endl;
copyShadowMap(cv, vdd, itr->second);
// return compute near far mode back to it's original settings cv.setComputeNearFarMode(cachedNearFarMode); return; } else { OSG_INFO << "User provided a shared frustum hint, but it was not valid." << std::endl; } } }
frustum.init(); if (_debugHud) { osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array(); for (osg::Vec3d &vertex : frustum.corners) vertexArray->push_back((osg::Vec3)vertex); _debugHud->setFrustumVertices(vertexArray, cv.getTraversalNumber()); }
double reducedNear, reducedFar; if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { reducedNear = osg::maximum<double>(cv.getCalculatedNearPlane(), minZNear); reducedFar = osg::minimum<double>(cv.getCalculatedFarPlane(), maxZFar); } else { reducedNear = minZNear; reducedFar = maxZFar; } // return compute near far mode back to it's original settings cv.setComputeNearFarMode(cachedNearFarMode);
OSG_INFO<<"frustum.eye="<<frustum.eye<<", frustum.centerNearPlane, "<<frustum.centerNearPlane<<" distance = "<<(frustum.eye-frustum.centerNearPlane).length()<<std::endl;
// 2. select active light sources // create a list of light sources + their matrices to place them selectActiveLights(&cv, vdd);
unsigned int pos_x = 0; unsigned int textureUnit = settings->getBaseShadowTextureUnit(); unsigned int numValidShadows = 0;
ShadowDataList& sdl = vdd->getShadowDataList(); ShadowDataList previous_sdl; previous_sdl.swap(sdl);
unsigned int numShadowMapsPerLight = settings->getNumShadowMapsPerLight(); osg::Vec4 cascadeSplits(0.0f, 0.0f, 0.0f, 0.0f);
LightDataList& pll = vdd->getLightDataList(); for(LightDataList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures
LightData& pl = **itr;
// 3.1 compute light space polytope // osg::Polytope polytope = computeLightViewFrustumPolytope(frustum, pl);
// if polytope is empty then no rendering. if (polytope.empty()) { OSG_NOTICE<<"Polytope empty no shadow to render"<<std::endl; continue; }
// 3.2 compute RTT camera view+projection matrix settings // osg::Matrixd projectionMatrix; osg::Matrixd viewMatrix; if (!computeShadowCameraSettings(frustum, pl, projectionMatrix, viewMatrix)) { OSG_NOTICE<<"No valid Camera settings, no shadow to render"<<std::endl; continue; }
// if we are using multiple shadow maps and CastShadowTraversalMask is being used // traverse the scene to compute the extents of the objects if (/*numShadowMapsPerLight>1 &&*/ (_shadowedScene->getCastsShadowTraversalMask() & _worldMask) == 0) { // osg::ElapsedTime timer;
osg::ref_ptr<osg::Viewport> viewport = new osg::Viewport(0,0,2048,2048); if (!_clsb) _clsb = new ComputeLightSpaceBounds; ComputeLightSpaceBounds& clsb = *_clsb; clsb.reset(); clsb.pushViewport(viewport); clsb.pushProjectionMatrix(new osg::RefMatrix(projectionMatrix)); clsb.pushModelViewMatrix(new osg::RefMatrix(viewMatrix), osg::Transform::ABSOLUTE_RF); clsb.setTraversalMask(_shadowedScene->getCastsShadowTraversalMask());
osg::Matrixd invertModelView; invertModelView.invert(viewMatrix); osg::Polytope local_polytope(polytope); local_polytope.transformProvidingInverse(invertModelView);
osg::CullingSet& cs = clsb.getProjectionCullingStack().back(); cs.setFrustum(local_polytope); clsb.pushCullingSet();
_shadowedScene->accept(clsb);
clsb.popCullingSet();
clsb.popModelViewMatrix(); clsb.popProjectionMatrix(); clsb.popViewport();
// OSG_NOTICE<<"Extents of LightSpace "<<clsb._bb.xMin()<<", "<<clsb._bb.xMax()<<", "<<clsb._bb.yMin()<<", "<<clsb._bb.yMax()<<", "<<clsb._bb.zMin()<<", "<<clsb._bb.zMax()<<std::endl; // OSG_NOTICE<<" time "<<timer.elapsedTime_m()<<"ms, mask = "<<std::hex<<_shadowedScene->getCastsShadowTraversalMask()<<std::endl;
if (clsb._bb.xMin()>-1.0f || clsb._bb.xMax()<1.0f || clsb._bb.yMin()>-1.0f || clsb._bb.yMax()<1.0f) { // OSG_NOTICE<<"Need to clamp projection matrix"<<std::endl;
#if 1 double xMid = (clsb._bb.xMin()+clsb._bb.xMax())*0.5f; double xRange = clsb._bb.xMax()-clsb._bb.xMin();#else double xMid = 0.0; double xRange = 2.0;#endif double yMid = (clsb._bb.yMin()+clsb._bb.yMax())*0.5f; double yRange = (clsb._bb.yMax()-clsb._bb.yMin());
osg::Matrixd cornerConverter = osg::Matrixd::inverse(projectionMatrix) * osg::Matrixd::inverse(viewMatrix) * *cv.getModelViewMatrix(); double minZ = dbl_max; double maxZ = -dbl_max; clsb._bb._max[2] = 1.0; for (unsigned int i = 0; i < 8; i++) { osg::Vec3 corner = clsb._bb.corner(i); corner = corner * cornerConverter;
maxZ = osg::maximum<double>(maxZ, -corner.z()); minZ = osg::minimum<double>(minZ, -corner.z()); } reducedNear = osg::maximum<double>(reducedNear, minZ); reducedFar = osg::minimum<double>(reducedFar, maxZ);
// OSG_NOTICE<<" xMid="<<xMid<<", yMid="<<yMid<<", xRange="<<xRange<<", yRange="<<yRange<<std::endl;
projectionMatrix = projectionMatrix * osg::Matrixd::translate(osg::Vec3d(-xMid,-yMid,0.0)) * osg::Matrixd::scale(osg::Vec3d(2.0/xRange, 2.0/yRange,1.0));
}
}
#if 0 double splitPoint = 0.0;
if (numShadowMapsPerLight>1) { osg::Vec3d eye_v = frustum.eye * viewMatrix; osg::Vec3d center_v = frustum.center * viewMatrix; osg::Vec3d viewdir_v = center_v-eye_v; viewdir_v.normalize(); osg::Vec3d lightdir(0.0,0.0,-1.0);
double dotProduct_v = lightdir * viewdir_v; double angle = acosf(dotProduct_v);
osg::Vec3d eye_ls = eye_v * projectionMatrix;
OSG_INFO<<"Angle between view vector and eye "<<osg::RadiansToDegrees(angle)<<std::endl; OSG_INFO<<"eye_ls="<<eye_ls<<std::endl;
if (eye_ls.y()>=-1.0 && eye_ls.y()<=1.0) { OSG_INFO<<"Eye point inside light space clip region "<<std::endl; splitPoint = 0.0; } else { double n = -1.0-eye_ls.y(); double f = 1.0-eye_ls.y(); double sqrt_nf = sqrt(n*f); double mid = eye_ls.y()+sqrt_nf; double ratioOfMidToUseForSplit = 0.8; splitPoint = mid * ratioOfMidToUseForSplit;
OSG_INFO<<" n="<<n<<", f="<<f<<", sqrt_nf="<<sqrt_nf<<" mid="<<mid<<std::endl; } }#endif
const bool debug = settings->getDebugDraw();
// 4. For each light/shadow map for (unsigned int sm_i=0; sm_i<numShadowMapsPerLight; ++sm_i) { osg::ref_ptr<ShadowData> sd;
if (previous_sdl.empty()) { OSG_INFO<<"Create new ShadowData"<<std::endl; sd = new ShadowData(vdd); } else { OSG_INFO<<"Taking ShadowData from from of previous_sdl"<<std::endl; sd = previous_sdl.front(); previous_sdl.erase(previous_sdl.begin());
if (!debug) sd->updateTextureSize(); }
osg::ref_ptr<osg::Camera> camera = sd->_camera;
camera->setProjectionMatrix(projectionMatrix); camera->setViewMatrix(viewMatrix);
if (debug) { camera->getViewport()->x() = pos_x; pos_x += static_cast<unsigned int>(camera->getViewport()->width()) + 40; }
if (_useFrontFaceCulling) camera->setCullingMode(camera->getCullingMode() | osg::CullSettings::CLUSTER_CULLING); else camera->setCullingMode(camera->getCullingMode() & ~osg::CullSettings::CLUSTER_CULLING);
camera->setCullingMode(camera->getCullingMode() | osg::CullSettings::SMALL_FEATURE_CULLING); // In shadow maps (2048x2048), distant cascades cover vast areas where small clutter // (chairs, crates, lanterns) projects to sub-pixel or invisible screen sizes. // Cull small casters progressively so distant cascades only render significant structures. static const float sCascadePixelSizes[4] = { 12.0f, 50.0f, 120.0f, 300.0f }; float cascadeCullingPixelSize = sCascadePixelSizes[std::min(sm_i, 3u)]; camera->setSmallFeatureCullingPixelSize(cascadeCullingPixelSize);
// transform polytope in model coords into light spaces eye coords. osg::Matrixd invertModelView; invertModelView.invert(camera->getViewMatrix());
osg::Polytope local_polytope(polytope); local_polytope.transformProvidingInverse(invertModelView);
double cascaseNear = reducedNear; double cascadeFar = reducedFar; if (numShadowMapsPerLight>1) { // compute the start and end range in non-dimensional coords#if 0 double r_start = (sm_i==0) ? -1.0 : (double(sm_i)/double(numShadowMapsPerLight)*2.0-1.0); double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : (double(sm_i+1)/double(numShadowMapsPerLight)*2.0-1.0);#elif 0
// hardwired for 2 splits double r_start = (sm_i==0) ? -1.0 : splitPoint; double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : splitPoint;#else double r_start, r_end;
// split system based on the original Parallel Split Shadow Maps paper. double n = reducedNear; double f = reducedFar; double i = double(sm_i); double m = double(numShadowMapsPerLight); if (sm_i == 0) r_start = -1.0; else { // compute the split point in main camera view double ciLog = n * pow(f / n, i / m); double ciUniform = n + (f - n) * i / m; double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias; cascaseNear = ci;
// work out where this is in light space osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci; osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix; r_start = lightSpacePos.y(); }
if (sm_i + 1 == numShadowMapsPerLight) r_end = 1.0; else { // compute the split point in main camera view double ciLog = n * pow(f / n, (i + 1) / m); double ciUniform = n + (f - n) * (i + 1) / m; double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias; cascadeFar = ci; // work out where this is in light space osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci; osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix; r_end = lightSpacePos.y(); }#endif // for all by the last shadowmap shift the r_end so that it overlaps slightly with the next shadowmap // to prevent a seam showing through between the shadowmaps if (sm_i+1<numShadowMapsPerLight) r_end+=0.01;
// We can't add these clipping planes with cascaded shadow maps as they wouldn't be parallel to the light direction.
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i>0) { // not the first shadowmap so insert a polytope to clip the scene from before r_start
// plane in clip space coords osg::Plane plane(0.0,1.0,0.0,-r_start);
// transform into eye coords plane.transformProvidingInverse(projectionMatrix); local_polytope.getPlaneList().push_back(plane);
//OSG_NOTICE<<"Adding r_start plane "<<plane<<std::endl;
}
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i+1<numShadowMapsPerLight) { // not the last shadowmap so insert a polytope to clip the scene from beyond r_end
// plane in clip space coords osg::Plane plane(0.0,-1.0,0.0,r_end);
// transform into eye coords plane.transformProvidingInverse(projectionMatrix); local_polytope.getPlaneList().push_back(plane);
//OSG_NOTICE<<"Adding r_end plane "<<plane<<std::endl; }
local_polytope.setupMask();
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT) { // OSG_NOTICE<<"Need to adjust RTT camera projection and view matrix here, r_start="<<r_start<<", r_end="<<r_end<<std::endl; // OSG_NOTICE<<" textureUnit = "<<textureUnit<<std::endl;
double mid_r = (r_start + r_end)*0.5; double range_r = (r_end - r_start);
// OSG_NOTICE<<" mid_r = "<<mid_r<<", range_r = "<<range_r<<std::endl;
camera->setProjectionMatrix( camera->getProjectionMatrix() * osg::Matrixd::translate(osg::Vec3d(0.0,-mid_r,0.0)) * osg::Matrixd::scale(osg::Vec3d(1.0,2.0/range_r,1.0))); } }
if (sm_i < 4) cascadeSplits[sm_i] = static_cast<float>(cascadeFar * 1.05);
std::vector<osg::Plane> extraPlanes; if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED) { cropShadowCameraToMainFrustum(frustum, camera, cascaseNear, cascadeFar, extraPlanes); for (const auto& plane : extraPlanes) local_polytope.getPlaneList().push_back(plane); local_polytope.setupMask(); } else cropShadowCameraToMainFrustum(frustum, camera, reducedNear, reducedFar, extraPlanes);
osg::ref_ptr<VDSMCameraCullCallback> vdsmCallback = new VDSMCameraCullCallback(this, local_polytope); camera->setCullCallback(vdsmCallback.get());
// 4.3 traverse RTT camera //
cv.pushStateSet(_shadowCastingStateSet.get());
cullShadowCastingScene(&cv, camera.get());
cv.popStateSet();
if (!orthographicViewFrustum && settings->getShadowMapProjectionHint()==ShadowSettings::PERSPECTIVE_SHADOW_MAP) { assignValidRegionSettings(cv, camera, sm_i, vddUniforms);
if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED) adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), cascaseNear, cascadeFar); else adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), reducedNear, reducedFar); if (vdsmCallback->getProjectionMatrix()) { vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix()); } } // 4.4 compute main scene graph TexGen + uniform settings + setup state // { assignShadowStateSettings(cv, camera, sm_i, vddUniforms); }
// mark the light as one that has active shadows and requires shaders pl.textureUnits.push_back(textureUnit);
// pass on shadow data to ShadowDataList sd->_textureUnit = textureUnit; sd->_sm_i = sm_i;
sdl.push_back(sd);
// increment counters. ++textureUnit; ++numValidShadows ;
if (_debugHud) _debugHud->draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv); } }
vdd->setNumValidShadows(numValidShadows);
vdd->_cascadeSplits = cascadeSplits; assignCascadeSplits(vdd->_cascadeSplits, vddUniforms);
if (numValidShadows>0) { prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber()); }
// OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()<<std::endl;}
bool MWShadowTechnique::selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const{ OSG_INFO<<"selectActiveLights"<<std::endl;
LightDataList& pll = vdd->getLightDataList();
LightDataList previous_ldl; previous_ldl.swap(pll);
//MR testing giving a specific light osgUtil::RenderStage * rs = cv->getCurrentRenderBin()->getStage();
OSG_INFO<<"selectActiveLights osgUtil::RenderStage="<<rs<<std::endl;
osg::Matrixd modelViewMatrix = *(cv->getModelViewMatrix());
struct FindFirstLightManager : public osg::NodeVisitor { FindFirstLightManager() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { }
void apply(osg::Group& node) override { if (auto* lm = dynamic_cast<LightManager*>(&node)) { mLightManager = lm; mLocalToWorld = new osg::RefMatrix(osg::computeLocalToWorld(getNodePath())); return; } traverse(node); } LightManager* mLightManager = nullptr; osg::ref_ptr<osg::RefMatrix> mLocalToWorld; };
FindFirstLightManager lightManagerFinder;
_shadowedScene->accept(lightManagerFinder);
if (lightManagerFinder.mLightManager && lightManagerFinder.mLightManager->getSunlight()) { LightData* ld = new LightData(vdd); ld->setLightData(lightManagerFinder.mLocalToWorld, lightManagerFinder.mLightManager->getSunlight(), modelViewMatrix); pll.push_back(ld); }
return !pll.empty();}
void MWShadowTechnique::createShaders(){ OSG_INFO<<"MWShadowTechnique::createShaders()"<<std::endl;
unsigned int _baseTextureUnit = 0;
_shadowCastingStateSet = new osg::StateSet;
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
if (!settings->getDebugDraw()) { // note soft (attribute only no mode override) setting. When this works ? // 1. for objects prepared for backface culling // because they usually also set CullFace and CullMode on in their state // For them we override CullFace but CullMode remains set by them // 2. For one faced, trees, and similar objects which cannot use // backface nor front face so they usually use CullMode off set here. // In this case we will draw them in their entirety.
if (_useFrontFaceCulling) { _shadowCastingStateSet->setAttribute(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
// make sure GL_CULL_FACE is off by default // we assume that if object has cull face attribute set to back // it will also set cull face mode ON so no need for override _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); } else _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); }
_polygonOffset = new osg::PolygonOffset(_polygonOffsetFactor, _polygonOffsetUnits); _shadowCastingStateSet->setAttribute(_polygonOffset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::Uniform> baseTextureSampler = new osg::Uniform("baseTexture",(int)_baseTextureUnit); osg::ref_ptr<osg::Uniform> baseTextureUnit = new osg::Uniform("baseTextureUnit",(int)_baseTextureUnit);
osg::ref_ptr<osg::Uniform> maxDistance = new osg::Uniform("maximumShadowMapDistance", (float)settings->getMaximumShadowMapDistance()); osg::ref_ptr<osg::Uniform> fadeStart = new osg::Uniform("shadowFadeStart", (float)_shadowFadeStart); osg::ref_ptr<osg::Uniform> cascadeSplits = new osg::Uniform("cascadeSplits", osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
for (auto& perFrameUniformList : _uniforms) { perFrameUniformList.clear(); perFrameUniformList.push_back(baseTextureSampler); perFrameUniformList.emplace_back(baseTextureUnit.get()); perFrameUniformList.push_back(maxDistance); perFrameUniformList.push_back(fadeStart); perFrameUniformList.push_back(cascadeSplits); }
for(unsigned int sm_i=0; sm_i<settings->getNumShadowMapsPerLight(); ++sm_i) { { std::stringstream sstr; sstr<<"shadowTexture"<<sm_i; osg::ref_ptr<osg::Uniform> shadowTextureSampler = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i)); for (auto& perFrameUniformList : _uniforms) perFrameUniformList.emplace_back(shadowTextureSampler.get()); } }
switch(settings->getShaderHint()) { case(ShadowSettings::NO_SHADERS): { OSG_INFO<<"No shaders provided by, user must supply own shaders"<<std::endl; break; } case(ShadowSettings::PROVIDE_VERTEX_AND_FRAGMENT_SHADER): case(ShadowSettings::PROVIDE_FRAGMENT_SHADER): { _program = new osg::Program;
//osg::ref_ptr<osg::Shader> fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture); if (settings->getNumShadowMapsPerLight()==2) { _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture_twoShadowMaps)); } else { _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture)); }
break; } }
{ osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage( 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE ); *(osg::Vec4ub*)image->data() = osg::Vec4ub( 0xFF, 0xFF, 0xFF, 0xFF );
_fallbackBaseTexture = new osg::Texture2D(image.get()); _fallbackBaseTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT); _fallbackBaseTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT); _fallbackBaseTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST); _fallbackBaseTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
_fallbackShadowMapTexture = new osg::Texture2D(image.get()); _fallbackShadowMapTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT); _fallbackShadowMapTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT); _fallbackShadowMapTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST); _fallbackShadowMapTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST); _fallbackShadowMapTexture->setShadowComparison(true); _fallbackShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);
}
if (!_castingPrograms[GL_ALWAYS - GL_NEVER]) OSG_NOTICE << "Shadow casting shader has not been set up. Remember to call setupCastingShader(Shader::ShaderManager &)" << std::endl;
// Always use the GL_ALWAYS shader as the shadows bin will change it if necessary _shadowCastingStateSet->setAttributeAndModes(_castingPrograms[GL_ALWAYS - GL_NEVER], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); // The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied _shadowCastingStateSet->setTextureAttribute(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); _shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr<osg::Depth> depth = new osg::Depth; depth->setWriteMask(true); osg::ref_ptr<osg::ClipControl> clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); _shadowCastingStateSet->setAttribute(clipcontrol, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON);
// TODO: compare performance when alpha testing is handled here versus using a discard in the fragment shader}
osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight){ OSG_INFO<<"computeLightViewFrustumPolytope()"<<std::endl;
osg::Polytope polytope; if (frustum.useCustomClipSpace) { polytope.setToBoundingBox(frustum.customClipSpace); } else { polytope.setToUnitFrustum(); }
polytope.transformProvidingInverse( frustum.projectionMatrix ); polytope.transformProvidingInverse( frustum.modelViewMatrix );
osg::Polytope lightVolumePolytope;
if (positionedLight.directionalLight) { osg::Polytope::PlaneList& planes = polytope.getPlaneList(); osg::Polytope::ClippingMask selector_mask = 0x1; osg::Polytope::ClippingMask result_mask = 0x0; for(unsigned int i=0; i<planes.size(); ++i, selector_mask <<= 1) { OSG_INFO<<" plane "<<planes[i]<<" planes["<<i<<"].dotProductNormal(lightDir)="<<planes[i].dotProductNormal(positionedLight.lightDir); if (planes[i].dotProductNormal(positionedLight.lightDir)>=0.0) { OSG_INFO<<" Need remove side "<<i<<std::endl; } else { OSG_INFO<<std::endl; lightVolumePolytope.add(planes[i]); result_mask = result_mask | selector_mask; } }
OSG_INFO<<" planes.size() = "<<planes.size()<<std::endl; OSG_INFO<<" planes.getResultMask() = "<<polytope.getResultMask()<<std::endl; OSG_INFO<<" resultMask = "<<result_mask<<std::endl; polytope.setResultMask(result_mask); } else { const osg::Polytope::PlaneList& planes = polytope.getPlaneList(); osg::Polytope::ClippingMask selector_mask = 0x1; osg::Polytope::ClippingMask result_mask = 0x0; for(unsigned int i=0; i<planes.size(); ++i, selector_mask <<= 1) {
double d = planes[i].distance(positionedLight.lightPos3); OSG_INFO<<" plane "<<planes[i]<<" planes["<<i<<"].distance(lightPos3)="<<d; if (d<0.0) { OSG_INFO<<" Need remove side "<<i<<std::endl; } else { OSG_INFO<<std::endl; lightVolumePolytope.add(planes[i]); result_mask = result_mask | selector_mask; } } OSG_INFO<<" planes.size() = "<<planes.size()<<std::endl; OSG_INFO<<" planes.getResultMask() = "<<polytope.getResultMask()<<std::endl; OSG_INFO<<" resultMask = "<<result_mask<<std::endl; polytope.setResultMask(result_mask); }
OSG_INFO<<"Which frustum edges are active?"<<std::endl; for(unsigned int i=0; i<12; ++i) { Frustum::Indices& indices = frustum.edges[i];
unsigned int corner_a = indices[0]; unsigned int corner_b = indices[1]; unsigned int face_a = indices[2]; unsigned int face_b = indices[3]; bool face_a_active = (polytope.getResultMask()&(0x1<<face_a))!=0; bool face_b_active = (polytope.getResultMask()&(0x1<<face_b))!=0; unsigned int numActive = 0; if (face_a_active) ++numActive; if (face_b_active) ++numActive; if (numActive==1) {
osg::Plane boundaryPlane;
if (positionedLight.directionalLight) { osg::Vec3d normal = (frustum.corners[corner_b]-frustum.corners[corner_a])^positionedLight.lightDir; normal.normalize(); boundaryPlane.set(normal, frustum.corners[corner_a]); } else { boundaryPlane.set(positionedLight.lightPos3, frustum.corners[corner_a], frustum.corners[corner_b]); }
OSG_INFO<<"Boundary Edge "<<i<<", corner_a="<<corner_a<<", corner_b="<<corner_b<<", face_a_active="<<face_a_active<<", face_b_active="<<face_b_active; if (boundaryPlane.distance(frustum.center)<0.0) { boundaryPlane.flip(); OSG_INFO<<", flipped boundary edge "<<boundaryPlane<<std::endl; } else { OSG_INFO<<", no need to flip boundary edge "<<boundaryPlane<<std::endl; } lightVolumePolytope.add(boundaryPlane); } else OSG_INFO<<"Internal Edge "<<i<<", corner_a="<<corner_a<<", corner_b="<<corner_b<<", face_a_active="<<face_a_active<<", face_b_active="<<face_b_active<<std::endl; }
const osg::Polytope::PlaneList& planes = lightVolumePolytope.getPlaneList(); for(unsigned int i=0; i<planes.size(); ++i) { OSG_INFO<<" plane "<<planes[i]<<" "<<((lightVolumePolytope.getResultMask() & (0x1<<i))?"on":"off")<<std::endl; }
return lightVolumePolytope;}
bool MWShadowTechnique::computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix){ OSG_INFO<<"standardShadowMapCameraSettings()"<<std::endl;
osg::Vec3d lightSide;
const ShadowSettings* settings = getShadowedScene()->getShadowSettings();
double dotProduct_v = positionedLight.lightDir * frustum.frustumCenterLine; double gamma_v = acos(dotProduct_v); if (gamma_v<osg::DegreesToRadians(settings->getPerspectiveShadowMapCutOffAngle()) || gamma_v>osg::DegreesToRadians(180.0-settings->getPerspectiveShadowMapCutOffAngle())) { OSG_INFO<<"View direction and Light direction below tolerance"<<std::endl; osg::Vec3d viewSide = osg::Matrixd::transform3x3(frustum.modelViewMatrix, osg::Vec3d(1.0,0.0,0.0)); lightSide = positionedLight.lightDir ^ (viewSide ^ positionedLight.lightDir); lightSide.normalize(); } else { lightSide = positionedLight.lightDir ^ frustum.frustumCenterLine; lightSide.normalize(); }
osg::Vec3d lightUp = lightSide ^ positionedLight.lightDir;
#if 0 OSG_NOTICE<<"positionedLight.lightDir="<<positionedLight.lightDir<<std::endl; OSG_NOTICE<<"lightSide="<<lightSide<<std::endl; OSG_NOTICE<<"lightUp="<<lightUp<<std::endl;#endif
if (positionedLight.directionalLight) { double xMin=0.0, xMax=0.0; double yMin=0.0, yMax=0.0; double zMin=0.0, zMax=0.0;
for(Frustum::Vertices::iterator itr = frustum.corners.begin(); itr != frustum.corners.end(); ++itr) { osg::Vec3d cornerDelta(*itr - frustum.center); osg::Vec3d cornerInLightCoords(cornerDelta*lightSide, cornerDelta*lightUp, cornerDelta*positionedLight.lightDir);
OSG_INFO<<" corner ="<<*itr<<" in lightcoords "<<cornerInLightCoords<<std::endl;
xMin = osg::minimum( xMin, cornerInLightCoords.x()); xMax = osg::maximum( xMax, cornerInLightCoords.x()); yMin = osg::minimum( yMin, cornerInLightCoords.y()); yMax = osg::maximum( yMax, cornerInLightCoords.y()); zMin = osg::minimum( zMin, cornerInLightCoords.z()); zMax = osg::maximum( zMax, cornerInLightCoords.z()); }
OSG_INFO<<"before bs xMin="<<xMin<<", xMax="<<xMax<<", yMin="<<yMin<<", yMax="<<yMax<<", zMin="<<zMin<<", zMax="<<zMax<<std::endl;
osg::BoundingSphere bs = _shadowedScene->getBound(); osg::Vec3d modelCenterRelativeFrustumCenter(bs.center()-frustum.center); osg::Vec3d modelCenterInLightCoords(modelCenterRelativeFrustumCenter*lightSide, modelCenterRelativeFrustumCenter*lightUp, modelCenterRelativeFrustumCenter*positionedLight.lightDir);
OSG_INFO<<"modelCenterInLight="<<modelCenterInLightCoords<<" radius="<<bs.radius()<<std::endl; double radius(bs.radius());
xMin = osg::maximum(xMin, modelCenterInLightCoords.x()-radius); xMax = osg::minimum(xMax, modelCenterInLightCoords.x()+radius); yMin = osg::maximum(yMin, modelCenterInLightCoords.y()-radius); yMax = osg::minimum(yMax, modelCenterInLightCoords.y()+radius); zMin = modelCenterInLightCoords.z()-radius; zMax = osg::minimum(zMax, modelCenterInLightCoords.z()+radius);
OSG_INFO<<"after bs xMin="<<xMin<<", xMax="<<xMax<<", yMin="<<yMin<<", yMax="<<yMax<<", zMin="<<zMin<<", zMax="<<zMax<<std::endl;
if (xMin>=xMax || yMin>=yMax || zMin>=zMax) { OSG_INFO<<"Warning nothing available to create shadows"<<zMax<<std::endl; return false; } else { projectionMatrix.makeOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin); viewMatrix.makeLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center+positionedLight.lightDir*zMax, lightUp); } } else { double zMax=-dbl_max;
OSG_INFO<<"lightDir = "<<positionedLight.lightDir<<std::endl; OSG_INFO<<"lightPos3 = "<<positionedLight.lightPos3<<std::endl; for(Frustum::Vertices::iterator itr = frustum.corners.begin(); itr != frustum.corners.end(); ++itr) { osg::Vec3d cornerDelta(*itr - positionedLight.lightPos3); osg::Vec3d cornerInLightCoords(cornerDelta*lightSide, cornerDelta*lightUp, cornerDelta*positionedLight.lightDir);
OSG_INFO<<" cornerInLightCoords= "<<cornerInLightCoords<<std::endl;
zMax = osg::maximum( zMax, cornerInLightCoords.z()); }
OSG_INFO<<"zMax = "<<zMax<<std::endl;
if (zMax<0.0) { // view frustum entirely behind light return false; }
double minRatio = 0.0001; double zMin=zMax*minRatio;
#if 0 double fov = positionedLight.light->getSpotCutoff() * 2.0;#else double fov = 180.0;#endif if(fov < 180.0) // spotlight { projectionMatrix.makePerspective(fov, 1.0, zMin, zMax); viewMatrix.makeLookAt(positionedLight.lightPos3, positionedLight.lightPos3+positionedLight.lightDir, lightUp); } else { double fovMAX = 160.0f; fov = 0.0;
// calculate the max FOV from the corners of the frustum relative to the light position for(Frustum::Vertices::iterator itr = frustum.corners.begin(); itr != frustum.corners.end(); ++itr) { osg::Vec3d cornerDelta(*itr - positionedLight.lightPos3); double length = cornerDelta.length();
if (length==0.0) fov = osg::minimum(fov, 180.0); else { double dotProduct = cornerDelta*positionedLight.lightDir; double angle = 2.0*osg::RadiansToDegrees( acos(dotProduct/length) ); fov = osg::maximum(fov, angle); } }
OSG_INFO<<"Computed fov = "<<fov<<std::endl;
if (fov>fovMAX) { OSG_INFO<<"Clampping fov = "<<fov<<std::endl; fov=fovMAX; }
projectionMatrix.makePerspective(fov, 1.0, zMin, zMax); viewMatrix.makeLookAt(positionedLight.lightPos3, positionedLight.lightPos3+positionedLight.lightDir, lightUp);
} } return true;}
struct ConvexHull{ typedef std::vector<osg::Vec3d> Vertices; typedef std::pair<osg::Vec3d, osg::Vec3d> Edge; typedef std::vector<Edge> Edges; typedef std::vector<osg::Vec3d> VertexSet;
Edges _edges;
bool valid() const { return !_edges.empty(); }
void setToFrustum(MWShadowTechnique::Frustum& frustum) { _edges.emplace_back(frustum.corners[0], frustum.corners[1]); _edges.emplace_back(frustum.corners[1], frustum.corners[2]); _edges.emplace_back(frustum.corners[2], frustum.corners[3]); _edges.emplace_back(frustum.corners[3], frustum.corners[0]);
_edges.emplace_back(frustum.corners[4], frustum.corners[5]); _edges.emplace_back(frustum.corners[5], frustum.corners[6]); _edges.emplace_back(frustum.corners[6], frustum.corners[7]); _edges.emplace_back(frustum.corners[7], frustum.corners[4]);
_edges.emplace_back(frustum.corners[0], frustum.corners[4]); _edges.emplace_back(frustum.corners[1], frustum.corners[5]); _edges.emplace_back(frustum.corners[2], frustum.corners[6]); _edges.emplace_back(frustum.corners[3], frustum.corners[7]); }
struct ConvexHull2D { // Implementation based on https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain#C++ typedef osg::Vec3d Point;
static double cross(const Point &O, const Point &A, const Point &B) { return (A.x() - O.x())*(B.y() - O.y()) - (A.y() - O.y())*(B.x() - O.x()); }
// Calculates the 2D convex hull and returns it as a vector containing the points in CCW order with the first and last point being the same. static Vertices convexHull(const VertexSet &P) { size_t n = P.size(), k = 0; if (n <= 3) return Vertices(P.cbegin(), P.cend());
Vertices H(2 * n);
// Points are already sorted in a std::set
// Build lower hull for(const auto& vert : P) { while (k >= 2 && cross(H[k - 2], H[k - 1], vert) <= 0) k--; H[k++] = vert; }
// Build upper hull size_t t = k + 1; for (auto pItr = std::next(P.crbegin()); pItr != P.crend(); ++pItr) { while (k >= t && cross(H[k - 2], H[k - 1], *pItr) <= 0) k--; H[k++] = *pItr; }
H.resize(k - 1); return H; } };
Vertices findInternalEdges(const osg::Vec3d& mainVertex, const Vertices& connectedVertices) { Vertices internalEdgeVertices; for (const auto& vertex : connectedVertices) { osg::Matrixd matrix; osg::Vec3d dir = vertex - mainVertex; matrix.makeLookAt(mainVertex, vertex, dir.z() == 0 ? osg::Vec3d(0, 0, 1) : osg::Vec3d(1, 0, 0)); Vertices testVertices; for (const auto& testVertex : connectedVertices) { if (vertex != testVertex) testVertices.push_back(testVertex); } std::vector<double> bearings; for (const auto& testVertex : testVertices) { osg::Vec3d transformedVertex = testVertex * matrix; bearings.push_back(atan2(transformedVertex.y(), transformedVertex.x())); } std::sort(bearings.begin(), bearings.end()); bool keep = false; for (auto itr = bearings.begin(); itr + 1 != bearings.end(); ++itr) { if (*itr + osg::PI < *(itr + 1)) { keep = true; break; } } if (!keep && bearings[0] + osg::PI > bearings.back()) keep = true; if (!keep) internalEdgeVertices.push_back(vertex); } return internalEdgeVertices; }
void extendTowardsNegativeZ() { // Collect the set of vertices VertexSet vertices; for (const Edge& edge : _edges) { vertices.emplace_back(edge.first); vertices.emplace_back(edge.second); }
// Sort and make unique. std::sort(vertices.begin(), vertices.end()); vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
if (vertices.size() == 0) return;
// Get the vertices contributing to the 2D convex hull Vertices extremeVertices = ConvexHull2D::convexHull(vertices);
// Add their extrusions to the final edge collection // We extrude as far as -1.5 as the coordinate space shouldn't ever put any shadow casters further than -1.0 Edges finalEdges; // Add edges towards -Z for (const auto& vertex : extremeVertices) finalEdges.emplace_back(vertex, osg::Vec3d(vertex.x(), vertex.y(), -1.5)); // Add edge loop to 'seal' the hull for (auto itr = extremeVertices.cbegin(); itr != extremeVertices.cend() - 1; ++itr) finalEdges.emplace_back(osg::Vec3d(itr->x(), itr->y(), -1.5), osg::Vec3d((itr + 1)->x(), (itr + 1)->y(), -1.5)); // The convex hull algorithm we are using sometimes places a point at both ends of the vector, so we don't always need to add the last edge separately. if (extremeVertices.front() != extremeVertices.back()) finalEdges.emplace_back(osg::Vec3d(extremeVertices.front().x(), extremeVertices.front().y(), -1.5), osg::Vec3d(extremeVertices.back().x(), extremeVertices.back().y(), -1.5));
// Remove internal edges connected to extreme vertices for (const auto& vertex : extremeVertices) { Vertices connectedVertices; for (const Edge& edge : _edges) { if (edge.first == vertex) connectedVertices.push_back(edge.second); else if (edge.second == vertex) connectedVertices.push_back(edge.first); } connectedVertices.emplace_back(vertex.x(), vertex.y(), -1.5);
Vertices unwantedEdgeEnds = findInternalEdges(vertex, connectedVertices); for (const auto& edgeEnd : unwantedEdgeEnds) { const auto edgeA = Edge(vertex, edgeEnd); const auto edgeB = Edge(edgeEnd, vertex); _edges.erase(std::remove_if(_edges.begin(), _edges.end(), [&](const auto& elem) { return elem == edgeA || elem == edgeB; }), _edges.end()); } }
// Gather connected vertices VertexSet unprocessedConnectedVertices = std::move(extremeVertices);
VertexSet connectedVertices; const auto containsVertex = [&](const auto& vert) { return std::find(connectedVertices.begin(), connectedVertices.end(), vert) != connectedVertices.end(); };
while (!unprocessedConnectedVertices.empty()) { osg::Vec3d vertex = unprocessedConnectedVertices.back(); unprocessedConnectedVertices.pop_back();
connectedVertices.emplace_back(vertex); for (const Edge& edge : _edges) { osg::Vec3d otherEnd; if (edge.first == vertex) otherEnd = edge.second; else if (edge.second == vertex) otherEnd = edge.first; else continue;
if (containsVertex(otherEnd)) continue;
unprocessedConnectedVertices.emplace_back(otherEnd); } }
for (const Edge& edge : _edges) { if (containsVertex(edge.first) || containsVertex(edge.second)) finalEdges.push_back(edge); }
_edges = std::move(finalEdges); }
void transform(const osg::Matrixd& m) { for (auto& edge : _edges) { edge.first = edge.first * m; edge.second = edge.second * m; } }
void clip(const osg::Plane& plane) { Vertices intersections;
// OSG_NOTICE<<"clip("<<plane<<") edges.size()="<<_edges.size()<<std::endl; for(auto itr = _edges.begin(); itr != _edges.end();) { double d0 = plane.distance(itr->first); double d1 = plane.distance(itr->second); if (d0<0.0 && d1<0.0) { // OSG_NOTICE<<" Edge completely outside, removing"<<std::endl; itr = _edges.erase(itr); } else if (d0>=0.0 && d1>=0.0) { // OSG_NOTICE<<" Edge completely inside"<<std::endl; ++itr; } else { osg::Vec3d& v0 = itr->first; osg::Vec3d& v1 = itr->second; osg::Vec3d intersection = v0 - (v1-v0)*(d0/(d1-d0)); intersections.push_back(intersection); // OSG_NOTICE<<" Edge across clip plane, v0="<<v0<<", v1="<<v1<<", intersection= "<<intersection<<std::endl; if (d0<0.0) { // move first vertex on edge v0 = intersection; } else { // move second vertex on edge v1 = intersection; }
++itr; } } // OSG_NOTICE<<"After clipping, have "<<intersections.size()<<" to insert"<<std::endl;
if (intersections.size() < 2) { return; }
if (intersections.size() == 2) { _edges.emplace_back(intersections[0], intersections[1]); return; }
if (intersections.size() == 3) { _edges.emplace_back(intersections[0], intersections[1]); _edges.emplace_back(intersections[1], intersections[2]); _edges.emplace_back(intersections[2], intersections[0]); return; }
// more than 3 intersections so have to sort them in clockwise order so that // we can generate the edges correctly.
osg::Vec3d normal(plane.getNormal());
osg::Vec3d side_x = osg::Vec3d(1.0,0.0,0.0) ^ normal; osg::Vec3d side_y = osg::Vec3d(0.0,1.0,0.0) ^ normal; osg::Vec3d side = (side_x.length2()>=side_y.length2()) ? side_x : side_y; side.normalize();
osg::Vec3d up = side ^ normal; up.normalize();
osg::Vec3d center; for(auto& vertex : intersections) { center += vertex;
center.x() = osg::maximum(center.x(), -dbl_max); center.y() = osg::maximum(center.y(), -dbl_max); center.z() = osg::maximum(center.z(), -dbl_max);
center.x() = osg::minimum(center.x(), dbl_max); center.y() = osg::minimum(center.y(), dbl_max); center.z() = osg::minimum(center.z(), dbl_max); }
center /= double(intersections.size());
typedef std::map<double, std::list<std::pair<osg::Vec3d, double>>> VertexMap; VertexMap vertexMap; for (const auto& vertex : intersections) { osg::Vec3d dv = vertex - center; double h = dv * side; double v = dv * up; double angle = atan2(h,v); // OSG_NOTICE<<"angle = "<<osg::RadiansToDegrees(angle)<<", h="<<h<<" v= "<<v<<std::endl;
// We need to make sure all intersections are added to the list in the right order, even if they're so far away that their angles work out the same. double sortValue; if (angle < osg::DegreesToRadians(-135.0) || angle > osg::DegreesToRadians(135.0)) sortValue = -h; else if (angle < osg::DegreesToRadians(-45.0)) sortValue = v; else if (angle < osg::DegreesToRadians(45.0)) sortValue = h; else sortValue = -v; if (vertexMap.count(angle)) { auto listItr = vertexMap[angle].begin(); while (listItr != vertexMap[angle].end() && listItr->second < sortValue) ++listItr; vertexMap[angle].emplace(listItr, std::make_pair(vertex, sortValue)); } else vertexMap[angle].emplace_back(vertex, sortValue); }
osg::Vec3d previous_v = vertexMap.rbegin()->second.back().first; for (auto itr = vertexMap.begin(); itr != vertexMap.end(); ++itr) { for (const auto& vertex : itr->second) { _edges.emplace_back(previous_v, vertex.first); previous_v = vertex.first; } }
// OSG_NOTICE<<" after clip("<<plane<<") edges.size()="<<_edges.size()<<std::endl; }
void clip(const osg::Polytope& polytope) { const osg::Polytope::PlaneList& planes = polytope.getPlaneList(); for(const auto& plane : planes) { clip(plane); } }
double min(unsigned int index) const { double m = dbl_max; for(const auto& edge : _edges) { if (edge.first[index] < m) m = edge.first[index]; if (edge.second[index] < m) m = edge.second[index]; } return m; }
double max(unsigned int index) const { double m = -dbl_max; for (const auto& edge : _edges) { if (edge.first[index] > m) m = edge.first[index]; if (edge.second[index] > m) m = edge.second[index]; } return m; }
double minRatio(const osg::Vec3d& eye, unsigned int index) const { double m = dbl_max; osg::Vec3d delta; double ratio; for (const auto& edge : _edges) { delta = edge.first - eye; ratio = delta[index] / delta[1]; if (ratio < m) m = ratio;
delta = edge.second - eye; ratio = delta[index] / delta[1]; if (ratio < m) m = ratio; } return m; }
double maxRatio(const osg::Vec3d& eye, unsigned int index) const { double m = -dbl_max; osg::Vec3d delta; double ratio; for (const auto& edge : _edges) { delta = edge.first - eye; ratio = delta[index] / delta[1]; if (ratio > m) m = ratio;
delta = edge.second - eye; ratio = delta[index] / delta[1]; if (ratio > m) m = ratio; } return m; }
void output(std::ostream& out) { out << "ConvexHull" << std::endl; for (const auto& edge : _edges) { out << " edge (" << edge.first << ") (" << edge.second << ")" << std::endl; } }};
struct RenderLeafBounds{ RenderLeafBounds(): computeRatios(false), numRenderLeaf(0), n(0.0), previous_modelview(0), clip_min_x(-1.0), clip_max_x(1.0), clip_min_y(-1.0), clip_max_y(1.0), clip_min_z(-1.0), clip_max_z(1.0), clip_min_x_ratio(-dbl_max), clip_max_x_ratio(dbl_max), clip_min_z_ratio(-dbl_max), clip_max_z_ratio(dbl_max), min_x_ratio(dbl_max), max_x_ratio(-dbl_max), min_z_ratio(dbl_max), max_z_ratio(-dbl_max), min_x(1.0), max_x(-1.0), min_y(1.0), max_y(-1.0), min_z(1.0), max_z(-1.0) { //OSG_NOTICE<<std::endl<<"RenderLeafBounds"<<std::endl; }
void set(const osg::Matrixd& p) { computeRatios = false; light_p = p; clip_min_x = -dbl_max; clip_max_x = dbl_max; clip_min_y = -dbl_max; clip_max_y = dbl_max; clip_min_z = -dbl_max; clip_max_z = dbl_max; min_x = dbl_max; max_x = -dbl_max; min_y = dbl_max; max_y = -dbl_max; min_z = dbl_max; max_z = -dbl_max; }
void set(const osg::Matrixd& p, osg::Vec3d& e_ls, double nr) { computeRatios = true; light_p = p; eye_ls = e_ls; n = nr; }
void operator() (const osgUtil::RenderLeaf* renderLeaf) { ++numRenderLeaf;
if (renderLeaf->_modelview.get()!=previous_modelview) { previous_modelview = renderLeaf->_modelview.get(); if (previous_modelview) { light_mvp.mult(*renderLeaf->_modelview, light_p); } else { // no modelview matrix (such as when LightPointNode is in the scene graph) so assume // that modelview matrix is indentity. light_mvp = light_p; } // OSG_INFO<<"Computing new light_mvp "<<light_mvp<<std::endl; } else { // OSG_INFO<<"Reusing light_mvp "<<light_mvp<<std::endl; }
const osg::BoundingBox& bb = renderLeaf->_drawable->getBoundingBox(); if (bb.valid()) { // OSG_NOTICE<<"checked extents of "<<renderLeaf->_drawable->getName()<<std::endl; handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMin())); handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMin())); handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMin())); handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMin())); handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMax())); handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMax())); handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMax())); handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMax())); } else { OSG_INFO<<"bb invalid"<<std::endl; } }
void handle(const osg::Vec3d& v) { osg::Vec3d ls = v * light_mvp;
// OSG_NOTICE<<" corner v="<<v<<", ls="<<ls<<std::endl;
if (computeRatios) { osg::Vec3d delta = ls-eye_ls;
double x_ratio, z_ratio; if (delta.y()>n) { x_ratio = delta.x()/delta.y(); z_ratio = delta.z()/delta.y(); } else { x_ratio = delta.x()/n; z_ratio = delta.z()/n; }
if (x_ratio<min_x_ratio) min_x_ratio = x_ratio; if (x_ratio>max_x_ratio) max_x_ratio = x_ratio; if (z_ratio<min_z_ratio) min_z_ratio = z_ratio; if (z_ratio>max_z_ratio) max_z_ratio = z_ratio; }
// clip to the light space if (ls.x()<clip_min_x) ls.x()=clip_min_x; if (ls.x()>clip_max_x) ls.x()=clip_max_x; if (ls.y()<clip_min_y) ls.y()=clip_min_y; if (ls.y()>clip_max_y) ls.y()=clip_max_y; if (ls.z()<clip_min_z) ls.z()=clip_min_z; if (ls.z()>clip_max_z) ls.z()=clip_max_z;
// compute the xyz range. if (ls.x()<min_x) min_x=ls.x(); if (ls.x()>max_x) max_x=ls.x(); if (ls.y()<min_y) min_y=ls.y(); if (ls.y()>max_y) max_y=ls.y(); if (ls.z()<min_z) { min_z=ls.z(); /* OSG_NOTICE<<" - ";*/ } if (ls.z()>max_z) { max_z=ls.z(); /* OSG_NOTICE<<" + ";*/ }
// OSG_NOTICE<<" bb.z() in ls = "<<ls.z()<<std::endl;
}
bool computeRatios;
unsigned int numRenderLeaf;
osg::Matrixd light_p; osg::Vec3d eye_ls; double n;
osg::Matrixd light_mvp; osg::RefMatrix* previous_modelview;
double clip_min_x, clip_max_x; double clip_min_y, clip_max_y; double clip_min_z, clip_max_z;
double clip_min_x_ratio, clip_max_x_ratio; double clip_min_z_ratio, clip_max_z_ratio;
double min_x_ratio, max_x_ratio; double min_z_ratio, max_z_ratio; double min_x, max_x; double min_y, max_y; double min_z, max_z;};
bool MWShadowTechnique::cropShadowCameraToMainFrustum(Frustum& frustum, osg::Camera* camera, double viewNear, double viewFar, std::vector<osg::Plane>& planeList){ osg::Matrixd light_p = camera->getProjectionMatrix(); osg::Matrixd light_v = camera->getViewMatrix(); osg::Matrixd light_vp = light_v * light_p; osg::Matrixd oldLightP = light_p; ConvexHull convexHull; convexHull.setToFrustum(frustum);
osg::Vec3d nearPoint = frustum.eye + frustum.frustumCenterLine * viewNear; osg::Vec3d farPoint = frustum.eye + frustum.frustumCenterLine * viewFar;
double nearDist = -frustum.frustumCenterLine * nearPoint; double farDist = frustum.frustumCenterLine * farPoint;
convexHull.clip(osg::Plane(frustum.frustumCenterLine, nearDist)); convexHull.clip(osg::Plane(-frustum.frustumCenterLine, farDist));
convexHull.transform(light_vp);
double xMin = -1.0, xMax = 1.0; double yMin = -1.0, yMax = 1.0; double zMin = -1.0, zMax = 1.0;
if (convexHull.valid()) { xMin = osg::maximum(-1.0, convexHull.min(0)); xMax = osg::minimum(1.0, convexHull.max(0)); yMin = osg::maximum(-1.0, convexHull.min(1)); yMax = osg::minimum(1.0, convexHull.max(1)); zMin = osg::maximum(-1.0, convexHull.min(2)); zMax = osg::minimum(1.0, convexHull.max(2)); } else return false;
if (xMin != -1.0 || yMin != -1.0 || zMin != -1.0 || xMax != 1.0 || yMax != 1.0 || zMax != 1.0) { osg::Matrix m; m.makeTranslate(osg::Vec3d(-0.5*(xMax + xMin), -0.5*(yMax + yMin), -0.5*(zMax + zMin)));
m.postMultScale(osg::Vec3d(2.0 / (xMax - xMin), 2.0 / (yMax - yMin), 2.0 / (zMax - zMin)));
light_p.postMult(m); camera->setProjectionMatrix(light_p);
convexHull.transform(osg::Matrixd::inverse(oldLightP));
xMin = convexHull.min(0); xMax = convexHull.max(0); yMin = convexHull.min(1); yMax = convexHull.max(1); zMin = convexHull.min(2);
planeList.emplace_back(0.0, -1.0, 0.0, yMax); planeList.emplace_back(0.0, 1.0, 0.0, -yMin); planeList.emplace_back(-1.0, 0.0, 0.0, xMax); planeList.emplace_back(1.0, 0.0, 0.0, -xMin); // In view space, the light is at the most positive value, and we want to cull stuff beyond the minimum value. planeList.emplace_back(0.0, 0.0, 1.0, -zMin); // Don't add a zMax culling plane - we still want those objects, but don't care about their depth buffer value. }
return true;}
bool MWShadowTechnique::adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& /*positionedLight*/, osg::Camera* camera, double viewNear, double viewFar){ const ShadowSettings* settings = getShadowedScene()->getShadowSettings();
//frustum.projectionMatrix; //frustum.modelViewMatrix;
osg::Matrixd light_p = camera->getProjectionMatrix(); osg::Matrixd light_v = camera->getViewMatrix(); osg::Matrixd light_vp = light_v * light_p; osg::Vec3d lightdir(0.0,0.0,-1.0);
// check whether this light space projection is perspective or orthographic. bool orthographicLightSpaceProjection = light_p(0,3)==0.0 && light_p(1,3)==0.0 && light_p(2,3)==0.0;
if (!orthographicLightSpaceProjection) { OSG_INFO<<"perspective light space projection not yet supported."<<std::endl; return false; }
//OSG_NOTICE<<"light_v="<<light_v<<std::endl; //OSG_NOTICE<<"light_p="<<light_p<<std::endl;
ConvexHull convexHull; convexHull.setToFrustum(frustum);
osg::Vec3d nearPoint = frustum.eye + frustum.frustumCenterLine * viewNear; osg::Vec3d farPoint = frustum.eye + frustum.frustumCenterLine * viewFar;
double nearDist = -frustum.frustumCenterLine * nearPoint; double farDist = frustum.frustumCenterLine * farPoint;
convexHull.clip(osg::Plane(frustum.frustumCenterLine, nearDist)); convexHull.clip(osg::Plane(-frustum.frustumCenterLine, farDist));
#if 0 OSG_NOTICE<<"ws ConvexHull xMin="<<convexHull.min(0)<<", xMax="<<convexHull.max(0)<<std::endl; OSG_NOTICE<<"ws ConvexHull yMin="<<convexHull.min(1)<<", yMax="<<convexHull.max(1)<<std::endl; OSG_NOTICE<<"ws ConvexHull zMin="<<convexHull.min(2)<<", zMax="<<convexHull.max(2)<<std::endl;
convexHull.output(osg::notify(osg::NOTICE));#endif
convexHull.transform(light_vp);
ConvexHull convexHullUnextended = convexHull;
convexHull.extendTowardsNegativeZ();
{ // extendTowardsNegativeZ is too numerically unstable to do this first convexHull.clip(osg::Plane(-1.0,0.0,0.0,1.0)); convexHull.clip(osg::Plane(1.0,0.0,0.0,1.0)); convexHull.clip(osg::Plane(0.0,-1.0,0.0,1.0)); convexHull.clip(osg::Plane(0.0,1.0,0.0,1.0)); convexHullUnextended.clip(osg::Plane(-1.0,0.0,0.0,1.0)); convexHullUnextended.clip(osg::Plane(1.0,0.0,0.0,1.0)); convexHullUnextended.clip(osg::Plane(0.0,-1.0,0.0,1.0)); convexHullUnextended.clip(osg::Plane(0.0,1.0,0.0,1.0)); }
#if 0 convexHull.output(osg::notify(osg::NOTICE));
OSG_NOTICE<<"ls ConvexHull xMin="<<convexHull.min(0)<<", xMax="<<convexHull.max(0)<<std::endl; OSG_NOTICE<<"ls ConvexHull yMin="<<convexHull.min(1)<<", yMax="<<convexHull.max(1)<<std::endl; OSG_NOTICE<<"ls ConvexHull zMin="<<convexHull.min(2)<<", zMax="<<convexHull.max(2)<<std::endl;#endif
#if 0 // only applicable when the light space contains the whole model contained in the view frustum. { convexHull.clip(osg::Plane(0.0,0.0,1,1.0)); // clip by near plane of light space. convexHull.clip(osg::Plane(0.0,0.0,-1,1.0)); // clip by far plane of light space. }#endif
#if 0 if (renderStage) {#if 1 osg::ElapsedTime timer;#endif
RenderLeafTraverser<RenderLeafBounds> rli; rli.set(light_p); rli.traverse(renderStage);
if (rli.numRenderLeaf==0) { return false; }#if 0 OSG_NOTICE<<"New Time for RenderLeafTraverser "<<timer.elapsedTime_m()<<"ms, number of render leaves "<<rli.numRenderLeaf<<std::endl; OSG_NOTICE<<" scene bounds min_x="<<rli.min_x<<", max_x="<<rli.max_x<<std::endl; OSG_NOTICE<<" scene bounds min_y="<<rli.min_y<<", max_y="<<rli.max_y<<std::endl; OSG_NOTICE<<" scene bounds min_z="<<rli.min_z<<", max_z="<<rli.max_z<<std::endl;#endif
#if 0 double widest_x = osg::maximum(fabs(rli.min_x), fabs(rli.max_x)); double widest_y = osg::maximum(fabs(rli.min_y), fabs(rli.max_y)); double widest_z = osg::maximum(fabs(rli.min_z), fabs(rli.max_z));#endif
#if 1#if 1 convexHull.clip(osg::Plane(1.0,0.0,0.0,-rli.min_x)); convexHull.clip(osg::Plane(-1.0,0.0,0.0,rli.max_x));
convexHullUnextended.clip(osg::Plane(1.0, 0.0, 0.0, -rli.min_x)); convexHullUnextended.clip(osg::Plane(-1.0, 0.0, 0.0, rli.max_x));#else convexHull.clip(osg::Plane(1.0,0.0,0.0,widest_x)); convexHull.clip(osg::Plane(-1.0,0.0,0.0,widest_x));#endif#if 1 convexHull.clip(osg::Plane(0.0,1.0,0.0,-rli.min_y)); convexHull.clip(osg::Plane(0.0,-1.0,0.0,rli.max_y));
convexHullUnextended.clip(osg::Plane(0.0, 1.0, 0.0, -rli.min_y)); convexHullUnextended.clip(osg::Plane(0.0, -1.0, 0.0, rli.max_y));#endif#endif
#if 1 convexHull.clip(osg::Plane(0.0,0.0,1.0,-rli.min_z)); convexHull.clip(osg::Plane(0.0,0.0,-1.0,rli.max_z));
convexHullUnextended.clip(osg::Plane(0.0, 0.0, 1.0, -rli.min_z)); convexHullUnextended.clip(osg::Plane(0.0, 0.0, -1.0, rli.max_z));#elif 0 convexHull.clip(osg::Plane(0.0,0.0,1.0,1.0)); convexHull.clip(osg::Plane(0.0,0.0,-1.0,1.0));#endif
#if 0 OSG_NOTICE<<"widest_x = "<<widest_x<<std::endl; OSG_NOTICE<<"widest_y = "<<widest_y<<std::endl; OSG_NOTICE<<"widest_z = "<<widest_z<<std::endl;#endif }#endif
#if 0 convexHull.output(osg::notify(osg::NOTICE));
OSG_NOTICE<<"after clipped ls ConvexHull xMin="<<convexHull.min(0)<<", xMax="<<convexHull.max(0)<<std::endl; OSG_NOTICE<<"after clipped ls ConvexHull yMin="<<convexHull.min(1)<<", yMax="<<convexHull.max(1)<<std::endl; OSG_NOTICE<<"after clipped ls ConvexHull zMin="<<convexHull.min(2)<<", zMax="<<convexHull.max(2)<<std::endl;#endif
double xMin=-1.0, xMax=1.0; double yMin=-1.0, yMax=1.0; double zMin=-1.0, zMax=1.0;
if (convexHull.valid()) { double widest_x = osg::maximum(fabs(convexHull.min(0)), fabs(convexHull.max(0))); xMin = osg::maximum(-1.0,-widest_x); xMax = osg::minimum(1.0,widest_x); yMin = osg::maximum(-1.0,convexHull.min(1)); yMax = osg::minimum(1.0,convexHull.max(1)); } else { // clipping of convex hull has invalidated it, so reset it so later checks on it provide valid results. convexHull.setToFrustum(frustum); convexHull.transform(light_vp); }
#if 0 OSG_NOTICE<<"xMin = "<<xMin<<", \txMax = "<<xMax<<std::endl; OSG_NOTICE<<"yMin = "<<yMin<<", \tyMax = "<<yMax<<std::endl; OSG_NOTICE<<"zMin = "<<zMin<<", \tzMax = "<<zMax<<std::endl;#endif
#if 1 // we always want the lightspace to include the computed near plane. zMin = -1.0; if (xMin!=-1.0 || yMin!=-1.0 || zMin!=-1.0 || xMax!=1.0 || yMax!=1.0 || zMax!=1.0) { osg::Matrix m; m.makeTranslate(osg::Vec3d(-0.5*(xMax+xMin), -0.5*(yMax+yMin), -0.5*(zMax+zMin)));
m.postMultScale(osg::Vec3d(2.0/(xMax-xMin), 2.0/(yMax-yMin), 2.0/(zMax-zMin)));
convexHull.transform(m); convexHullUnextended.transform(m); light_p.postMult(m); light_vp = light_v * light_p;
#if 0 OSG_NOTICE<<"Adjusting projection matrix "<<m<<std::endl; convexHull.output(osg::notify(osg::NOTICE));#endif camera->setProjectionMatrix(light_p); }
#endif
osg::Vec3d eye_v = frustum.eye * light_v; //osg::Vec3d centerNearPlane_v = frustum.centerNearPlane * light_v; osg::Vec3d center_v = frustum.center * light_v; osg::Vec3d viewdir_v = center_v-eye_v; viewdir_v.normalize();
double dotProduct_v = lightdir * viewdir_v; double gamma_v = acos(dotProduct_v); if (gamma_v<osg::DegreesToRadians(settings->getPerspectiveShadowMapCutOffAngle()) || gamma_v>osg::DegreesToRadians(180-settings->getPerspectiveShadowMapCutOffAngle())) { // OSG_NOTICE<<"Light and view vectors near parallel - use standard shadow map."<<std::endl; return true; }
//OSG_NOTICE<<"gamma="<<osg::RadiansToDegrees(gamma_v)<<std::endl; //OSG_NOTICE<<"eye_v="<<eye_v<<std::endl; //OSG_NOTICE<<"viewdir_v="<<viewdir_v<<std::endl;
osg::Vec3d eye_ls = frustum.eye * light_vp;#if 0 if (eye_ls.y()>-1.0) { OSG_NOTICE<<"Eye point within light space - use standard shadow map."<<std::endl; return true; }#endif
//osg::Vec3d centerNearPlane_ls = frustum.centerNearPlane * light_vp; //osg::Vec3d centerFarPlane_ls = frustum.centerFarPlane * light_vp; osg::Vec3d center_ls = frustum.center * light_vp; osg::Vec3d viewdir_ls = center_ls-eye_ls; viewdir_ls.normalize();
osg::Vec3d side = lightdir ^ viewdir_ls; side.normalize(); osg::Vec3d up = side ^ lightdir;
double d = 2.0;
double alpha = osg::DegreesToRadians(30.0); double n = tan(alpha)*tan(osg::PI_2-gamma_v)*tan(osg::PI_2-gamma_v); //double n = tan(alpha)*tan(osg::PI_2-gamma_v);
//OSG_NOTICE<<"n = "<<n<<", eye_ls.y()="<<eye_ls.y()<<", eye_v="<<eye_v<<", eye="<<frustum.eye<<std::endl; double min_n = osg::maximum(-1.0-eye_ls.y(), settings->getMinimumShadowMapNearFarRatio()); if (n<min_n) { //OSG_NOTICE<<"Clamping n to eye point"<<std::endl; n=min_n; }
//n = min_n;
//n = 0.01;
//n = z_n;
double f = n+d;
double a = (f+n)/(f-n); double b = -2.0*f*n/(f-n);
osg::Vec3d virtual_eye(0.0,-1.0-n, eye_ls.z());
osg::Matrixd lightView; lightView.makeLookAt(virtual_eye, virtual_eye+lightdir, up);
#if 0 OSG_NOTICE<<"n = "<<n<<", f="<<f<<std::endl; OSG_NOTICE<<"eye_ls = "<<eye_ls<<", virtual_eye="<<virtual_eye<<std::endl; OSG_NOTICE<<"frustum.eyes="<<frustum.eye<<std::endl;#endif
double min_x_ratio = 0.0; double max_x_ratio = 0.0; double min_z_ratio = dbl_max; double max_z_ratio = -dbl_max;
min_x_ratio = convexHull.valid() ? convexHull.minRatio(virtual_eye,0) : -dbl_max; max_x_ratio = convexHull.valid() ? convexHull.maxRatio(virtual_eye,0) : dbl_max; //min_z_ratio = convexHull.minRatio(virtual_eye,2); //max_z_ratio = convexHull.maxRatio(virtual_eye,2);
if (convexHullUnextended.valid()) { min_z_ratio = convexHullUnextended.minRatio(virtual_eye, 2); max_z_ratio = convexHullUnextended.maxRatio(virtual_eye, 2); }
#if 0 OSG_NOTICE<<"convexHull min_x_ratio = "<<min_x_ratio<<std::endl; OSG_NOTICE<<"convexHull max_x_ratio = "<<max_x_ratio<<std::endl; OSG_NOTICE<<"convexHull min_z_ratio = "<<min_z_ratio<<std::endl; OSG_NOTICE<<"convexHull max_z_ratio = "<<max_z_ratio<<std::endl;#endif
#if 0 if (renderStage) {#if 1 osg::ElapsedTime timer;#endif
RenderLeafTraverser<RenderLeafBounds> rli; rli.set(light_p, virtual_eye, n); rli.traverse(renderStage);
if (rli.numRenderLeaf==0) { return false; }
#if 0 OSG_NOTICE<<"Time for RenderLeafTraverser "<<timer.elapsedTime_m()<<"ms, number of render leaves "<<rli.numRenderLeaf<<std::endl; OSG_NOTICE<<"scene bounds min_x="<<rli.min_x<<", max_x="<<rli.max_x<<std::endl; OSG_NOTICE<<"scene bounds min_y="<<rli.min_y<<", max_y="<<rli.max_y<<std::endl; OSG_NOTICE<<"scene bounds min_z="<<rli.min_z<<", max_z="<<rli.max_z<<std::endl; OSG_NOTICE<<"min_x_ratio="<<rli.min_x_ratio<<", max_x_ratio="<<rli.max_x_ratio<<std::endl; OSG_NOTICE<<"min_z_ratio="<<rli.min_z_ratio<<", max_z_ratio="<<rli.max_z_ratio<<std::endl;#endif if (rli.min_x_ratio>min_x_ratio) min_x_ratio = rli.min_x_ratio; if (rli.max_x_ratio<max_x_ratio) max_x_ratio = rli.max_x_ratio;
if (min_z_ratio == dbl_max || rli.min_z_ratio > min_z_ratio) min_z_ratio = rli.min_z_ratio; if (max_z_ratio == -dbl_max || rli.max_z_ratio < max_z_ratio) max_z_ratio = rli.max_z_ratio; }#endif double best_x_ratio = osg::maximum(fabs(min_x_ratio),fabs(max_x_ratio)); double best_z_ratio = osg::maximum(fabs(min_z_ratio),fabs(max_z_ratio));
//best_z_ratio = osg::maximum(1.0, best_z_ratio);#if 0 OSG_NOTICE<<"min_x_ratio = "<<min_x_ratio<<std::endl; OSG_NOTICE<<"max_x_ratio = "<<max_x_ratio<<std::endl; OSG_NOTICE<<"best_x_ratio = "<<best_x_ratio<<std::endl; OSG_NOTICE<<"min_z_ratio = "<<min_z_ratio<<std::endl; OSG_NOTICE<<"max_z_ratio = "<<max_z_ratio<<std::endl; OSG_NOTICE<<"best_z_ratio = "<<best_z_ratio<<std::endl;#endif
//best_z_ratio *= 10.0;
osg::Matrixd lightPerspective( 1.0/best_x_ratio, 0.0, 0.0, 0.0, 0.0, a, 0.0, 1.0, 0.0, 0.0, 1.0/best_z_ratio, 0.0, 0.0, b, 0.0, 0.0 ); osg::Matrixd light_persp = light_p * lightView * lightPerspective;
if (convexHull.valid()) { convexHull.transform(lightView * lightPerspective); xMin = osg::maximum(-1.0, convexHull.min(0)); xMax = osg::minimum(1.0, convexHull.max(0)); yMin = osg::maximum(-1.0, convexHull.min(1)); yMax = osg::minimum(1.0, convexHull.max(1));
if (xMin != -1.0 || yMin != -1.0 || xMax != 1.0 || yMax != 1.0) { osg::Matrix m; m.makeTranslate(osg::Vec3d(-0.5*(xMax+xMin), -0.5*(yMax+yMin), 0.0));
m.postMultScale(osg::Vec3d(2.0/(xMax-xMin), 2.0/(yMax-yMin), 1.0));
convexHull.transform(m); convexHullUnextended.transform(m); light_persp.postMult(m); } }
#if 0 OSG_NOTICE<<"light_p = "<<light_p<<std::endl; OSG_NOTICE<<"lightView = "<<lightView<<std::endl; OSG_NOTICE<<"lightPerspective = "<<lightPerspective<<std::endl; OSG_NOTICE<<"light_persp result = "<<light_persp<<std::endl;#endif camera->setProjectionMatrix(light_persp);
return true;}
void MWShadowTechnique::assignShadowStateSettings(osgUtil::CullVisitor& cv, osg::Camera* camera, unsigned int sm_i, Uniforms& uniforms){ osg::Matrix inverseViewMatrix = osg::Matrix::inverse(*cv.getModelViewMatrix()); osg::Matrix shadowSpaceMatrix = inverseViewMatrix * camera->getViewMatrix() * camera->getProjectionMatrix() * osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5);
std::string shadowSpaceUniformName = "shadowSpaceMatrix" + std::to_string(sm_i); osg::ref_ptr<osg::Uniform> shadowSpaceUniform;
for (const auto & uniform : uniforms) { if (uniform->getName() == shadowSpaceUniformName) { shadowSpaceUniform = uniform; break; } }
if (!shadowSpaceUniform) { shadowSpaceUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, shadowSpaceUniformName); uniforms.push_back(shadowSpaceUniform); }
shadowSpaceUniform->set(shadowSpaceMatrix);}
void SceneUtil::MWShadowTechnique::assignValidRegionSettings(osgUtil::CullVisitor & cv, osg::Camera* camera, unsigned int sm_i, Uniforms & uniforms){ osg::Matrix validRegionMatrix = osg::Matrix::inverse(*cv.getModelViewMatrix()) * camera->getViewMatrix() * camera->getProjectionMatrix();
std::string validRegionUniformName = "validRegionMatrix" + std::to_string(sm_i); osg::ref_ptr<osg::Uniform> validRegionUniform;
for (const auto & uniform : uniforms) { if (uniform->getName() == validRegionUniformName) { validRegionUniform = uniform; break; } }
if (!validRegionUniform) { validRegionUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, validRegionUniformName); uniforms.push_back(validRegionUniform); }
validRegionUniform->set(validRegionMatrix);}
void SceneUtil::MWShadowTechnique::assignCascadeSplits(const osg::Vec4& splits, Uniforms& uniforms){ static const std::string cascadeSplitsUniformName = "cascadeSplits"; osg::ref_ptr<osg::Uniform> cascadeSplitsUniform;
for (const auto& uniform : uniforms) { if (uniform->getName() == cascadeSplitsUniformName) { cascadeSplitsUniform = uniform; break; } }
if (!cascadeSplitsUniform) { cascadeSplitsUniform = new osg::Uniform(osg::Uniform::FLOAT_VEC4, cascadeSplitsUniformName); uniforms.push_back(cascadeSplitsUniform); }
cascadeSplitsUniform->set(splits);}
void MWShadowTechnique::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const{ OSG_INFO<<"cullShadowReceivingScene()"<<std::endl;
// record the traversal mask on entry so we can reapply it later. unsigned int traversalMask = cv->getTraversalMask();
cv->setTraversalMask( traversalMask & _shadowedScene->getShadowSettings()->getReceivesShadowTraversalMask() );
_shadowedScene->osg::Group::traverse(*cv);
cv->setTraversalMask( traversalMask );
return;}
void MWShadowTechnique::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const{ OSG_INFO<<"cullShadowCastingScene()"<<std::endl;
// record the traversal mask on entry so we can reapply it later. unsigned int traversalMask = cv->getTraversalMask();
cv->setTraversalMask( traversalMask & _shadowedScene->getShadowSettings()->getCastsShadowTraversalMask() );
if (camera) camera->accept(*cv);
cv->setTraversalMask( traversalMask );
return;}
osg::StateSet* MWShadowTechnique::prepareStateSetForRenderingShadow(ViewDependentData& vdd, unsigned int traversalNumber) const{ OSG_INFO<<" prepareStateSetForRenderingShadow() "<<vdd.getStateSet(traversalNumber)<<std::endl;
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet(traversalNumber);
stateset->clear();
stateset->setTextureAttribute(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
for(const auto& uniform : _uniforms[traversalNumber % 2]) { OSG_INFO<<"addUniform("<<uniform->getName()<<")"<<std::endl; stateset->addUniform(uniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); }
for(const auto& uniform : vdd._uniforms[traversalNumber % 2]) { OSG_INFO<<"addUniform("<<uniform->getName()<<")"<<std::endl; stateset->addUniform(uniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); }
if (_program.valid()) { stateset->setAttribute(_program.get()); }
LightDataList& pll = vdd.getLightDataList(); for(LightDataList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures
LightData& pl = (**itr);
// if no texture units have been activated for this light then no shadow state required. if (pl.textureUnits.empty()) continue;
for(LightData::ActiveTextureUnits::iterator atu_itr = pl.textureUnits.begin(); atu_itr != pl.textureUnits.end(); ++atu_itr) { OSG_INFO<<" Need to assign state for "<<*atu_itr<<std::endl; }
}
unsigned int shadowMapModeValue = osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE;
ShadowDataList& sdl = vdd.getShadowDataList(); for(ShadowDataList::iterator itr = sdl.begin(); itr != sdl.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures
ShadowData& sd = (**itr);
OSG_INFO<<" ShadowData for "<<sd._textureUnit<<std::endl;
stateset->setTextureAttribute(sd._textureUnit, sd._texture.get(), shadowMapModeValue); }
return stateset;}
void MWShadowTechnique::resizeGLObjectBuffers(unsigned int /*maxSize*/){ // the way that ViewDependentData is mapped shouldn't}
void MWShadowTechnique::releaseGLObjects(osg::State* state) const{ std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex); for(ViewDependentDataMap::const_iterator itr = _viewDependentDataMap.begin(); itr != _viewDependentDataMap.end(); ++itr) { ViewDependentData* vdd = itr->second.get(); if (vdd) { vdd->releaseGLObjects(state); } } if (_debugHud) _debugHud->releaseGLObjects(state);}
class DoubleBufferCallback : public osg::Callback{public: DoubleBufferCallback(osg::NodeList &children) : mChildren(children) {}
bool run(osg::Object* node, osg::Object* visitor) override { // We can't use a static cast as NodeVisitor virtually inherits from Object osg::ref_ptr<osg::NodeVisitor> nodeVisitor = visitor->asNodeVisitor(); unsigned int traversalNumber = nodeVisitor->getTraversalNumber(); mChildren[traversalNumber % 2]->accept(*nodeVisitor);
return true; }
protected: osg::NodeList mChildren;};
SceneUtil::MWShadowTechnique::DebugHUD::DebugHUD(int numberOfShadowMapsPerLight) : mDebugProgram(new osg::Program){ osg::ref_ptr<osg::Shader> vertexShader = new osg::Shader(osg::Shader::VERTEX, debugVertexShaderSource); mDebugProgram->addShader(vertexShader); osg::ref_ptr<osg::Shader> fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFragmentShaderSource); mDebugProgram->addShader(fragmentShader);
osg::ref_ptr<osg::Program> frustumProgram = new osg::Program; vertexShader = new osg::Shader(osg::Shader::VERTEX, debugFrustumVertexShaderSource); frustumProgram->addShader(vertexShader); fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFrustumFragmentShaderSource); frustumProgram->addShader(fragmentShader);
for (auto& frustumGeometry : mFrustumGeometries) { frustumGeometry = new osg::Geometry(); frustumGeometry->setCullingActive(false);
frustumGeometry->getOrCreateStateSet()->setAttributeAndModes(frustumProgram, osg::StateAttribute::ON); }
osg::ref_ptr<osg::DrawElementsUShort> frustumDrawElements = new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP); for (auto & geom : mFrustumGeometries) geom->addPrimitiveSet(frustumDrawElements); frustumDrawElements->push_back(0); frustumDrawElements->push_back(1); frustumDrawElements->push_back(2); frustumDrawElements->push_back(3); frustumDrawElements->push_back(0); frustumDrawElements->push_back(4); frustumDrawElements->push_back(5); frustumDrawElements->push_back(6); frustumDrawElements->push_back(7); frustumDrawElements->push_back(4);
frustumDrawElements = new osg::DrawElementsUShort(osg::PrimitiveSet::LINES); for (auto & geom : mFrustumGeometries) geom->addPrimitiveSet(frustumDrawElements); frustumDrawElements->push_back(1); frustumDrawElements->push_back(5); frustumDrawElements->push_back(2); frustumDrawElements->push_back(6); frustumDrawElements->push_back(3); frustumDrawElements->push_back(7);
for (int i = 0; i < numberOfShadowMapsPerLight; ++i) addAnotherShadowMap();}
void SceneUtil::MWShadowTechnique::DebugHUD::draw(osg::ref_ptr<osg::Texture2D> texture, unsigned int shadowMapNumber, const osg::Matrixd &matrix, osgUtil::CullVisitor& cv){ // It might be possible to change shadow settings at runtime if (shadowMapNumber > mDebugCameras.size()) addAnotherShadowMap(); osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet(); stateSet->setTextureAttribute(sDebugTextureUnit, texture, osg::StateAttribute::ON);
auto frustumUniform = mFrustumUniforms[cv.getTraversalNumber() % 2][shadowMapNumber]; frustumUniform->set(matrix); stateSet->addUniform(frustumUniform);
// Some of these calls may be superfluous. unsigned int traversalMask = cv.getTraversalMask(); cv.setTraversalMask(mDebugGeometry[shadowMapNumber]->getNodeMask()); cv.pushStateSet(stateSet); mDebugCameras[shadowMapNumber]->accept(cv); cv.popStateSet(); cv.setTraversalMask(traversalMask);}
void SceneUtil::MWShadowTechnique::DebugHUD::releaseGLObjects(osg::State* state) const{ for (auto const& camera : mDebugCameras) camera->releaseGLObjects(state); mDebugProgram->releaseGLObjects(state); for (auto const& node : mDebugGeometry) node->releaseGLObjects(state); for (auto const& node : mFrustumTransforms) node->releaseGLObjects(state); for (auto const& node : mFrustumGeometries) node->releaseGLObjects(state);}
void SceneUtil::MWShadowTechnique::DebugHUD::setFrustumVertices(osg::ref_ptr<osg::Vec3Array> vertices, unsigned int traversalNumber){ mFrustumGeometries[traversalNumber % 2]->setVertexArray(vertices);}
void SceneUtil::MWShadowTechnique::DebugHUD::addAnotherShadowMap(){ size_t shadowMapNumber = mDebugCameras.size();
mDebugCameras.push_back(new osg::Camera); mDebugCameras[shadowMapNumber]->setViewport(static_cast<int>(200 * shadowMapNumber), 0, 200, 200); mDebugCameras[shadowMapNumber]->setRenderOrder(osg::Camera::POST_RENDER); mDebugCameras[shadowMapNumber]->setClearColor(osg::Vec4(1.0, 1.0, 0.0, 1.0)); mDebugCameras[shadowMapNumber]->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mDebugGeometry.emplace_back(osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0))); mDebugGeometry[shadowMapNumber]->setCullingActive(false); mDebugCameras[shadowMapNumber]->addChild(mDebugGeometry[shadowMapNumber]); osg::ref_ptr<osg::StateSet> stateSet = mDebugGeometry[shadowMapNumber]->getOrCreateStateSet(); stateSet->setAttributeAndModes(mDebugProgram, osg::StateAttribute::ON); osg::ref_ptr<osg::Uniform> textureUniform = new osg::Uniform("texture", sDebugTextureUnit); //textureUniform->setType(osg::Uniform::SAMPLER_2D); stateSet->addUniform(textureUniform.get());
mFrustumTransforms.push_back(new osg::Group); osg::NodeList frustumGeometryNodeList(mFrustumGeometries.cbegin(), mFrustumGeometries.cend()); mFrustumTransforms[shadowMapNumber]->setCullCallback(new DoubleBufferCallback(frustumGeometryNodeList)); mFrustumTransforms[shadowMapNumber]->setCullingActive(false); mDebugCameras[shadowMapNumber]->addChild(mFrustumTransforms[shadowMapNumber]);
for(auto& uniformVector : mFrustumUniforms) uniformVector.push_back(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "transform"));}
osg::ref_ptr<osg::StateSet> SceneUtil::MWShadowTechnique::getOrCreateShadowsBinStateSet(){ if (_shadowsBinStateSet == nullptr) { if (_shadowsBin == nullptr) { _shadowsBin = new ShadowsBin(_castingPrograms); osgUtil::RenderBin::addRenderBinPrototype(_shadowsBinName, _shadowsBin); } _shadowsBinStateSet = new osg::StateSet; _shadowsBinStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, _shadowsBinName, osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); } return _shadowsBinStateSet;}
// NOLINTEND(readability-identifier-naming)
// clang-format on