mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
new water WIP, caustics, chromatic abberation, accurate fresnel, underwater reflection, etc
This commit is contained in:
parent
4434fb640e
commit
739455e6f8
22 changed files with 556 additions and 98 deletions
|
@ -102,6 +102,7 @@ target_link_libraries(openmw
|
|||
${MYGUI_PLATFORM_LIBRARIES}
|
||||
"shiny"
|
||||
"shiny.OgrePlatform"
|
||||
components
|
||||
)
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
|
|
|
@ -261,7 +261,7 @@ int main(int argc, char**argv)
|
|||
boost::filesystem::current_path(bundlePath);
|
||||
#endif
|
||||
|
||||
try
|
||||
//try
|
||||
{
|
||||
Files::ConfigurationManager cfgMgr;
|
||||
OMW::Engine engine(cfgMgr);
|
||||
|
@ -271,11 +271,11 @@ int main(int argc, char**argv)
|
|||
engine.go();
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
/*catch (std::exception &e)
|
||||
{
|
||||
std::cout << "\nERROR: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,14 +14,14 @@ enum RenderQueueGroups
|
|||
|
||||
RQG_Main = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_Alpha = Ogre::RENDER_QUEUE_MAIN+1,
|
||||
|
||||
RQG_OcclusionQuery = Ogre::RENDER_QUEUE_6,
|
||||
|
||||
RQG_UnderWater = Ogre::RENDER_QUEUE_7,
|
||||
|
||||
RQG_Water = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_Alpha = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_UnderWater = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_OcclusionQuery = Ogre::RENDER_QUEUE_8,
|
||||
|
||||
// Sky late (sun & sun flare)
|
||||
RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE
|
||||
};
|
||||
|
@ -54,7 +54,7 @@ enum VisibilityFlags
|
|||
|
||||
RV_OcclusionQuery = 256,
|
||||
|
||||
RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water,
|
||||
RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water
|
||||
|
||||
/// \todo markers (normally hidden)
|
||||
};
|
||||
|
|
|
@ -264,7 +264,7 @@ void RenderingManager::update (float duration){
|
|||
|
||||
checkUnderwater();
|
||||
|
||||
mWater->update();
|
||||
mWater->update(duration);
|
||||
}
|
||||
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){
|
||||
if(store->cell->data.flags & store->cell->HasWater
|
||||
|
|
|
@ -44,7 +44,6 @@ namespace MWRender
|
|||
mTerrainGlobals->setMaxPixelError(8);
|
||||
|
||||
mTerrainGlobals->setLayerBlendMapSize(32);
|
||||
mTerrainGlobals->setDefaultGlobalColourMapSize(65);
|
||||
|
||||
//10 (default) didn't seem to be quite enough
|
||||
mTerrainGlobals->setSkirtSize(128);
|
||||
|
@ -53,28 +52,6 @@ namespace MWRender
|
|||
//this seemed the distance where it wasn't too noticeable
|
||||
mTerrainGlobals->setCompositeMapDistance(mWorldSize*2);
|
||||
|
||||
/*
|
||||
mActiveProfile->setLightmapEnabled(false);
|
||||
mActiveProfile->setLayerSpecularMappingEnabled(false);
|
||||
mActiveProfile->setLayerNormalMappingEnabled(false);
|
||||
mActiveProfile->setLayerParallaxMappingEnabled(false);
|
||||
|
||||
bool shadows = Settings::Manager::getBool("enabled", "Shadows");
|
||||
mActiveProfile->setReceiveDynamicShadowsEnabled(shadows);
|
||||
mActiveProfile->setReceiveDynamicShadowsDepth(shadows);
|
||||
if (Settings::Manager::getBool("split", "Shadows"))
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(mRendering->getShadows()->getPSSMSetup());
|
||||
else
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(0);
|
||||
|
||||
mActiveProfile->setShadowFar(mRendering->getShadows()->getShadowFar());
|
||||
mActiveProfile->setShadowFadeStart(mRendering->getShadows()->getFadeStart());
|
||||
|
||||
//composite maps lead to a drastic increase in loading time so are
|
||||
//disabled
|
||||
mActiveProfile->setCompositeMapEnabled(false);
|
||||
*/
|
||||
|
||||
mTerrainGroup.setOrigin(Vector3(mWorldSize/2,
|
||||
0,
|
||||
-mWorldSize/2));
|
||||
|
@ -181,10 +158,9 @@ namespace MWRender
|
|||
terrain->setVisibilityFlags(RV_Terrain);
|
||||
terrain->setRenderQueueGroup(RQG_Main);
|
||||
|
||||
// disable or enable global colour map (depends on available vertex colours)
|
||||
if ( land->landData->usingColours )
|
||||
{
|
||||
// disable or enable global colour map (depends on available vertex colours)
|
||||
//mActiveProfile->setGlobalColourMapEnabled(true);
|
||||
TexturePtr vertex = getVertexColours(land,
|
||||
cellX, cellY,
|
||||
x*(mLandSize-1),
|
||||
|
@ -193,17 +169,9 @@ namespace MWRender
|
|||
|
||||
mActiveProfile->setGlobalColourMapEnabled(true);
|
||||
mActiveProfile->setGlobalColourMap (terrain, vertex->getName());
|
||||
|
||||
//this is a hack to get around the fact that Ogre seems to
|
||||
//corrupt the global colour map leading to rendering errors
|
||||
//MaterialPtr mat = terrain->getMaterial();
|
||||
/// \todo
|
||||
//mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
|
||||
|
||||
|
||||
//mat = terrain->_getCompositeMapMaterial();
|
||||
//mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
|
||||
}
|
||||
else
|
||||
mActiveProfile->setGlobalColourMapEnabled (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,10 @@ namespace MWRender
|
|||
shadowTex->setProperty ("content_type", sh::makeProperty<sh::StringValue> (new sh::StringValue("shadow")));
|
||||
}
|
||||
|
||||
// caustics
|
||||
sh::MaterialInstanceTextureUnit* caustics = p->createTextureUnit ("causticMap");
|
||||
caustics->setProperty ("direct_texture", sh::makeProperty<sh::StringValue> (new sh::StringValue("water_nm.png")));
|
||||
|
||||
p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty<sh::StringValue>(new sh::StringValue(
|
||||
Ogre::StringConverter::toString(numBlendTextures + numLayers + 2))));
|
||||
|
||||
|
@ -149,6 +153,8 @@ namespace MWRender
|
|||
--freeTextureUnits;
|
||||
freeTextureUnits -= 3; // shadow PSSM
|
||||
|
||||
--freeTextureUnits; // caustics
|
||||
|
||||
// each layer needs 1.25 units (1xdiffusespec, 0.25xblend)
|
||||
return static_cast<Ogre::uint8>(freeTextureUnits / (1.25f));
|
||||
}
|
||||
|
|
|
@ -8,11 +8,16 @@
|
|||
#include <OgreCompositorInstance.h>
|
||||
#include <OgreCompositorChain.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreOverlayManager.h>
|
||||
#include <OgreOverlayContainer.h>
|
||||
#include <OgreOverlayElement.h>
|
||||
|
||||
#include "sky.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
#include "compositors.hpp"
|
||||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
|
@ -22,10 +27,16 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
|
|||
mCamera (camera), mSceneManager (camera->getSceneManager()),
|
||||
mIsUnderwater(false), mVisibilityFlags(0),
|
||||
mReflectionTarget(0), mActive(1), mToggled(1),
|
||||
mReflectionRenderActive(false), mRendering(rend)
|
||||
mReflectionRenderActive(false), mRendering(rend),
|
||||
mOldFarClip(0),
|
||||
mWaterTimer(0.f)
|
||||
{
|
||||
mSky = rend->getSkyManager();
|
||||
|
||||
sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty<sh::Vector3>(new sh::Vector3(0.5, -0.8, 0.2)));
|
||||
sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(0)));
|
||||
sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty<sh::Vector2>(new sh::Vector2(1, 0.6)));
|
||||
|
||||
mTop = cell->water;
|
||||
|
||||
mIsUnderwater = false;
|
||||
|
@ -40,7 +51,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
|
|||
mWater->setCastShadows(false);
|
||||
|
||||
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
|
||||
mWaterNode->setPosition(0, mTop, 0);
|
||||
|
||||
mReflectionCamera = mSceneManager->createCamera("ReflectionCamera");
|
||||
|
||||
|
@ -59,10 +69,19 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
|
|||
|
||||
mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water");
|
||||
|
||||
Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh");
|
||||
underwaterDome->setRenderQueueGroup (RQG_UnderWater);
|
||||
mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode ();
|
||||
mUnderwaterDome->attachObject (underwaterDome);
|
||||
mUnderwaterDome->setScale(100,100,100);
|
||||
mUnderwaterDome->setVisible(false);
|
||||
|
||||
mSceneManager->addRenderQueueListener(this);
|
||||
|
||||
assignTextures();
|
||||
|
||||
setHeight(mTop);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// ---------------------------------- reflection debug overlay ----------------------------------
|
||||
|
@ -73,7 +92,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
|
|||
OverlayManager& mgr = OverlayManager::getSingleton();
|
||||
Overlay* overlay;
|
||||
// destroy if already exists
|
||||
if (overlay = mgr.getByName("ReflectionDebugOverlay"))
|
||||
if ((overlay = mgr.getByName("ReflectionDebugOverlay")))
|
||||
mgr.destroy(overlay);
|
||||
|
||||
overlay = mgr.create("ReflectionDebugOverlay");
|
||||
|
@ -85,18 +104,17 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
|
|||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
|
||||
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName());
|
||||
|
||||
OverlayContainer* debugPanel;
|
||||
|
||||
// destroy container if exists
|
||||
try
|
||||
{
|
||||
if (debugPanel =
|
||||
if ((debugPanel =
|
||||
static_cast<OverlayContainer*>(
|
||||
mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel"
|
||||
)))
|
||||
))))
|
||||
mgr.destroyOverlayElement(debugPanel);
|
||||
}
|
||||
catch (Ogre::Exception&) {}
|
||||
|
@ -146,6 +164,7 @@ void Water::setHeight(const float height)
|
|||
mTop = height;
|
||||
mWaterPlane = Plane(Vector3::UNIT_Y, height);
|
||||
mWaterNode->setPosition(0, height, 0);
|
||||
sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(height)));
|
||||
}
|
||||
|
||||
void Water::toggle()
|
||||
|
@ -164,13 +183,15 @@ void Water::checkUnderwater(float y)
|
|||
|
||||
if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID)
|
||||
{
|
||||
mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false);
|
||||
//mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false);
|
||||
|
||||
// tell the shader we are not underwater
|
||||
|
||||
/*
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0));
|
||||
|
||||
*/
|
||||
mWater->setRenderQueueGroup(RQG_Water);
|
||||
|
||||
mIsUnderwater = false;
|
||||
|
@ -178,15 +199,16 @@ void Water::checkUnderwater(float y)
|
|||
|
||||
if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID)
|
||||
{
|
||||
if (mUnderwaterEffect)
|
||||
mRendering->getCompositors()->setCompositorEnabled(mCompositorName, true);
|
||||
//if (mUnderwaterEffect)
|
||||
//mRendering->getCompositors()->setCompositorEnabled(mCompositorName, true);
|
||||
|
||||
// tell the shader we are underwater
|
||||
/*
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1));
|
||||
|
||||
mWater->setRenderQueueGroup(RQG_UnderWater);
|
||||
*/
|
||||
//mWater->setRenderQueueGroup(RQG_UnderWater);
|
||||
|
||||
mIsUnderwater = true;
|
||||
}
|
||||
|
@ -211,12 +233,11 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt)
|
|||
mReflectionCamera->setFOVy(mCamera->getFOVy());
|
||||
mReflectionRenderActive = true;
|
||||
|
||||
/// \todo For some reason this camera is delayed for 1 frame, which causes ugly sky reflection behaviour..
|
||||
/// to circumvent this we just scale the sky up, so it's not that noticable
|
||||
/// \todo the reflection render (and probably all renderingmanager-updates) lag behind 1 camera frame for some reason
|
||||
Vector3 pos = mCamera->getRealPosition();
|
||||
pos.y = mTop*2 - pos.y;
|
||||
mSky->setSkyPosition(pos);
|
||||
mSky->scaleSky(mCamera->getFarClipDistance() / 5000.f);
|
||||
mSky->scaleSky(mCamera->getFarClipDistance() / 50.f);
|
||||
mReflectionCamera->enableReflection(mWaterPlane);
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +263,7 @@ void Water::createMaterial()
|
|||
else
|
||||
{
|
||||
mMaterial = MaterialManager::getSingleton().getByName("Water");
|
||||
mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture(mReflectionTexture);
|
||||
sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mReflectionTexture->getName());
|
||||
}
|
||||
|
||||
// these have to be set in code
|
||||
|
@ -251,30 +272,23 @@ void Water::createMaterial()
|
|||
{
|
||||
textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds";
|
||||
}
|
||||
Ogre::Technique* tech;
|
||||
if (mReflectionTarget == 0)
|
||||
tech = mMaterial->getTechnique(0);
|
||||
else
|
||||
tech = mMaterial->getTechnique(1);
|
||||
|
||||
tech->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2);
|
||||
if (mReflectionTarget == 0)
|
||||
mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2);
|
||||
}
|
||||
|
||||
void Water::assignTextures()
|
||||
{
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
|
||||
CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mRendering->getViewport())->getCompositor("gbuffer");
|
||||
|
||||
TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0);
|
||||
TextureUnitState* tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("refractionMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(colorTexture);
|
||||
sh::Factory::getInstance ().setTextureAlias ("WaterRefraction", colorTexture->getName());
|
||||
|
||||
TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1);
|
||||
tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("depthMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(depthTexture);
|
||||
sh::Factory::getInstance ().setTextureAlias ("SceneDepth", depthTexture->getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +302,7 @@ void Water::updateVisible()
|
|||
{
|
||||
mWater->setVisible(mToggled && mActive);
|
||||
if (mReflectionTarget)
|
||||
mReflectionTarget->setActive(mToggled && mActive && !mIsUnderwater);
|
||||
mReflectionTarget->setActive(mToggled && mActive);
|
||||
}
|
||||
|
||||
void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation)
|
||||
|
@ -296,7 +310,9 @@ void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &in
|
|||
// We don't want the sky to get clipped by custom near clip plane (the water plane)
|
||||
if (queueGroupId < 20 && mReflectionRenderActive)
|
||||
{
|
||||
mOldFarClip = mReflectionCamera->getFarClipDistance ();
|
||||
mReflectionCamera->disableCustomNearClipPlane();
|
||||
mReflectionCamera->setFarClipDistance (1000000000);
|
||||
Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS());
|
||||
}
|
||||
}
|
||||
|
@ -305,13 +321,21 @@ void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invo
|
|||
{
|
||||
if (queueGroupId < 20 && mReflectionRenderActive)
|
||||
{
|
||||
mReflectionCamera->setFarClipDistance (mOldFarClip);
|
||||
if (!mIsUnderwater)
|
||||
mReflectionCamera->enableCustomNearClipPlane(mWaterPlane);
|
||||
Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS());
|
||||
}
|
||||
}
|
||||
|
||||
void Water::update()
|
||||
void Water::update(float dt)
|
||||
{
|
||||
Ogre::Vector3 pos = mCamera->getDerivedPosition ();
|
||||
pos.y = -mWaterPlane.d;
|
||||
mUnderwaterDome->setPosition (pos);
|
||||
|
||||
mWaterTimer += dt;
|
||||
sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(mWaterTimer)));
|
||||
}
|
||||
|
||||
void Water::applyRTT()
|
||||
|
|
|
@ -39,11 +39,17 @@ namespace MWRender {
|
|||
Ogre::SceneNode *mWaterNode;
|
||||
Ogre::Entity *mWater;
|
||||
|
||||
Ogre::SceneNode* mUnderwaterDome;
|
||||
|
||||
bool mIsUnderwater;
|
||||
bool mActive;
|
||||
bool mToggled;
|
||||
int mTop;
|
||||
|
||||
int mOldFarClip;
|
||||
|
||||
float mWaterTimer;
|
||||
|
||||
bool mReflectionRenderActive;
|
||||
|
||||
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
|
||||
|
@ -83,7 +89,7 @@ namespace MWRender {
|
|||
void setActive(bool active);
|
||||
|
||||
void toggle();
|
||||
void update();
|
||||
void update(float dt);
|
||||
|
||||
void assignTextures();
|
||||
|
||||
|
|
2
extern/shiny
vendored
2
extern/shiny
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 5a9bda6010413555736479ef03103f764fecb91d
|
||||
Subproject commit bf003238a27d94be43724e6774d25c38b4d578c8
|
|
@ -18,7 +18,7 @@ compositor gbuffer
|
|||
{
|
||||
// Renders everything except water
|
||||
first_render_queue 0
|
||||
last_render_queue 70
|
||||
last_render_queue 50
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ compositor gbufferFinalizer
|
|||
}
|
||||
pass render_scene
|
||||
{
|
||||
first_render_queue 71
|
||||
first_render_queue 51
|
||||
last_render_queue 100
|
||||
}
|
||||
}
|
||||
|
|
117
files/materials/caustics.h
Normal file
117
files/materials/caustics.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
#define VISIBILITY 1500.0 // how far you can look through water
|
||||
|
||||
#define BIG_WAVES_X 0.3 // strength of big waves
|
||||
#define BIG_WAVES_Y 0.3
|
||||
|
||||
#define MID_WAVES_X 0.3 // strength of middle sized waves
|
||||
#define MID_WAVES_Y 0.15
|
||||
|
||||
#define SMALL_WAVES_X 0.15 // strength of small waves
|
||||
#define SMALL_WAVES_Y 0.1
|
||||
|
||||
#define WAVE_CHOPPYNESS 0.15 // wave choppyness
|
||||
#define WAVE_SCALE 0.01 // overall wave scale
|
||||
|
||||
#define ABBERATION 0.001 // chromatic abberation amount
|
||||
|
||||
float3 intercept(float3 lineP,
|
||||
float3 lineN,
|
||||
float3 planeN,
|
||||
float planeD)
|
||||
{
|
||||
|
||||
float distance = (planeD - dot(planeN, lineP)) / dot(lineN, planeN);
|
||||
return lineP + lineN * distance;
|
||||
}
|
||||
|
||||
float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer)
|
||||
{
|
||||
float2 nCoord = float2(0.0);
|
||||
bend *= WAVE_CHOPPYNESS;
|
||||
nCoord = coords * (WAVE_SCALE * 0.05) + windDir * timer * (windSpeed*0.04);
|
||||
float3 normal0 = 2.0 * shSample(tex, nCoord + float2(-timer*0.015,-timer*0.05)).rgb - 1.0;
|
||||
nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.08)-normal0.xy*bend;
|
||||
float3 normal1 = 2.0 * shSample(tex, nCoord + float2(+timer*0.020,+timer*0.015)).rgb - 1.0;
|
||||
|
||||
nCoord = coords * (WAVE_SCALE * 0.25) + windDir * timer * (windSpeed*0.07)-normal1.xy*bend;
|
||||
float3 normal2 = 2.0 * shSample(tex, nCoord + float2(-timer*0.04,-timer*0.03)).rgb - 1.0;
|
||||
nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.09)-normal2.xy*bend;
|
||||
float3 normal3 = 2.0 * shSample(tex, nCoord + float2(+timer*0.03,+timer*0.04)).rgb - 1.0;
|
||||
|
||||
nCoord = coords * (WAVE_SCALE* 1.0) + windDir * timer * (windSpeed*0.4)-normal3.xy*bend;
|
||||
float3 normal4 = 2.0 * shSample(tex, nCoord + float2(-timer*0.2,+timer*0.1)).rgb - 1.0;
|
||||
nCoord = coords * (WAVE_SCALE * 2.0) + windDir * timer * (windSpeed*0.7)-normal4.xy*bend;
|
||||
float3 normal5 = 2.0 * shSample(tex, nCoord + float2(+timer*0.1,-timer*0.06)).rgb - 1.0;
|
||||
|
||||
|
||||
float3 normal = normalize(normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y +
|
||||
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
|
||||
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y);
|
||||
return normal;
|
||||
}
|
||||
|
||||
float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer)
|
||||
{
|
||||
bend *= WAVE_CHOPPYNESS;
|
||||
float3 col = float3(0.0);
|
||||
float2 nCoord = float2(0.0); //normal coords
|
||||
|
||||
nCoord = coords * (WAVE_SCALE * 0.025) + windDir * timer * (windSpeed*0.03);
|
||||
col += shSample(tex,nCoord + float2(-timer*0.005,-timer*0.01)).rgb*0.20;
|
||||
nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.05)-(col.xy/col.zz)*bend;
|
||||
col += shSample(tex,nCoord + float2(+timer*0.01,+timer*0.005)).rgb*0.20;
|
||||
|
||||
nCoord = coords * (WAVE_SCALE * 0.2) + windDir * timer * (windSpeed*0.1)-(col.xy/col.zz)*bend;
|
||||
col += shSample(tex,nCoord + float2(-timer*0.02,-timer*0.03)).rgb*0.20;
|
||||
nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.2)-(col.xy/col.zz)*bend;
|
||||
col += shSample(tex,nCoord + float2(+timer*0.03,+timer*0.02)).rgb*0.15;
|
||||
|
||||
nCoord = coords * (WAVE_SCALE* 0.8) + windDir * timer * (windSpeed*1.0)-(col.xy/col.zz)*bend;
|
||||
col += shSample(tex, nCoord + float2(-timer*0.06,+timer*0.08)).rgb*0.15;
|
||||
nCoord = coords * (WAVE_SCALE * 1.0) + windDir * timer * (windSpeed*1.3)-(col.xy/col.zz)*bend;
|
||||
col += shSample(tex,nCoord + float2(+timer*0.08,-timer*0.06)).rgb*0.10;
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
|
||||
float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 eyePosWS, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed)
|
||||
{
|
||||
float3 waterEyePos = intercept(worldPos.xyz, eyePosWS - worldPos, float3(0,1,0), waterLevel);
|
||||
float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0);
|
||||
|
||||
float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel);
|
||||
|
||||
///\ todo clean this up
|
||||
float causticdepth = length(causticPos-worldPos.xyz);
|
||||
causticdepth = 1.0-shSaturate(causticdepth / VISIBILITY);
|
||||
causticdepth = shSaturate(causticdepth);
|
||||
|
||||
// NOTE: the original shader calculated a tangent space basis here,
|
||||
// but using only the world normal is cheaper and i couldn't see a visual difference
|
||||
// also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information
|
||||
float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1;
|
||||
|
||||
//float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0);
|
||||
|
||||
float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0);
|
||||
|
||||
float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z;
|
||||
|
||||
/// \todo sunFade
|
||||
|
||||
// float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth;
|
||||
float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth;
|
||||
float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z;
|
||||
float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z;
|
||||
//caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth;
|
||||
caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*causticdepth;
|
||||
|
||||
caustics *= 3;
|
||||
|
||||
// shore transition
|
||||
caustics = shLerp (float3(1,1,1), caustics, waterDepth);
|
||||
|
||||
return caustics;
|
||||
}
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
#define shSampler2D(name) uniform sampler2D name; @shUseSampler(name)
|
||||
|
||||
#define shMatrixMult(m, v) m * v
|
||||
#define shMatrixMult(m, v) (m * v)
|
||||
|
||||
// automatically recognized by ogre when the input name equals this
|
||||
#define shInputPosition vertex
|
||||
|
|
|
@ -61,5 +61,10 @@ material openmw_objects_base
|
|||
tex_address_mode clamp
|
||||
filtering none
|
||||
}
|
||||
|
||||
texture_unit causticMap
|
||||
{
|
||||
direct_texture water_nm.png
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#define NEED_DEPTH
|
||||
#endif
|
||||
|
||||
|
||||
#define UNDERWATER LIGHTING
|
||||
|
||||
|
||||
#define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
@ -89,6 +93,10 @@
|
|||
|
||||
// ----------------------------------- FRAGMENT ------------------------------------------
|
||||
|
||||
#if UNDERWATER
|
||||
#include "caustics.h"
|
||||
#endif
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shSampler2D(diffuseMap)
|
||||
shInput(float2, UV)
|
||||
|
@ -145,6 +153,20 @@
|
|||
#if SHADOWS || SHADOWS_PSSM
|
||||
shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart)
|
||||
#endif
|
||||
|
||||
#if UNDERWATER
|
||||
shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix)
|
||||
shUniform(float, waterLevel) @shSharedParameter(waterLevel)
|
||||
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position)
|
||||
shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0)
|
||||
|
||||
shSampler2D(causticMap)
|
||||
|
||||
shUniform(float, waterTimer) @shSharedParameter(waterTimer)
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#endif
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0) = shSample(diffuseMap, UV);
|
||||
|
@ -175,19 +197,41 @@
|
|||
float shadow = 1.0;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
float3 caustics = float3(1,1,1);
|
||||
#if UNDERWATER
|
||||
float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1));
|
||||
if (worldPos.y < waterLevel)
|
||||
{
|
||||
float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0));
|
||||
caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@shForeach(@shGlobalSettingString(num_lights))
|
||||
|
||||
|
||||
/// \todo use the array auto params for lights, and use a real for-loop with auto param "light_count" iterations
|
||||
lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w);
|
||||
d = length(lightDir);
|
||||
|
||||
lightDir = normalize(lightDir);
|
||||
|
||||
#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM)
|
||||
diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow;
|
||||
#if @shIterator == 0
|
||||
|
||||
#if (SHADOWS || SHADOWS_PSSM)
|
||||
diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics;
|
||||
|
||||
#else
|
||||
diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0);
|
||||
#endif
|
||||
|
||||
@shEndForeach
|
||||
|
||||
#if HAS_VERTEXCOLOR
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#define NEED_DEPTH 1
|
||||
#endif
|
||||
|
||||
#define UNDERWATER LIGHTING
|
||||
|
||||
|
||||
#if NEED_DEPTH
|
||||
@shAllocatePassthrough(1, depth)
|
||||
|
@ -122,6 +124,10 @@
|
|||
|
||||
// ----------------------------------- FRAGMENT ------------------------------------------
|
||||
|
||||
#if UNDERWATER
|
||||
#include "caustics.h"
|
||||
#endif
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
|
||||
|
||||
|
@ -180,6 +186,20 @@
|
|||
#endif
|
||||
|
||||
|
||||
#if UNDERWATER
|
||||
shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix)
|
||||
shUniform(float, waterLevel) @shSharedParameter(waterLevel)
|
||||
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position)
|
||||
shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0)
|
||||
|
||||
shSampler2D(causticMap)
|
||||
|
||||
shUniform(float, waterTimer) @shSharedParameter(waterTimer)
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#endif
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
|
||||
|
@ -198,6 +218,19 @@
|
|||
|
||||
|
||||
|
||||
float3 caustics = float3(1,1,1);
|
||||
#if UNDERWATER
|
||||
|
||||
float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1));
|
||||
if (worldPos.y < waterLevel)
|
||||
{
|
||||
float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0));
|
||||
caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Layer calculations
|
||||
@shForeach(@shPropertyString(num_blendmaps))
|
||||
float4 blendValues@shIterator = shSample(blendMap@shIterator, UV);
|
||||
|
@ -272,11 +305,20 @@
|
|||
|
||||
lightDir = normalize(lightDir);
|
||||
|
||||
#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM)
|
||||
diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow;
|
||||
#if @shIterator == 0
|
||||
|
||||
#if (SHADOWS || SHADOWS_PSSM)
|
||||
diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics;
|
||||
|
||||
#else
|
||||
diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0);
|
||||
#endif
|
||||
|
||||
@shEndForeach
|
||||
|
||||
shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse);
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
// note: the fixed function water is created manually, not here
|
||||
|
||||
material openmw_water
|
||||
material Water
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_program water_vertex
|
||||
fragment_program water_fragment
|
||||
|
||||
cull_hardware none
|
||||
|
||||
texture_unit reflectionMap
|
||||
{
|
||||
texture_alias WaterReflection
|
||||
tex_address_mode clamp
|
||||
}
|
||||
|
||||
texture_unit refractionMap
|
||||
{
|
||||
texture_alias WaterRefraction
|
||||
tex_address_mode clamp
|
||||
}
|
||||
|
||||
texture_unit depthMap
|
||||
{
|
||||
|
||||
texture_alias SceneDepth
|
||||
tex_address_mode clamp
|
||||
}
|
||||
|
||||
texture_unit normalMap
|
||||
{
|
||||
texture
|
||||
direct_texture water_nm.png
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
#include "core.h"
|
||||
|
||||
// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
|
||||
shOutput(float3, screenCoordsPassthrough)
|
||||
shOutput(float4, position)
|
||||
shOutput(float, depthPassthrough)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
UV = uv0;
|
||||
|
||||
|
||||
#if !SH_GLSL
|
||||
float4x4 scalemat = float4x4( 0.5, 0, 0, 0.5,
|
||||
0, -0.5, 0, 0.5,
|
||||
0, 0, 0.5, 0.5,
|
||||
0, 0, 0, 1 );
|
||||
#else
|
||||
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||
0.0, -0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0);
|
||||
#endif
|
||||
|
||||
float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition);
|
||||
screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w);
|
||||
|
||||
position = shInputPosition;
|
||||
|
||||
depthPassthrough = shOutputPosition.z;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// tweakables ----------------------------------------------------
|
||||
|
||||
#define BIG_WAVES_X 0.3 // strength of big waves
|
||||
#define BIG_WAVES_Y 0.3
|
||||
|
||||
#define MID_WAVES_X 0.3 // strength of middle sized waves
|
||||
#define MID_WAVES_Y 0.15
|
||||
|
||||
#define SMALL_WAVES_X 0.15 // strength of small waves
|
||||
#define SMALL_WAVES_Y 0.1
|
||||
|
||||
#define WAVE_CHOPPYNESS 0.15 // wave choppyness
|
||||
#define WAVE_SCALE 150 // overall wave scale
|
||||
|
||||
#define ABBERATION 0.001 // chromatic abberation amount
|
||||
#define BUMP 1.5 // overall water surface bumpiness
|
||||
#define REFL_BUMP 0.11 // reflection distortion amount
|
||||
#define REFR_BUMP 0.08 // refraction distortion amount
|
||||
|
||||
#define SCATTER_AMOUNT 3.0 // amount of sunlight scattering
|
||||
#define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering
|
||||
|
||||
#define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction
|
||||
|
||||
#define SPEC_HARDNESS 256 // specular highlights hardness
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
float fresnel_dielectric(float3 Incoming, float3 Normal, float eta)
|
||||
{
|
||||
/* compute fresnel reflectance without explicitly computing
|
||||
the refracted direction */
|
||||
float c = abs(dot(Incoming, Normal));
|
||||
float g = eta * eta - 1.0 + c * c;
|
||||
float result;
|
||||
|
||||
if(g > 0.0) {
|
||||
g = sqrt(g);
|
||||
float A =(g - c)/(g + c);
|
||||
float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);
|
||||
result = 0.5 * A * A *(1.0 + B * B);
|
||||
}
|
||||
else
|
||||
result = 1.0; /* TIR (no refracted component) */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shInput(float3, screenCoordsPassthrough)
|
||||
shInput(float4, position)
|
||||
shInput(float, depthPassthrough)
|
||||
|
||||
shUniform(float, far) @shAutoConstant(far, far_clip_distance)
|
||||
|
||||
shSampler2D(reflectionMap)
|
||||
shSampler2D(refractionMap)
|
||||
shSampler2D(depthMap)
|
||||
shSampler2D(normalMap)
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#define WIND_SPEED windDir_windSpeed.z
|
||||
#define WIND_DIR windDir_windSpeed.xy
|
||||
|
||||
shUniform(float, waterTimer) @shSharedParameter(waterTimer)
|
||||
shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight)
|
||||
|
||||
shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0)
|
||||
shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0)
|
||||
|
||||
|
||||
|
||||
shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping)
|
||||
|
||||
|
||||
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
|
||||
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
|
||||
|
||||
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space)
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
|
||||
float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
|
||||
screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y;
|
||||
|
||||
float depth = shSample(depthMap, screenCoords).x * far - depthPassthrough;
|
||||
float shoreFade = shSaturate(depth / 50.0);
|
||||
|
||||
float2 nCoord = float2(0.0);
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04);
|
||||
float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS;
|
||||
float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0;
|
||||
|
||||
|
||||
|
||||
float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y +
|
||||
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
|
||||
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y).xzy;
|
||||
|
||||
normal = normalize(float3(normal.x * BUMP, normal.y, normal.z * BUMP));
|
||||
|
||||
// normal for sunlight scattering
|
||||
float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 +
|
||||
normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 +
|
||||
normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xzy;
|
||||
lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y, lNormal.z * BUMP));
|
||||
|
||||
|
||||
float3 lVec = normalize(sunPosition.xyz);
|
||||
float3 vVec = normalize(position.xyz - cameraPos.xyz);
|
||||
|
||||
|
||||
float isUnderwater = (cameraPos.y > 0) ? 0.0 : 1.0;
|
||||
|
||||
// sunlight scattering
|
||||
float3 pNormal = float3(0,1,0);
|
||||
vec3 lR = reflect(lVec, lNormal);
|
||||
vec3 llR = reflect(lVec, pNormal);
|
||||
|
||||
float s = shSaturate(dot(lR, vVec)*2.0-1.2);
|
||||
float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y));
|
||||
float3 scatterColour = shLerp(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT)));
|
||||
|
||||
// fresnel
|
||||
float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air
|
||||
float fresnel = fresnel_dielectric(-vVec, normal, ior);
|
||||
|
||||
fresnel = shSaturate(fresnel);
|
||||
|
||||
// reflection
|
||||
float3 reflection = shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb;
|
||||
|
||||
// refraction
|
||||
float3 R = reflect(vVec, normal);
|
||||
|
||||
float3 refraction = float3(0,0,0);
|
||||
refraction.r = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0).r;
|
||||
refraction.g = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION)).g;
|
||||
refraction.b = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION*2.0)).b;
|
||||
|
||||
// brighten up the refraction underwater
|
||||
refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction;
|
||||
|
||||
float waterSunGradient = dot(-vVec, -lVec);
|
||||
waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0));
|
||||
float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5;
|
||||
|
||||
float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0));
|
||||
waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0);
|
||||
float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0;
|
||||
float3 waterext = float3(0.6, 0.9, 1.0);//water extinction
|
||||
watercolour = mix(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT)));
|
||||
|
||||
// specular
|
||||
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS);
|
||||
|
||||
shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz;
|
||||
|
||||
// smooth transition to shore (above water only)
|
||||
shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, refraction, (1-shoreFade) * (1-isUnderwater));
|
||||
|
||||
// fog
|
||||
float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w);
|
||||
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
shader_set water_vertex
|
||||
{
|
||||
source water.shader
|
||||
type vertex
|
||||
profiles_cg vs_2_0 vp40 arbvp1
|
||||
profiles_hlsl vs_2_0
|
||||
}
|
||||
|
||||
shader_set water_fragment
|
||||
{
|
||||
source water.shader
|
||||
type fragment
|
||||
profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1
|
||||
profiles_hlsl ps_2_0
|
||||
}
|
BIN
files/water/underwater_dome.mesh
Normal file
BIN
files/water/underwater_dome.mesh
Normal file
Binary file not shown.
|
@ -39,7 +39,6 @@ compositor Underwater
|
|||
{
|
||||
material Water/Compositor
|
||||
input 0 rt0
|
||||
input 3 scene 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ fragment_program Water_FP cg
|
|||
profiles ps_2_x arbfp1
|
||||
}
|
||||
|
||||
material Water
|
||||
material __Water
|
||||
{
|
||||
technique
|
||||
{
|
||||
|
|
BIN
files/water/water_nm.png
Normal file
BIN
files/water/water_nm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Loading…
Reference in a new issue