water ripples (experimental)

actorid
scrawl 12 years ago
parent 3ec703e6af
commit a461b282c1

@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap videoplayer
compositors characterpreview externalrendering globalmap videoplayer ripplesimulation
)
add_openmw_dir (mwinput

@ -386,7 +386,10 @@ void RenderingManager::update (float duration, bool paused)
*world->getPlayer().getPlayer().getCell()->mCell,
Ogre::Vector3(cam.x, -cam.z, cam.y))
);
mWater->update(duration);
// MW to ogre coordinates
orig = Ogre::Vector3(orig.x, orig.z, -orig.y);
mWater->update(duration, orig);
}
}

@ -0,0 +1,212 @@
#include "ripplesimulation.hpp"
#include <OgreTextureManager.h>
#include <OgreStringConverter.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRoot.h>
#include <extern/shiny/Main/Factory.hpp>
namespace MWRender
{
RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager)
: mMainSceneMgr(mainSceneManager),
mTime(0),
mCurrentFrameOffset(0,0),
mPreviousFrameOffset(0,0),
mRippleCenter(0,0),
mTextureSize(512),
mRippleAreaLength(1000),
mImpulseSize(20),
mTexelOffset(0,0)
{
Ogre::AxisAlignedBox aabInf;
aabInf.setInfinite();
mHeightToNormalMapMaterial = Ogre::MaterialManager::getSingleton().getByName("HeightToNormalMap");
mHeightmapMaterial = Ogre::MaterialManager::getSingleton().getByName("HeightmapSimulation");
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
mCamera = mSceneMgr->createCamera("RippleCamera");
mRectangle = new Ogre::Rectangle2D(true);
mRectangle->setBoundingBox(aabInf);
mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0, false);
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(mRectangle);
mImpulse = new Ogre::Rectangle2D(true);
mImpulse->setCorners(-0.1, 0.1, 0.1, -0.1, false);
mImpulse->setBoundingBox(aabInf);
mImpulse->setMaterial("AddImpulse");
Ogre::SceneNode* impulseNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
impulseNode->attachObject(mImpulse);
float w=0.05;
for (int i=0; i<4; ++i)
{
Ogre::TexturePtr texture;
if (i != 3)
texture = Ogre::TextureManager::getSingleton().createManual("RippleHeight" + Ogre::StringConverter::toString(i),
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
else
texture = Ogre::TextureManager::getSingleton().createManual("RippleNormal",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
Ogre::RenderTexture* rt = texture->getBuffer()->getRenderTarget();
rt->removeAllViewports();
rt->addViewport(mCamera);
rt->setAutoUpdated(false);
rt->getViewport(0)->setClearEveryFrame(false);
// debug overlay
Ogre::Rectangle2D* debugOverlay = new Ogre::Rectangle2D(true);
debugOverlay->setCorners(w*2-1, 0.9, (w+0.18)*2-1, 0.4, false);
w += 0.2;
debugOverlay->setBoundingBox(aabInf);
Ogre::SceneNode* debugNode = mMainSceneMgr->getRootSceneNode()->createChildSceneNode();
debugNode->attachObject(debugOverlay);
Ogre::MaterialPtr debugMaterial = Ogre::MaterialManager::getSingleton().create("RippleDebug" + Ogre::StringConverter::toString(i),
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
if (i != 3)
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleHeight" + Ogre::StringConverter::toString(i));
else
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleNormal");
debugMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
debugOverlay->setMaterial("RippleDebug" + Ogre::StringConverter::toString(i));
mRenderTargets[i] = rt;
mTextures[i] = texture;
}
sh::Factory::getInstance().setSharedParameter("rippleTextureSize", sh::makeProperty<sh::Vector4>(
new sh::Vector4(1.0/512, 1.0/512, 512, 512)));
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
new sh::Vector3(0, 0, 0)));
sh::Factory::getInstance().setSharedParameter("rippleAreaLength", sh::makeProperty<sh::FloatValue>(
new sh::FloatValue(mRippleAreaLength)));
}
RippleSimulation::~RippleSimulation()
{
delete mRectangle;
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
}
void RippleSimulation::update(float dt, Ogre::Vector2 position)
{
// try to keep 20 fps
mTime += dt;
while (mTime >= 1/20.0)
{
mPreviousFrameOffset = mCurrentFrameOffset;
mCurrentFrameOffset = position - mRippleCenter;
// add texel offsets from previous frame.
mCurrentFrameOffset += mTexelOffset;
mTexelOffset = Ogre::Vector2(std::fmod(mCurrentFrameOffset.x, 1.0f/mTextureSize),
std::fmod(mCurrentFrameOffset.y, 1.0f/mTextureSize));
// now subtract new offset in order to snap to texels
mCurrentFrameOffset -= mTexelOffset;
// texture coordinate space
mCurrentFrameOffset /= mRippleAreaLength;
std::cout << "Offset " << mCurrentFrameOffset << std::endl;
mRippleCenter = position;
addImpulses();
waterSimulation();
heightMapToNormalMap();
swapHeightMaps();
mTime -= 1/20.0;
}
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
new sh::Vector3(mRippleCenter.x + mTexelOffset.x, mRippleCenter.y + mTexelOffset.y, 0)));
}
void RippleSimulation::addImpulse(Ogre::Vector2 position)
{
mImpulses.push(position);
}
void RippleSimulation::addImpulses()
{
mRectangle->setVisible(false);
mImpulse->setVisible(true);
while (mImpulses.size())
{
Ogre::Vector2 pos = mImpulses.front();
pos -= mRippleCenter;
pos /= mRippleAreaLength;
float size = mImpulseSize / mRippleAreaLength;
mImpulse->setCorners(pos.x-size, pos.y+size, pos.x+size, pos.y-size, false);
mImpulses.pop();
mRenderTargets[1]->update();
}
mImpulse->setVisible(false);
mRectangle->setVisible(true);
}
void RippleSimulation::waterSimulation()
{
mRectangle->setMaterial("HeightmapSimulation");
sh::Factory::getInstance().setTextureAlias("Heightmap0", mTextures[0]->getName());
sh::Factory::getInstance().setTextureAlias("Heightmap1", mTextures[1]->getName());
sh::Factory::getInstance().setSharedParameter("currentFrameOffset", sh::makeProperty<sh::Vector3>(
new sh::Vector3(mCurrentFrameOffset.x, mCurrentFrameOffset.y, 0)));
sh::Factory::getInstance().setSharedParameter("previousFrameOffset", sh::makeProperty<sh::Vector3>(
new sh::Vector3(mPreviousFrameOffset.x, mPreviousFrameOffset.y, 0)));
mRenderTargets[2]->update();
}
void RippleSimulation::heightMapToNormalMap()
{
mRectangle->setMaterial("HeightToNormalMap");
sh::Factory::getInstance().setTextureAlias("Heightmap2", mTextures[2]->getName());
mRenderTargets[TEX_NORMAL]->update();
}
void RippleSimulation::swapHeightMaps()
{
// 0 -> 1 -> 2 to 2 -> 0 ->1
Ogre::RenderTexture* tmp = mRenderTargets[0];
Ogre::TexturePtr tmp2 = mTextures[0];
mRenderTargets[0] = mRenderTargets[1];
mTextures[0] = mTextures[1];
mRenderTargets[1] = mRenderTargets[2];
mTextures[1] = mTextures[2];
mRenderTargets[2] = tmp;
mTextures[2] = tmp2;
}
}

