mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-22 03:13:15 -04:00
Merge branch 'freakier-flowmaps' into 'master'
Flowmap extradata effect See merge request OpenMW/openmw!4917
This commit is contained in:
commit
4f7a9c1a5f
@ -200,33 +200,6 @@ namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleExtraData(const std::string& data, osg::Group* node)
|
||||
{
|
||||
YAML::Node root = YAML::Load(data);
|
||||
|
||||
for (const auto& it : root["shader"])
|
||||
{
|
||||
std::string key = it.first.as<std::string>();
|
||||
|
||||
if (key == "soft_effect" && NifOsg::Loader::getSoftEffectEnabled())
|
||||
{
|
||||
SceneUtil::SoftEffectConfig config;
|
||||
config.mSize = it.second["size"].as<float>(config.mSize);
|
||||
config.mFalloff = it.second["falloff"].as<bool>(config.mFalloff);
|
||||
config.mFalloffDepth = it.second["falloffDepth"].as<float>(config.mFalloffDepth);
|
||||
|
||||
SceneUtil::setupSoftEffect(*node, config);
|
||||
}
|
||||
else if (key == "distortion")
|
||||
{
|
||||
SceneUtil::DistortionConfig config;
|
||||
config.mStrength = it.second["strength"].as<float>(config.mStrength);
|
||||
|
||||
SceneUtil::setupDistortion(*node, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace NifOsg
|
||||
@ -664,6 +637,64 @@ namespace NifOsg
|
||||
return node;
|
||||
}
|
||||
|
||||
void handleExtraData(
|
||||
const Nif::NiAVObject* nifNode, const std::string& data, osg::Group* node, HandleNodeArgs& args)
|
||||
{
|
||||
YAML::Node root = YAML::Load(data);
|
||||
|
||||
const bool isNiGeometry = isTypeNiGeometry(nifNode->recType);
|
||||
const bool isBSGeometry = isTypeBSGeometry(nifNode->recType);
|
||||
const bool isGeometry = isNiGeometry || isBSGeometry;
|
||||
|
||||
for (const auto& it : root["shader"])
|
||||
{
|
||||
std::string key = it.first.as<std::string>();
|
||||
|
||||
if (key == "soft_effect" && NifOsg::Loader::getSoftEffectEnabled())
|
||||
{
|
||||
SceneUtil::SoftEffectConfig config;
|
||||
config.mSize = it.second["size"].as<float>(config.mSize);
|
||||
config.mFalloff = it.second["falloff"].as<bool>(config.mFalloff);
|
||||
config.mFalloffDepth = it.second["falloffDepth"].as<float>(config.mFalloffDepth);
|
||||
|
||||
SceneUtil::setupSoftEffect(*node, config);
|
||||
}
|
||||
else if (key == "distortion")
|
||||
{
|
||||
SceneUtil::DistortionConfig config;
|
||||
config.mStrength = it.second["strength"].as<float>(config.mStrength);
|
||||
|
||||
SceneUtil::setupDistortion(*node, config);
|
||||
}
|
||||
else if (key == "flowmap")
|
||||
{
|
||||
if (!isGeometry)
|
||||
{
|
||||
throw Nif::Exception(
|
||||
"flowmap effect can only be used on geometry nodes such as NiTriShape", mFilename.string());
|
||||
}
|
||||
|
||||
SceneUtil::FlowMapConfig config;
|
||||
config.mStrength = it.second["strength"].as<float>(config.mStrength);
|
||||
config.mSpeed = it.second["speed"].as<float>(config.mSpeed);
|
||||
config.mOffset = it.second["offset"].as<float>(config.mOffset);
|
||||
config.mJump = it.second["jump"].as<osg::Vec2f>(config.mJump);
|
||||
|
||||
std::string path = it.second["texture"]["path"].as<std::string>();
|
||||
int uvSet = it.second["texture"]["uvSet"].as<int>(0);
|
||||
|
||||
config.mTexture = new osg::Texture2D(getTextureImage(path));
|
||||
config.mTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
|
||||
config.mTexture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
|
||||
config.mTextureUnit = args.mBoundTextures.size();
|
||||
|
||||
args.mBoundTextures.emplace_back(uvSet);
|
||||
|
||||
SceneUtil::setupFlowMap(*node, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> handleNode(
|
||||
const Nif::NiAVObject* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args)
|
||||
{
|
||||
@ -830,7 +861,7 @@ namespace NifOsg
|
||||
|
||||
// Apply any extra effects after processing the nodes children and particle system handling
|
||||
if (!extraData.empty())
|
||||
handleExtraData(extraData, node);
|
||||
handleExtraData(nifNode, extraData, node, args);
|
||||
|
||||
if (composite->getNumControllers() > 0)
|
||||
{
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include "extradata.hpp"
|
||||
|
||||
#include <osg/Node>
|
||||
#include <osg/Vec2f>
|
||||
|
||||
#include <components/misc/osguservalues.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/texturetype.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
@ -35,4 +37,18 @@ namespace SceneUtil
|
||||
|
||||
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
void setupFlowMap(osg::Node& node, const FlowMapConfig& config)
|
||||
{
|
||||
osg::StateSet* stateset = node.getOrCreateStateSet();
|
||||
|
||||
stateset->addUniform(new osg::Uniform("flowMapStrength", config.mStrength));
|
||||
stateset->addUniform(new osg::Uniform("flowMapSpeed", config.mSpeed));
|
||||
stateset->addUniform(new osg::Uniform("flowMapOffset", config.mOffset));
|
||||
stateset->addUniform(new osg::Uniform("flowMapJump", config.mJump));
|
||||
|
||||
stateset->setTextureAttribute(config.mTextureUnit, config.mTexture, osg::StateAttribute::ON);
|
||||
stateset->setTextureAttribute(
|
||||
config.mTextureUnit, new SceneUtil::TextureType("flowMap"), osg::StateAttribute::ON);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
namespace osg
|
||||
{
|
||||
class Node;
|
||||
class vec2f;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
@ -20,8 +21,19 @@ namespace SceneUtil
|
||||
float mStrength = 0.1f;
|
||||
};
|
||||
|
||||
struct FlowMapConfig
|
||||
{
|
||||
float mStrength = 1.f;
|
||||
float mSpeed = 1.f;
|
||||
float mOffset = 0.f;
|
||||
osg::Vec2f mJump = { 0.f, 0.f };
|
||||
osg::ref_ptr<osg::Texture2D> mTexture = nullptr;
|
||||
std::size_t mTextureUnit = 0;
|
||||
};
|
||||
|
||||
void setupSoftEffect(osg::Node& node, const SoftEffectConfig& config);
|
||||
void setupDistortion(osg::Node& node, const DistortionConfig& config);
|
||||
void setupFlowMap(osg::Node& node, const FlowMapConfig& config);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -291,7 +291,7 @@ namespace Shader
|
||||
// shader defines. Normal maps and normal height maps both get sent to the shader as a normal map, so the latter
|
||||
// must be detected separately.
|
||||
const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap",
|
||||
"specularMap", "decalMap", "bumpMap", "glossMap" };
|
||||
"specularMap", "decalMap", "bumpMap", "glossMap", "flowMap" };
|
||||
bool isTextureNameRecognized(std::string_view name)
|
||||
{
|
||||
if (std::find(std::begin(defaultTextures), std::end(defaultTextures), name) != std::end(defaultTextures))
|
||||
@ -395,6 +395,10 @@ namespace Shader
|
||||
// As well as gloss maps
|
||||
writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON);
|
||||
}
|
||||
else if (texName == "flowMap")
|
||||
{
|
||||
mRequirements.back().mShaderRequired = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
Log(Debug::Error) << "ShaderVisitor encountered unknown texture " << texture;
|
||||
|
@ -59,7 +59,7 @@ Distortion
|
||||
----------
|
||||
|
||||
This effect is used to imitate effects such as refraction and heat distortion. A common use case is to assign a normal map to the
|
||||
diffuse slot to a material and add uv scrolling. The red and green channels of the texture are used to offset the final scene texture.
|
||||
diffuse slot to a material and add UV scrolling. The red and green channels of the texture are used to offset the final scene texture.
|
||||
Blue and alpha channels are ignored.
|
||||
|
||||
To use this feature the :ref:`post processing <Post Processing>` setting must be enabled.
|
||||
@ -90,3 +90,64 @@ Example usage.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flow Map
|
||||
--------
|
||||
|
||||
+-----------------+
|
||||
| Supported Nodes |
|
||||
+-----------------+
|
||||
| NiTriShape |
|
||||
+-----------------+
|
||||
| NiTriStrips |
|
||||
+-----------------+
|
||||
|
||||
This effect allows textured geometry to be animated and distorted using flow map textures. This can be useful for simulating
|
||||
flowing liquids or magical effects. UV maps are distorted according to the flow map texture. They are then blended with
|
||||
another phase of the same UV at a different time offset. Flow maps can only be applied to geometry nodes, such as `NiTriShape`,
|
||||
and affects the diffuse, normal, and specular maps.
|
||||
|
||||
Variables.
|
||||
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| Name | Description | Type | Default |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| strength| The strength of the flow map UV distortion. Scales linearly. | float | 1 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| speed | The speed of the flow map, affects both the UV distortion and the phase change. | float | 1 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| offset | The time offset of the flow map. Controls where the animation starts. | float | 0 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| jump | Controls the UV offset applied with each phase shift, along the x and y axis. | vec2f | [0, 0] |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
|
||||
The flow map requires a texture and UV set to use. The red and green channels of the flow map texture are the flow directions
|
||||
in x and y respectively, the blue channel is used to offset the phase change, hiding repetition. The texture path and UV set
|
||||
are defined in a JSON object with the name `texture`.
|
||||
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| Name | Description | Type | Default |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| path | Path to the flow map texture in the VFS. This is required. | string | nullptr |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| uvSet | ID of the UV set to use for the flow map texture. | int | 0 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
|
||||
Example usage.
|
||||
|
||||
::
|
||||
|
||||
omw:data {
|
||||
"shader" : {
|
||||
"flowmap" : {
|
||||
"strength": 0.5,
|
||||
"speed": 1.0,
|
||||
"offset": 0.0,
|
||||
"jump": [0.25, 0.24],
|
||||
"texture": {
|
||||
"path" : "flowmap.tga",
|
||||
"uvSet": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ set(SHADER_FILES
|
||||
lib/util/quickstep.glsl
|
||||
lib/util/coordinates.glsl
|
||||
lib/util/distortion.glsl
|
||||
lib/util/flowmap.glsl
|
||||
lib/core/fragment.glsl
|
||||
lib/core/fragment.h.glsl
|
||||
lib/core/fragment_multiview.glsl
|
||||
|
@ -62,6 +62,11 @@ uniform sampler2D glossMap;
|
||||
varying vec2 glossMapUV;
|
||||
#endif
|
||||
|
||||
#if @flowMap
|
||||
uniform sampler2D flowMap;
|
||||
varying vec2 flowMapUV;
|
||||
#endif
|
||||
|
||||
uniform vec2 screenRes;
|
||||
uniform float near;
|
||||
uniform float far;
|
||||
@ -94,6 +99,7 @@ varying vec4 passTangent;
|
||||
#include "lib/material/parallax.glsl"
|
||||
#include "lib/material/alpha.glsl"
|
||||
#include "lib/util/distortion.glsl"
|
||||
#include "lib/util/flowmap.glsl"
|
||||
|
||||
#include "fog.glsl"
|
||||
#include "vertexcolors.glsl"
|
||||
@ -114,6 +120,8 @@ uniform sampler2D orthoDepthMap;
|
||||
varying vec3 orthoDepthMapCoord;
|
||||
#endif
|
||||
|
||||
uniform float osg_SimulationTime;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if @particleOcclusion
|
||||
@ -135,7 +143,12 @@ void main()
|
||||
vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
|
||||
#if @diffuseMap
|
||||
|
||||
#if @flowMap
|
||||
gl_FragData[0] = applyFlowMap(diffuseMap, diffuseMapUV + offset, flowMap, flowMapUV, osg_SimulationTime);
|
||||
#else
|
||||
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV + offset);
|
||||
#endif
|
||||
|
||||
#if defined(DISTORTION) && DISTORTION
|
||||
gl_FragData[0].a *= getDiffuseColor().a;
|
||||
@ -163,7 +176,13 @@ vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef);
|
||||
|
||||
#if @normalMap
|
||||
|
||||
#if @flowMap
|
||||
vec4 normalTex = applyFlowMap(normalMap, normalMapUV + offset, flowMap, flowMapUV, osg_SimulationTime);
|
||||
#else
|
||||
vec4 normalTex = texture2D(normalMap, normalMapUV + offset);
|
||||
#endif
|
||||
|
||||
vec3 normal = normalTex.xyz * 2.0 - 1.0;
|
||||
#if @reconstructNormalZ
|
||||
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
|
||||
@ -221,7 +240,13 @@ vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
specular = passSpecular + shadowSpecularLighting * shadowing;
|
||||
#else
|
||||
#if @specularMap
|
||||
|
||||
#if @flowMap
|
||||
vec4 specTex = applyFlowMap(specularMap, specularMapUV, flowMap, flowMapUV, osg_SimulationTime);
|
||||
#else
|
||||
vec4 specTex = texture2D(specularMap, specularMapUV);
|
||||
#endif
|
||||
|
||||
float shininess = specTex.a * 255.0;
|
||||
vec3 specularColor = specTex.xyz;
|
||||
#else
|
||||
|
@ -49,6 +49,10 @@ varying vec2 specularMapUV;
|
||||
varying vec2 glossMapUV;
|
||||
#endif
|
||||
|
||||
#if @flowMap
|
||||
varying vec2 flowMapUV;
|
||||
#endif
|
||||
|
||||
#define PER_PIXEL_LIGHTING (@normalMap || @specularMap || @forcePPL)
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
@ -147,6 +151,10 @@ void main(void)
|
||||
glossMapUV = (gl_TextureMatrix[@glossMapUV] * gl_MultiTexCoord@glossMapUV).xy;
|
||||
#endif
|
||||
|
||||
#if @flowMap
|
||||
flowMapUV = (gl_TextureMatrix[@flowMapUV] * gl_MultiTexCoord@flowMapUV).xy;
|
||||
#endif
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
vec3 diffuseLight, ambientLight, specularLight;
|
||||
doLighting(viewPos.xyz, viewNormal, gl_FrontMaterial.shininess, diffuseLight, ambientLight, specularLight, shadowDiffuseLighting, shadowSpecularLighting);
|
||||
|
41
files/shaders/lib/util/flowmap.glsl
Normal file
41
files/shaders/lib/util/flowmap.glsl
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef LIB_UTIL_FLOWMAP
|
||||
#define LIB_UTIL_FLOWMAP
|
||||
|
||||
uniform float flowMapStrength;
|
||||
uniform float flowMapSpeed;
|
||||
uniform float flowMapOffset;
|
||||
uniform vec2 flowMapJump;
|
||||
|
||||
vec3 flowUVW(vec2 uv, vec2 flowVector, vec2 jump, float flowOffset, float time, float phaseOffset)
|
||||
{
|
||||
float alpha = fract(time + phaseOffset);
|
||||
|
||||
vec3 uvw = vec3(0.0);
|
||||
uvw.xy = uv - flowVector * (alpha + flowOffset);
|
||||
uvw.xy += phaseOffset;
|
||||
uvw.xy += (time - alpha) * jump;
|
||||
uvw.z = 1.0 - abs(1.0 - 2.0 * alpha);
|
||||
|
||||
return uvw;
|
||||
}
|
||||
|
||||
vec4 applyFlowMap(sampler2D tex, vec2 texUV, sampler2D flowMapTex, vec2 flowMapUV, float time)
|
||||
{
|
||||
// Based on https://catlikecoding.com/unity/tutorials/flow/texture-distortion/
|
||||
// TODO: When using this on a normal map the blended normals are incorrect, does this need a unique function or is it 'good enough'?
|
||||
vec2 flowVector = texture2D(flowMapTex, flowMapUV).xy * 2.0 - 1.0;
|
||||
flowVector *= flowMapStrength;
|
||||
|
||||
float noise = texture2D(flowMapTex, flowMapUV).z;
|
||||
time = time * flowMapSpeed + noise;
|
||||
|
||||
vec3 uvwA = flowUVW(texUV, flowVector, flowMapJump, flowMapOffset, time, 0.0);
|
||||
vec3 uvwB = flowUVW(texUV, flowVector, flowMapJump, flowMapOffset, time, 0.5);
|
||||
|
||||
vec4 texA = texture2D(tex, uvwA.xy) * uvwA.z;
|
||||
vec4 texB = texture2D(tex, uvwB.xy) * uvwB.z;
|
||||
|
||||
return texA + texB;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user