mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2024-12-28 18:18:52 +00:00
Merge branch 'master' into 'master'
Overhaul raindrop water ripple effect Closes #6360 See merge request OpenMW/openmw!1316
This commit is contained in:
commit
68e7a4083e
@ -232,6 +232,7 @@ namespace MWGui
|
||||
getWidget(mControllerSwitch, "ControllerButton");
|
||||
getWidget(mWaterTextureSize, "WaterTextureSize");
|
||||
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
|
||||
getWidget(mWaterRainRippleDetail, "WaterRainRippleDetail");
|
||||
getWidget(mLightingMethodButton, "LightingMethodButton");
|
||||
getWidget(mLightsResetButton, "LightsResetButton");
|
||||
getWidget(mMaxLights, "MaxLights");
|
||||
@ -259,6 +260,7 @@ namespace MWGui
|
||||
|
||||
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
|
||||
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
|
||||
mWaterRainRippleDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterRainRippleDetailChanged);
|
||||
|
||||
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
|
||||
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
|
||||
@ -307,10 +309,12 @@ namespace MWGui
|
||||
if (waterTextureSize >= 2048)
|
||||
mWaterTextureSize->setIndexSelected(2);
|
||||
|
||||
int waterReflectionDetail = Settings::Manager::getInt("reflection detail", "Water");
|
||||
waterReflectionDetail = std::clamp(waterReflectionDetail, 0, 5);
|
||||
int waterReflectionDetail = std::clamp(Settings::Manager::getInt("reflection detail", "Water"), 0, 5);
|
||||
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
|
||||
|
||||
int waterRainRippleDetail = std::clamp(Settings::Manager::getInt("rain ripple detail", "Water"), 0, 2);
|
||||
mWaterRainRippleDetail->setIndexSelected(waterRainRippleDetail);
|
||||
|
||||
updateMaxLightsComboBox(mMaxLights);
|
||||
|
||||
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
|
||||
@ -394,11 +398,18 @@ namespace MWGui
|
||||
|
||||
void SettingsWindow::onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
unsigned int level = std::min((unsigned int)5, (unsigned int)pos);
|
||||
unsigned int level = static_cast<unsigned int>(std::min<size_t>(pos, 5));
|
||||
Settings::Manager::setInt("reflection detail", "Water", level);
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onWaterRainRippleDetailChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
unsigned int level = static_cast<unsigned int>(std::min<size_t>(pos, 2));
|
||||
Settings::Manager::setInt("rain ripple detail", "Water", level);
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
if (pos == MyGUI::ITEM_NONE)
|
||||
|
@ -31,6 +31,7 @@ namespace MWGui
|
||||
|
||||
MyGUI::ComboBox* mWaterTextureSize;
|
||||
MyGUI::ComboBox* mWaterReflectionDetail;
|
||||
MyGUI::ComboBox* mWaterRainRippleDetail;
|
||||
|
||||
MyGUI::ComboBox* mMaxLights;
|
||||
MyGUI::ComboBox* mLightingMethodButton;
|
||||
@ -55,6 +56,7 @@ namespace MWGui
|
||||
|
||||
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onWaterRainRippleDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
|
||||
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include <components/nifosg/controller.hpp>
|
||||
|
||||
@ -676,7 +677,10 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
||||
{
|
||||
// use a define map to conditionally compile the shader
|
||||
std::map<std::string, std::string> defineMap;
|
||||
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(mRefraction ? "1" : "0")));
|
||||
defineMap["refraction_enabled"] = std::string(mRefraction ? "1" : "0");
|
||||
const auto rippleDetail = std::clamp(Settings::Manager::getInt("rain ripple detail", "Water"), 0, 2);
|
||||
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
||||
|
||||
|
||||
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
||||
osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
||||
|
@ -77,6 +77,21 @@ Controls what kinds of things are rendered in water reflections.
|
||||
In interiors the lowest level is 2.
|
||||
This setting can be changed ingame with the "Reflection shader detail" dropdown under the Water tab of the Video panel in the Options menu.
|
||||
|
||||
rain ripple detail
|
||||
-----------------
|
||||
|
||||
:Type: integer
|
||||
:Range: 0, 1, 2
|
||||
:Default: 2
|
||||
|
||||
Controls how detailed the raindrop ripples on water are.
|
||||
|
||||
0: single, non-normal-mapped ring per raindrop
|
||||
1: normal-mapped raindrops, with multiple rings
|
||||
2: same as 1, but with a greater number of raindrops
|
||||
|
||||
This setting can be changed ingame with the "Rain ripple detail/density" dropdown under the Water tab of the Video panel in the Options menu.
|
||||
|
||||
small feature culling pixel size
|
||||
--------------------------------
|
||||
|
||||
|
@ -454,6 +454,16 @@
|
||||
<Property key="Caption" value="Reflection shader detail"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 84 350 24">
|
||||
<Widget type="ComboBox" skin="MW_ComboBox" position="0 0 115 24" align="Left Top" name="WaterRainRippleDetail">
|
||||
<Property key="AddItem" value="Simple"/>
|
||||
<Property key="AddItem" value="Sparse"/>
|
||||
<Property key="AddItem" value="Dense"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="64 4 90 16" align="Left Top">
|
||||
<Property key="Caption" value="Rain ripple detail/density"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
@ -652,6 +652,10 @@ refraction = false
|
||||
# Draw objects on water reflections.
|
||||
reflection detail = 2
|
||||
|
||||
# Whether to use fully detailed raindrop ripples. (0, 1, 2).
|
||||
# 0 = rings only; 1 = sparse, high detail; 2 = dense, high detail
|
||||
rain ripple detail = 2
|
||||
|
||||
# Overrides the value in '[Camera] small feature culling pixel size' specifically for water reflection/refraction textures.
|
||||
small feature culling pixel size = 20.0
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#endif
|
||||
|
||||
#define REFRACTION @refraction_enabled
|
||||
#define RAIN_RIPPLE_DETAIL @rain_ripple_detail
|
||||
|
||||
// Inspired by Blender GLSL Water by martinsh ( https://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
|
||||
|
||||
@ -55,14 +56,22 @@ const float WOBBLY_SHORE_FADE_DISTANCE = 6200.0; // fade out wobbly shores to
|
||||
|
||||
// ---------------- rain ripples related stuff ---------------------
|
||||
|
||||
const float RAIN_RIPPLE_GAPS = 5.0;
|
||||
const float RAIN_RIPPLE_RADIUS = 0.1;
|
||||
const float RAIN_RIPPLE_GAPS = 10.0;
|
||||
const float RAIN_RIPPLE_RADIUS = 0.2;
|
||||
|
||||
vec2 randOffset(vec2 c)
|
||||
float scramble(float x, float z)
|
||||
{
|
||||
return fract(vec2(
|
||||
c.x * c.y / 8.0 + c.y * 0.3 + c.x * 0.2,
|
||||
c.x * c.y / 14.0 + c.y * 0.5 + c.x * 0.7));
|
||||
return fract(pow(fract(x)*3.0+1.0, z));
|
||||
}
|
||||
|
||||
vec2 randOffset(vec2 c, float time)
|
||||
{
|
||||
time = fract(time/1000.0);
|
||||
c = vec2(c.x * c.y / 8.0 + c.y * 0.3 + c.x * 0.2,
|
||||
c.x * c.y / 14.0 + c.y * 0.5 + c.x * 0.7);
|
||||
c.x *= scramble(scramble(time + c.x/1000.0, 4.0), 3.0) + 1.0;
|
||||
c.y *= scramble(scramble(time + c.y/1000.0, 3.5), 3.0) + 1.0;
|
||||
return fract(c);
|
||||
}
|
||||
|
||||
float randPhase(vec2 c)
|
||||
@ -70,43 +79,104 @@ float randPhase(vec2 c)
|
||||
return fract((c.x * c.y) / (c.x + c.y + 0.1));
|
||||
}
|
||||
|
||||
vec4 circle(vec2 coords, vec2 i_part, float phase)
|
||||
float blip(float x)
|
||||
{
|
||||
vec2 center = vec2(0.5,0.5) + (0.5 - RAIN_RIPPLE_RADIUS) * (2.0 * randOffset(i_part) - 1.0);
|
||||
vec2 toCenter = coords - center;
|
||||
float d = length(toCenter);
|
||||
|
||||
float r = RAIN_RIPPLE_RADIUS * phase;
|
||||
|
||||
if (d > r)
|
||||
return vec4(0.0,0.0,1.0,0.0);
|
||||
|
||||
float sinValue = (sin(d / r * 1.2) + 0.7) / 2.0;
|
||||
|
||||
float height = (1.0 - abs(phase)) * pow(sinValue,3.0);
|
||||
|
||||
vec3 normal = normalize(mix(vec3(0.0,0.0,1.0),vec3(normalize(toCenter),0.0),height));
|
||||
|
||||
return vec4(normal,height);
|
||||
x = max(0.0, 1.0-x*x);
|
||||
return x*x*x;
|
||||
}
|
||||
|
||||
float blipDerivative(float x)
|
||||
{
|
||||
x = clamp(x, -1.0, 1.0);
|
||||
float n = x*x-1.0;
|
||||
return -6.0*x*n*n;
|
||||
}
|
||||
|
||||
const float RAIN_RING_TIME_OFFSET = 1.0/6.0;
|
||||
|
||||
vec4 circle(vec2 coords, vec2 corner, float adjusted_time)
|
||||
{
|
||||
vec2 center = vec2(0.5,0.5) + (0.5 - RAIN_RIPPLE_RADIUS) * (2.0 * randOffset(corner, floor(adjusted_time)) - 1.0);
|
||||
float phase = fract(adjusted_time);
|
||||
vec2 toCenter = coords - center;
|
||||
|
||||
float r = RAIN_RIPPLE_RADIUS;
|
||||
float d = length(toCenter);
|
||||
float ringfollower = (phase-d/r)/RAIN_RING_TIME_OFFSET-1.0; // -1.0 ~ +1.0 cover the breadth of the ripple's ring
|
||||
|
||||
#if RAIN_RIPPLE_DETAIL > 0
|
||||
// normal mapped ripples
|
||||
if(ringfollower < -1.0 || ringfollower > 1.0)
|
||||
return vec4(0.0);
|
||||
|
||||
if(d > 1.0) // normalize center direction vector, but not for near-center ripples
|
||||
toCenter /= d;
|
||||
|
||||
float height = blip(ringfollower*2.0+0.5); // brighten up outer edge of ring; for fake specularity
|
||||
float range_limit = blip(min(0.0, ringfollower));
|
||||
float energy = 1.0-phase;
|
||||
|
||||
vec2 normal2d = -toCenter*blipDerivative(ringfollower)*5.0;
|
||||
vec3 normal = vec3(normal2d, 0.5);
|
||||
vec4 ret = vec4(normal, height);
|
||||
ret.xyw *= energy*energy;
|
||||
// do energy adjustment here rather than later, so that we can use the w component for fake specularity
|
||||
ret.xyz = normalize(ret.xyz) * energy*range_limit;
|
||||
ret.z *= range_limit;
|
||||
return ret;
|
||||
#else
|
||||
// ring-only ripples
|
||||
if(ringfollower < -1.0 || ringfollower > 0.5)
|
||||
return vec4(0.0);
|
||||
|
||||
float energy = 1.0-phase;
|
||||
float height = blip(ringfollower*2.0+0.5)*energy*energy; // fake specularity
|
||||
|
||||
return vec4(0.0, 0.0, 0.0, height);
|
||||
#endif
|
||||
}
|
||||
vec4 rain(vec2 uv, float time)
|
||||
{
|
||||
vec2 i_part = floor(uv * RAIN_RIPPLE_GAPS);
|
||||
vec2 f_part = fract(uv * RAIN_RIPPLE_GAPS);
|
||||
return circle(f_part,i_part,fract(time * 1.2 + randPhase(i_part)));
|
||||
uv *= RAIN_RIPPLE_GAPS;
|
||||
vec2 f_part = fract(uv);
|
||||
vec2 i_part = floor(uv);
|
||||
float adjusted_time = time * 1.2 + randPhase(i_part);
|
||||
#if RAIN_RIPPLE_DETAIL > 0
|
||||
vec4 a = circle(f_part, i_part, adjusted_time);
|
||||
vec4 b = circle(f_part, i_part, adjusted_time - RAIN_RING_TIME_OFFSET);
|
||||
vec4 c = circle(f_part, i_part, adjusted_time - RAIN_RING_TIME_OFFSET*2.0);
|
||||
vec4 d = circle(f_part, i_part, adjusted_time - RAIN_RING_TIME_OFFSET*3.0);
|
||||
vec4 ret;
|
||||
ret.xy = a.xy - b.xy/2.0 + c.xy/4.0 - d.xy/8.0;
|
||||
// z should always point up
|
||||
ret.z = a.z + b.z /2.0 + c.z /4.0 + d.z /8.0;
|
||||
//ret.xyz *= 1.5;
|
||||
// fake specularity looks weird if we use every single ring, also if the inner rings are too bright
|
||||
ret.w = (a.w + c.w /8.0)*1.5;
|
||||
return ret;
|
||||
#else
|
||||
return circle(f_part, i_part, adjusted_time) * 1.5;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 rainCombined(vec2 uv, float time) // returns ripple normal in xyz and ripple height in w
|
||||
vec2 complex_mult(vec2 a, vec2 b)
|
||||
{
|
||||
return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
|
||||
}
|
||||
vec4 rainCombined(vec2 uv, float time) // returns ripple normal in xyz and fake specularity in w
|
||||
{
|
||||
return
|
||||
rain(uv,time) +
|
||||
rain(uv + vec2(10.5,5.7),time) +
|
||||
rain(uv * 0.75 + vec2(3.7,18.9),time) +
|
||||
rain(uv * 0.9 + vec2(5.7,30.1),time) +
|
||||
rain(uv * 0.8 + vec2(1.2,3.0),time);
|
||||
rain(uv, time)
|
||||
+ rain(complex_mult(uv, vec2(0.4, 0.7)) + vec2(1.2, 3.0),time)
|
||||
#if RAIN_RIPPLE_DETAIL == 2
|
||||
+ rain(uv * 0.75 + vec2( 3.7,18.9),time)
|
||||
+ rain(uv * 0.9 + vec2( 5.7,30.1),time)
|
||||
+ rain(uv * 1.0 + vec2(10.5 ,5.7),time)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
||||
|
||||
float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
|
||||
@ -193,11 +263,11 @@ void main(void)
|
||||
vec4 rainRipple;
|
||||
|
||||
if (rainIntensity > 0.01)
|
||||
rainRipple = rainCombined(position.xy / 1000.0,waterTimer) * clamp(rainIntensity,0.0,1.0);
|
||||
rainRipple = rainCombined(position.xy/1000.0, waterTimer) * clamp(rainIntensity, 0.0, 1.0);
|
||||
else
|
||||
rainRipple = vec4(0.0);
|
||||
|
||||
vec3 rippleAdd = rainRipple.xyz * rainRipple.w * 10.0;
|
||||
vec3 rippleAdd = rainRipple.xyz * 10.0;
|
||||
|
||||
vec2 bigWaves = vec2(BIG_WAVES_X,BIG_WAVES_Y);
|
||||
vec2 midWaves = mix(vec2(MID_WAVES_X,MID_WAVES_Y),vec2(MID_WAVES_RAIN_X,MID_WAVES_RAIN_Y),rainIntensity);
|
||||
@ -245,7 +315,14 @@ void main(void)
|
||||
|
||||
vec4 sunSpec = lcalcSpecular(0);
|
||||
|
||||
// artificial specularity to make rain ripples more noticeable
|
||||
vec3 skyColorEstimate = vec3(max(0.0, mix(-0.3, 1.0, sunFade)));
|
||||
vec3 rainSpecular = abs(rainRipple.w)*mix(skyColorEstimate, vec3(1.0), 0.05)*0.5;
|
||||
|
||||
#if REFRACTION
|
||||
// no alpha here, so make sure raindrop ripple specularity gets properly subdued
|
||||
rainSpecular *= clamp(fresnel*6.0 + specular * sunSpec.w, 0.0, 1.0);
|
||||
|
||||
// refraction
|
||||
vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb;
|
||||
vec3 rawRefraction = refraction;
|
||||
@ -265,7 +342,7 @@ void main(void)
|
||||
vec3 scatterColour = mix(SCATTER_COLOUR*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0));
|
||||
vec3 lR = reflect(lVec, lNormal);
|
||||
float lightScatter = clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0) * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0);
|
||||
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.2;
|
||||
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpec.xyz + rainSpecular;
|
||||
gl_FragData[0].w = 1.0;
|
||||
|
||||
// wobbly water: hard-fade into refraction texture at extremely low depth, with a wobble based on normal mapping
|
||||
@ -278,7 +355,7 @@ void main(void)
|
||||
shoreOffset = clamp(mix(shoreOffset, 1.0, clamp(linearDepth / WOBBLY_SHORE_FADE_DISTANCE, 0.0, 1.0)), 0.0, 1.0);
|
||||
gl_FragData[0].xyz = mix(rawRefraction, gl_FragData[0].xyz, shoreOffset);
|
||||
#else
|
||||
gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.7;
|
||||
gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * sunSpec.xyz + rainSpecular;
|
||||
gl_FragData[0].w = clamp(fresnel*6.0 + specular * sunSpec.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);
|
||||
#endif
|
||||
|
||||
@ -288,7 +365,7 @@ void main(void)
|
||||
#else
|
||||
float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);
|
||||
#endif
|
||||
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
|
||||
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
|
||||
|
||||
applyShadowDebugOverlay();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user