@ -0,0 +1,72 @@
#ifndef RIPPLE_SIMULATION_H
#define RIPPLE_SIMULATION_H
#include <OgreTexture.h>
#include <OgreMaterial.h>
#include <OgreVector2.h>
namespace Ogre
{
class RenderTexture;
class Camera;
class SceneManager;
class Rectangle2D;
}
namespace MWRender
{
class RippleSimulation
{
public:
RippleSimulation(Ogre::SceneManager* mainSceneManager);
~RippleSimulation();
void update(float dt, Ogre::Vector2 position);
void addImpulse (Ogre::Vector2 position);
private:
Ogre::RenderTexture* mRenderTargets[4];
Ogre::TexturePtr mTextures[4];
int mTextureSize;
float mRippleAreaLength;
float mImpulseSize;
Ogre::Camera* mCamera;
// own scenemanager to render our simulation
Ogre::SceneManager* mSceneMgr;
Ogre::Rectangle2D* mRectangle;
// scenemanager to create the debug overlays on
Ogre::SceneManager* mMainSceneMgr;
Ogre::MaterialPtr mHeightmapMaterial;
Ogre::MaterialPtr mHeightToNormalMapMaterial;
static const int TEX_NORMAL = 3;
Ogre::Rectangle2D* mImpulse;
std::queue <Ogre::Vector2> mImpulses;
void addImpulses();
void heightMapToNormalMap();
void waterSimulation();
void swapHeightMaps();
float mTime;
Ogre::Vector2 mRippleCenter;
Ogre::Vector2 mTexelOffset;
Ogre::Vector2 mCurrentFrameOffset;
Ogre::Vector2 mPreviousFrameOffset;
};
}
#endif

