water ripples (experimental)
parent
3ec703e6af
commit
a461b282c1
@ -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
|
@ -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…
Reference in New Issue