Underwater effect
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
compositor Underwater
|
||||
{
|
||||
technique
|
||||
{
|
||||
texture scene target_width target_height PF_R8G8B8A8
|
||||
|
||||
target scene
|
||||
{
|
||||
input previous
|
||||
}
|
||||
|
||||
target_output
|
||||
{
|
||||
input none
|
||||
pass render_quad
|
||||
{
|
||||
material Underwater/Post
|
||||
input 0 scene
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
OGRE_NATIVE_GLSL_VERSION_DIRECTIVE
|
||||
#include <OgreUnifiedShader.h>
|
||||
|
||||
SAMPLER2D(sceneTex, 0);
|
||||
|
||||
OGRE_UNIFORMS(
|
||||
uniform float time;
|
||||
)
|
||||
|
||||
IN(vec2 vUV, TEXCOORD0)
|
||||
|
||||
float hash(vec2 p)
|
||||
{
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
||||
}
|
||||
|
||||
MAIN_DECLARATION
|
||||
{
|
||||
// Underwater wavy distortion
|
||||
float waveX = sin(vUV.y * 40.0 + time * 2.5) * 0.003
|
||||
+ sin(vUV.y * 20.0 - time * 1.2) * 0.002;
|
||||
float waveY = cos(vUV.x * 35.0 + time * 2.0) * 0.003
|
||||
+ cos(vUV.x * 15.0 - time * 1.5) * 0.002;
|
||||
vec2 distortedUV = vUV + vec2(waveX, waveY);
|
||||
|
||||
vec4 color = texture2D(sceneTex, distortedUV);
|
||||
|
||||
// Blue-green underwater tint
|
||||
color.rgb = mix(color.rgb, vec3(0.0, 0.35, 0.5), 0.2);
|
||||
color.rgb *= vec3(0.9, 1.0, 1.1);
|
||||
|
||||
// Bubbles - procedural circles that rise over time
|
||||
vec2 bubbleUV = vUV * 25.0;
|
||||
bubbleUV.y += time * 0.3;
|
||||
vec2 bubbleId = floor(bubbleUV);
|
||||
vec2 bubbleFract = fract(bubbleUV);
|
||||
float bubbleNoise = hash(bubbleId);
|
||||
float bubble = 0.0;
|
||||
if (bubbleNoise > 0.65) {
|
||||
vec2 bubbleCenter = vec2(
|
||||
hash(bubbleId + 1.0),
|
||||
hash(bubbleId + 2.0));
|
||||
float d = length(bubbleFract - bubbleCenter);
|
||||
bubble = smoothstep(0.12, 0.04, d);
|
||||
// Fade bubbles at top/bottom edges
|
||||
bubble *= smoothstep(0.0, 0.1, bubbleFract.y)
|
||||
* smoothstep(1.0, 0.9, bubbleFract.y);
|
||||
}
|
||||
color.rgb += vec3(0.7, 0.8, 0.9) * bubble * 0.4;
|
||||
|
||||
// Occasional spark flashes
|
||||
float sparkInterval = 2.5;
|
||||
float sparkTime = floor(time / sparkInterval);
|
||||
vec2 sparkUV = vUV * 12.0;
|
||||
vec2 sparkId = floor(sparkUV);
|
||||
float sparkNoise = hash(sparkId + sparkTime);
|
||||
float spark = 0.0;
|
||||
if (sparkNoise > 0.96) {
|
||||
vec2 sparkCenter = vec2(
|
||||
hash(sparkNoise * 17.3),
|
||||
hash(sparkNoise * 53.1));
|
||||
float d = length(fract(sparkUV) - sparkCenter);
|
||||
float flash = sin(time * 15.0) * 0.5 + 0.5;
|
||||
spark = smoothstep(0.06, 0.01, d) * flash;
|
||||
}
|
||||
color.rgb += vec3(1.0, 0.95, 0.7) * spark * 0.7;
|
||||
|
||||
// Simple vignette for depth feeling
|
||||
float vignette = 1.0 - length(vUV - 0.5) * 0.6;
|
||||
vignette = clamp(vignette, 0.3, 1.0);
|
||||
color.rgb *= vignette;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
material Underwater/Post
|
||||
{
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
lighting off
|
||||
depth_check off
|
||||
depth_write off
|
||||
cull_hardware none
|
||||
|
||||
vertex_program_ref UnderwaterVP
|
||||
{
|
||||
}
|
||||
|
||||
fragment_program_ref UnderwaterFP
|
||||
{
|
||||
}
|
||||
|
||||
texture_unit
|
||||
{
|
||||
// filled by compositor input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
vertex_program UnderwaterVP glsl glsles glslang hlsl
|
||||
{
|
||||
source underwater.vert
|
||||
}
|
||||
|
||||
fragment_program UnderwaterFP glsl glsles glslang hlsl
|
||||
{
|
||||
source underwater.frag
|
||||
default_params
|
||||
{
|
||||
param_named time float 0.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
OGRE_NATIVE_GLSL_VERSION_DIRECTIVE
|
||||
#include <OgreUnifiedShader.h>
|
||||
|
||||
OUT(vec2 vUV, TEXCOORD0)
|
||||
|
||||
MAIN_PARAMETERS
|
||||
IN(vec4 vertex, POSITION)
|
||||
IN(vec2 uv0, TEXCOORD0)
|
||||
MAIN_DECLARATION
|
||||
{
|
||||
gl_Position = vertex;
|
||||
vUV = uv0;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <OgreTechnique.h>
|
||||
#include <OgrePass.h>
|
||||
#include <OgreGpuProgramParams.h>
|
||||
#include <OgreCompositorManager.h>
|
||||
|
||||
static const uint32_t WATER_MASK = 0xF00;
|
||||
|
||||
@@ -89,6 +90,8 @@ void EditorWaterPlaneSystem::update(float deltaTime, Ogre::Camera *camera)
|
||||
|
||||
wp.sceneNode->setVisible(true);
|
||||
});
|
||||
|
||||
updateUnderwaterEffect(camera, deltaTime);
|
||||
}
|
||||
|
||||
void EditorWaterPlaneSystem::rebuildWaterPlane(
|
||||
@@ -405,3 +408,58 @@ void EditorWaterPlaneSystem::updateShaderParams(WaterPlane &wp,
|
||||
if (vpParams)
|
||||
vpParams->setNamedConstant("tiling", wp.tiling);
|
||||
}
|
||||
|
||||
void EditorWaterPlaneSystem::updateUnderwaterEffect(Ogre::Camera *camera,
|
||||
float deltaTime)
|
||||
{
|
||||
if (!camera)
|
||||
return;
|
||||
|
||||
Ogre::Viewport *viewport = camera->getViewport();
|
||||
if (!viewport)
|
||||
return;
|
||||
|
||||
Ogre::SceneNode *camNode = camera->getParentSceneNode();
|
||||
float camY = camNode ? camNode->_getDerivedPosition().y : 0.0f;
|
||||
|
||||
// Check if camera is below any enabled water plane
|
||||
bool underwater = false;
|
||||
m_query.each([&](flecs::entity, WaterPlane &wp,
|
||||
TransformComponent &) {
|
||||
if (wp.enabled && camY < wp.waterSurfaceY)
|
||||
underwater = true;
|
||||
});
|
||||
|
||||
// Add compositor to viewport once (or re-add if viewport changed)
|
||||
if (!m_compositorAdded || m_compositorViewport != viewport) {
|
||||
if (m_compositorAdded && m_compositorViewport)
|
||||
Ogre::CompositorManager::getSingleton().removeCompositor(
|
||||
m_compositorViewport, "Underwater");
|
||||
Ogre::CompositorManager::getSingleton().addCompositor(
|
||||
viewport, "Underwater");
|
||||
m_compositorAdded = true;
|
||||
m_compositorViewport = viewport;
|
||||
}
|
||||
|
||||
// Enable/disable compositor based on underwater state
|
||||
Ogre::CompositorManager::getSingleton().setCompositorEnabled(
|
||||
viewport, "Underwater", underwater);
|
||||
|
||||
// Update shader time when underwater
|
||||
if (underwater) {
|
||||
m_underwaterTime += deltaTime;
|
||||
Ogre::MaterialPtr mat =
|
||||
Ogre::MaterialManager::getSingleton().getByName(
|
||||
"Underwater/Post",
|
||||
Ogre::ResourceGroupManager::
|
||||
DEFAULT_RESOURCE_GROUP_NAME);
|
||||
if (mat) {
|
||||
Ogre::Pass *pass = mat->getTechnique(0)->getPass(0);
|
||||
Ogre::GpuProgramParametersSharedPtr fpParams =
|
||||
pass->getFragmentProgramParameters();
|
||||
if (fpParams)
|
||||
fpParams->setNamedConstant(
|
||||
"time", m_underwaterTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ private:
|
||||
void updatePosition(WaterPlane &wp, float deltaTime,
|
||||
Ogre::Camera *camera);
|
||||
void updateShaderParams(WaterPlane &wp, float deltaTime);
|
||||
void updateUnderwaterEffect(Ogre::Camera *camera, float deltaTime);
|
||||
Ogre::SceneNode *getReflectionNode(WaterPlane &wp);
|
||||
Ogre::SceneNode *getRefractionNode(WaterPlane &wp);
|
||||
|
||||
@@ -44,6 +45,9 @@ private:
|
||||
Ogre::SceneManager *m_sceneMgr;
|
||||
flecs::query<struct WaterPlane, struct TransformComponent> m_query;
|
||||
WaterRenderTargetListener m_listener;
|
||||
float m_underwaterTime = 0.0f;
|
||||
bool m_compositorAdded = false;
|
||||
Ogre::Viewport *m_compositorViewport = nullptr;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_EDITOR_WATERPLANE_SYSTEM_HPP
|
||||
|
||||
Reference in New Issue
Block a user