@ -12,6 +12,7 @@
#include "sky.hpp"
#include "renderingmanager.hpp"
#include "compositors.hpp"
#include "ripplesimulation.hpp"
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
@ -180,8 +181,11 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
mActive(1), mToggled(1),
mRendering(rend),
mWaterTimer(0.f),
mReflection(NULL)
mReflection(NULL),
mSimulation(NULL)
{
mSimulation = new RippleSimulation(mSceneMgr);
mSky = rend->getSkyManager();
mMaterial = MaterialManager::getSingleton().getByName("Water");
@ -375,7 +379,7 @@ void Water::updateVisible()
}
}
void Water::update(float dt)
void Water::update(float dt, Ogre::Vector3 player)
{
/*
Ogre::Vector3 pos = mCamera->getDerivedPosition ();
@ -387,6 +391,12 @@ void Water::update(float dt)
sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(mWaterTimer)));
mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater);
//if (player.y <= mTop)
{
mSimulation->addImpulse(Ogre::Vector2(player.x, player.z));
}
mSimulation->update(dt, Ogre::Vector2(player.x, player.z));
}
void Water::applyRTT()

@ -29,6 +29,7 @@ namespace MWRender {
class SkyManager;
class RenderingManager;
class RippleSimulation;
class Reflection
{
@ -110,6 +111,8 @@ namespace MWRender {
float mWaterTimer;
RippleSimulation* mSimulation;
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
protected:
@ -137,7 +140,7 @@ namespace MWRender {
void setActive(bool active);
void toggle();
void update(float dt);
void update(float dt, Ogre::Vector3 player);
void assignTextures();

@ -219,6 +219,8 @@ namespace sh
break;
}
std::cout << "loading " << it->first << std::endl;
MaterialInstance newInstance(it->first, this);
newInstance.create(mPlatform);
if (!mShadersEnabled)

@ -3,6 +3,7 @@ project(resources)
set(WATER_FILES
underwater_dome.mesh
water_nm.png
circle.png
)
set(GBUFFER_FILES
@ -43,6 +44,12 @@ set(MATERIAL_FILES
selection.mat
selection.shader
selection.shaderset
watersim_heightmap.shader
watersim_addimpulse.shader
watersim_heighttonormal.shader
watersim_common.h
watersim.mat
watersim.shaderset
)
copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}")

@ -28,6 +28,8 @@
#define shNormalInput(type) , in type normal : NORMAL
#define shColourInput(type) , in type colour : COLOR
#define shFract(val) frac(val)
#ifdef SH_VERTEX_SHADER
@ -64,6 +66,8 @@
#if SH_GLSL == 1
#define shFract(val) fract(val)
@version 120
#define float2 vec2

@ -4,7 +4,7 @@ material quad
pass
{
vertex_program quad_vertex
vertex_program transform_vertex
fragment_program quad_fragment
depth_write $depth_write

@ -1,4 +1,4 @@
shader_set quad_vertex
shader_set transform_vertex
{
source quad.shader
type vertex

@ -34,7 +34,13 @@ material Water
{
direct_texture water_nm.png
}
texture_unit rippleNormalMap
{
direct_texture RippleNormal
tex_address_mode border
tex_border_colour 0.5 0.5 1.0
}
// for simple_water
texture_unit animatedTexture

@ -61,7 +61,7 @@
// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
#define RIPPLES 1
#ifdef SH_VERTEX_SHADER
@ -119,8 +119,8 @@
#define WAVE_SCALE 75 // overall wave scale
#define BUMP 1.5 // overall water surface bumpiness
#define REFL_BUMP 0.08 // reflection distortion amount
#define REFR_BUMP 0.06 // refraction distortion amount
#define REFL_BUMP 0.16 // reflection distortion amount
#define REFR_BUMP 0.12 // refraction distortion amount
#define SCATTER_AMOUNT 3.0 // amount of sunlight scattering
#define SCATTER_COLOUR gammaCorrectRead(float3(0.0,1.0,0.95)) // colour of sunlight scattering
@ -159,6 +159,11 @@
shInput(float3, screenCoordsPassthrough)
shInput(float4, position)
shInput(float, depthPassthrough)
#if RIPPLES
shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter)
shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength)
#endif
shUniform(float, far) @shAutoConstant(far, far_clip_distance)
@ -166,6 +171,11 @@
shSampler2D(refractionMap)
shSampler2D(depthMap)
shSampler2D(normalMap)
#if RIPPLES
shSampler2D(rippleNormalMap)
shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix)
#endif
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
#define WIND_SPEED windDir_windSpeed.z
@ -220,8 +230,14 @@
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));
float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1));
float2 relPos = (worldPosition.xz - rippleCenter.xy) / rippleAreaLength + 0.5;
float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2 - 1);
normal_ripple = normal_ripple.xzy;
normal = normalize(normal + normal_ripple);
// normal for sunlight scattering
float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 +
@ -303,7 +319,7 @@
}
shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz);
//shOutputColour(0).xyz = float3(relPos.x, relPos.y, 0);
shOutputColour(0).w = 1;
}

@ -0,0 +1,59 @@
material HeightmapSimulation
{
allow_fixed_function false
pass
{
depth_check off
depth_write off
vertex_program transform_vertex
fragment_program watersim_fragment
texture_unit heightPrevSampler
{
tex_address_mode border
tex_border_colour 0 0 0
texture_alias Heightmap0
}
texture_unit heightCurrentSampler
{
tex_address_mode border
tex_border_colour 0 0 0
texture_alias Heightmap1
}
}
}
material HeightToNormalMap
{
allow_fixed_function false
pass
{
depth_check off
depth_write off
vertex_program transform_vertex
fragment_program height_to_normal_fragment
texture_unit heightCurrentSampler
{
texture_alias Heightmap2
}
}
}
material AddImpulse
{
allow_fixed_function false
pass
{
depth_check off
depth_write off
scene_blend alpha_blend
vertex_program transform_vertex
fragment_program add_impulse_fragment
texture_unit alphaMap
{
texture circle.png
}
}
}

@ -0,0 +1,31 @@
shader_set transform_vertex
{
source quad.shader
type vertex
profiles_cg vs_2_0 vp40 arbvp1
profiles_hlsl vs_2_0
}
shader_set watersim_fragment
{
source watersim_heightmap.shader
type fragment
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
profiles_hlsl ps_3_0 ps_2_0
}
shader_set height_to_normal_fragment
{
source watersim_heighttonormal.shader
type fragment
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
profiles_hlsl ps_3_0 ps_2_0
}
shader_set add_impulse_fragment
{
source watersim_addimpulse.shader
type fragment
profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1
profiles_hlsl ps_3_0 ps_2_0
}

@ -0,0 +1,12 @@
#include "core.h"
#include "watersim_common.h"
SH_BEGIN_PROGRAM
shInput(float2, UV)
shSampler2D(alphaMap)
SH_START_PROGRAM
{
shOutputColour(0) = EncodeHeightmap(1.0);
shOutputColour(0).a = shSample (alphaMap, UV.xy).a;
}

@ -0,0 +1,25 @@
float DecodeHeightmap(float4 heightmap)
{
float4 table = float4(1.0, -1.0, 0.0, 0.0);
return dot(heightmap, table);
}
float DecodeHeightmap(shTexture2D HeightmapSampler, float2 texcoord)
{
float4 heightmap = shSample(HeightmapSampler, texcoord);
return DecodeHeightmap(heightmap);
}
float4 EncodeHeightmap(float fHeight)
{
float h = fHeight;
float positive = fHeight > 0.0 ? fHeight : 0.0;
float negative = fHeight < 0.0 ? -fHeight : 0.0;
float4 color = float4(0,0,0,0);
color.r = positive;
color.g = negative;
return color;
}

@ -0,0 +1,42 @@
#include "core.h"
#define DAMPING 0.92
#include "watersim_common.h"
SH_BEGIN_PROGRAM
shInput(float2, UV)
shSampler2D(heightPrevSampler)
shSampler2D(heightCurrentSampler)
shUniform(float3, previousFrameOffset) @shSharedParameter(previousFrameOffset, previousFrameOffset)
shUniform(float3, currentFrameOffset) @shSharedParameter(currentFrameOffset, currentFrameOffset)
shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize)
SH_START_PROGRAM
{
const float3 offset[4] = float3[4](
float3(-1.0, 0.0, 0.25),
float3( 1.0, 0.0, 0.25),
float3( 0.0,-1.0, 0.25),
float3( 0.0, 1.0, 0.25)
);
float fHeightPrev = DecodeHeightmap(heightPrevSampler, UV.xy + previousFrameOffset.xy + currentFrameOffset.xy);
float fNeighCurrent = 0;
for ( int i=0; i<4; i++ )
{
float2 vTexcoord = UV + currentFrameOffset.xy + offset[i].xy * rippleTextureSize.xy;
fNeighCurrent += (DecodeHeightmap(heightCurrentSampler, vTexcoord) * offset[i].z);
}
float fHeight = fNeighCurrent * 2.0 - fHeightPrev;
fHeight *= DAMPING;
shOutputColour(0) = EncodeHeightmap(fHeight);
}

@ -0,0 +1,27 @@
#include "core.h"
#include "watersim_common.h"
SH_BEGIN_PROGRAM
shInput(float2, UV)
shSampler2D(heightCurrentSampler)
shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize)
SH_START_PROGRAM
{
float2 offset[4] = float2[4] (
vec2(-1.0, 0.0),
vec2( 1.0, 0.0),
vec2( 0.0,-1.0),
vec2( 0.0, 1.0)
);
float fHeightL = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[0]*rippleTextureSize.xy);
float fHeightR = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[1]*rippleTextureSize.xy);
float fHeightT = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[2]*rippleTextureSize.xy);
float fHeightB = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[3]*rippleTextureSize.xy);
float3 n = float3(fHeightB - fHeightT, fHeightR - fHeightL, 1.0);
float3 normal = (n + 1.0) * 0.5;
shOutputColour(0) = float4(normal.rgb, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Loading…
Cancel
Save