Turns out it is correct way, although I still have something wrong.
As for questions:
przemir wrote: ↑Sun Nov 06, 2022 11:57 am
Can I set and unset this property for the same item depending on whether I use motion blur at that moment (enable it only during fast animation)?
Seems I have to change datablock to enforce calculateHashForPreCreate (I had to do it even after creation of item as I couldn't set renderable mCustomProperties to subItems there). So yes, I can enable/disable motion blur by changing datablock (I may set/remove mCustomProperties just before then).
przemir wrote: ↑Sun Nov 06, 2022 11:57 am
But how do I identify which matrix from 2) to use for given item inside a shader?
Using buffer for renderable and filling place used by PLANAR_REFLECTION (it is not used if not build with OGRE_BUILD_COMPONENT_PLANAR_REFLECTIONS).
Code: Select all
Ogre::uint32 fillBuffersFor(const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer, bool isV1)
{
// Keep in mind that only the first one has to be correct, as Ogre's auto instancing will increment the base value automatically.
Ogre::uint32 drawId = Ogre::HlmsPbs::fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer, isV1);
// After Ogre::HlmsUnlit::fillBuffersFor const buffer was already filled. There was one
// unused value though at position [3]. Buffer was shifted by 4.
// Beware that const buffer .w may collide with PLANAR_REFLECTIONS if enabled...
Ogre::uint32 * RESTRICT_ALIAS currentMappedConstBuffer = mCurrentMappedConstBuffer;
*(currentMappedConstBuffer-1) = queuedRenderable.renderable->mCustomParameter;
return drawId;
}
In vertex shader InstanceBuffer isn't declared by default. You need to @insertpiece( InstanceStructDecl ) inside @piece( custom_vs_uniformDeclaration ). Then it can be accesed by worldMaterialIdx[finalDrawId].w:
przemir wrote: ↑Sun Nov 06, 2022 11:57 am
And how to declare this movement texture for pbs?
It is called MRT (Multi Render Target). It can be declared in compositor like that:
Code: Select all
texture rt0 target_width target_height PFG_RGBA8_UNORM_SRGB
texture motionTexture target_width target_height PFG_RG32_FLOAT
rtv mrt
{
// For MRT (Multiple Render Target) write multiple textures here
colour rt0 motionTexture
}
For motion blur float2 texture is needed.
For pixel shader to declare custom second target you need to modify file from Ogre: inside RenderDepthOnly_piece_ps.hlsl add @insertpiece( custom_ps_output_types ) just after @insertpiece( ExtraOutputTypes ).
przemir wrote: ↑Sun Nov 06, 2022 11:57 am
How to connect movement texture from 3) to compositor?
Just use motionTexture from code above.
Now I get this:

It is behaving like cube was moving in different direction.
All my pieces:
500.Structs_piece_vs_piece_ps.hlsl
Code: Select all
@piece( custom_passBuffer )
// custom_passBuffer
@property(motion_blur_enabled)
float4x4 prevMatrix[20];
//float3x4 prevMatrix[20];
//float4x3 prevMatrix[20];
float motionBlurInfluence[20];
@end
@end
@piece( custom_VStoPS )
// custom_VStoPS
@property(motion_blur_enabled)
INTERPOLANT( float4 currClipSpacePos0, @counter(texcoord) );
INTERPOLANT( float4 prevClipSpacePos0, @counter(texcoord) );
INTERPOLANT( float motionBlurInfluence, @counter(texcoord) );
@end
@end
800.VertexShader_piece_vs.hlsl
Code: Select all
@piece( custom_vs_uniformDeclaration )
// custom_vs_uniformDeclaration
@property(motion_blur_enabled)
@insertpiece( InstanceStructDecl )
@end
@end
@piece( custom_vs_attributes )
// custom_vs_attributes
@end
@piece( custom_vs_preExecution )
// custom_vs_preExecution
@end
@piece( custom_vs_posExecution )
// custom_vs_posExecution
@property(motion_blur_enabled)
uint motionBlurDataIndex = worldMaterialIdx[finalDrawId].w;
float4x4 prevWorldMat = passBuf.prevMatrix[motionBlurDataIndex];
outVs.currClipSpacePos0 = outVs_Position;
// float4 worldPos = float4( mul(inVs_vertex, worldMat ).xyz, 1.0f );
float4 prevWorldPos = float4( mul(inVs_vertex, prevWorldMat).xyz, 1.0f );
// float4 prevWorldPos = mul(inVs_vertex, prevWorldMat);
// outVs.currClipSpacePos0 = mul( worldPos , passBuf.viewProj );
outVs.prevClipSpacePos0 = mul( prevWorldPos, passBuf.viewProj );
outVs.motionBlurInfluence = passBuf.motionBlurInfluence[motionBlurDataIndex];
@end
@end
800.PixelShader_piece_ps.any
Code: Select all
@piece( custom_ps_uniformDeclaration )
// custom_ps_uniformDeclaration
@end
@piece( custom_ps_output_types )
// custom_ps_output_types
@property(motion_blur_enabled)
float2 motion : SV_Target@counter(rtv_target);
@end
@end
@piece( custom_ps_preExecution )
// custom_ps_preExecution
@end
@piece( custom_ps_posExecution )
// custom_ps_posExecution
@property(motion_blur_enabled)
float3 NDCPos = (inPs.currClipSpacePos0 / inPs.currClipSpacePos0.w).xyz;
float3 PrevNDCPos = (inPs.prevClipSpacePos0 / inPs.prevClipSpacePos0.w).xyz;
outPs.motion = (NDCPos - PrevNDCPos).xy * inPs.motionBlurInfluence;
@end
@end
Previous matrix was from mSceneNode->_getFullTransformUpdated() before changing it to new position, and my hlms code:
Code: Select all
#ifndef HLMSMOTIONBLURPBS_H
#define HLMSMOTIONBLURPBS_H
#include <OgreMatrix4.h>
#include <OgreHlmsPbs.h>
#include "OgreHlmsListener.h"
#include "OgreSceneManager.h"
#include <OgreItem.h>
#include <OgreSubItem.h>
#include <OgreRenderQueue.h>
namespace Ogre {
class SceneManager;
class CompositorShadowNode;
}
class HlmsMotionBlurPbsListener : public Ogre::HlmsListener {
public:
static const int MaxMotionBlurItemCount = 20;
struct MotionBlurData
{
Ogre::Matrix4 mPrevMatrix;
float mInfluence;
};
std::vector<MotionBlurData> mMotionBlurDataList;
public:
HlmsMotionBlurPbsListener() = default;
virtual ~HlmsMotionBlurPbsListener() = default;
virtual Ogre::uint32 getPassBufferSize(const Ogre::CompositorShadowNode* shadowNode, bool casterPass,
bool dualParaboloid, Ogre::SceneManager* sceneManager) const {
Ogre::uint32 size = 0;
if (!casterPass) {
Ogre::uint32 motionBlurPrevMatrixSize = sizeof(float) * 4 * 4;
Ogre::uint32 motionBlurInfluenceSize = sizeof(float);
size += motionBlurPrevMatrixSize * MaxMotionBlurItemCount;
size += motionBlurInfluenceSize * MaxMotionBlurItemCount;
}
return size;
}
virtual float* preparePassBuffer(const Ogre::CompositorShadowNode* shadowNode, bool casterPass,
bool dualParaboloid, Ogre::SceneManager* sceneManager,
float* passBufferPtr) {
if (!casterPass)
{
size_t maxListSize = mMotionBlurDataList.size();
if(maxListSize > MaxMotionBlurItemCount) {
maxListSize = MaxMotionBlurItemCount;
}
for (size_t i = 0; i < maxListSize; ++i) {
const MotionBlurData& motionDataBlur = mMotionBlurDataList[i];
// for( size_t j=0; j<16; ++j )
// *passBufferPtr++ = (float)motionDataBlur.mPrevMatrix[0][j];
for( int y = 0; y < 4; ++y )
{
for( int x = 0; x < 4; ++x )
{
*passBufferPtr++ = (float)motionDataBlur.mPrevMatrix[ y ][ x ];
}
}
}
for (size_t i = mMotionBlurDataList.size(); i < MaxMotionBlurItemCount; ++i) {
for( size_t j=0; j<16; ++j )
*passBufferPtr++ = (float)0.0f;
}
for (size_t i = 0; i < maxListSize; ++i) {
const MotionBlurData& motionDataBlur = mMotionBlurDataList[i];
*passBufferPtr++ = (float)motionDataBlur.mInfluence;
}
for (size_t i = mMotionBlurDataList.size(); i < MaxMotionBlurItemCount; ++i) {
*passBufferPtr++ = (float)0.0f;
}
}
return passBufferPtr;
}
};
class HlmsMotionBlurPbs : public Ogre::HlmsPbs {
public:
static const int MotionBlurParameterIndex = 1238;
private:
HlmsMotionBlurPbsListener mBlademasterPbsListener;
void calculateHashForPreCreate(Ogre::Renderable* renderable, Ogre::PiecesMap* inOutPieces) override {
HlmsPbs::calculateHashForPreCreate(renderable, inOutPieces);
if(renderable->hasCustomParameter(MotionBlurParameterIndex)) {
setProperty("motion_blur_enabled", 1);
}
}
public:
HlmsMotionBlurPbs(Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders)
: Ogre::HlmsPbs(dataFolder, libraryFolders)
{
// mType = Ogre::HLMS_PBS;
// mTypeName = "pbs";
// mTypeNameStr = "pbs";
setListener(&mBlademasterPbsListener);
}
virtual ~HlmsMotionBlurPbs() = default;
void setup(Ogre::SceneManager* manager) {
}
void shutdown(Ogre::SceneManager* manager) {
}
void notifyPropertiesMergedPreGenerationStep(void) {
HlmsPbs::notifyPropertiesMergedPreGenerationStep();
}
static void getDefaultPaths(Ogre::String& outDataFolderPath, Ogre::StringVector& outLibraryFoldersPaths) {
HlmsPbs::getDefaultPaths(outDataFolderPath, outLibraryFoldersPaths);
//We need to know what RenderSystem is currently in use, as the
//name of the compatible shading language is part of the path
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingleton().getRenderSystem();
Ogre::String shaderSyntax = "GLSL";
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
shaderSyntax = "HLSL";
else if (renderSystem->getName() == "Metal Rendering Subsystem")
shaderSyntax = "Metal";
//Fill the library folder paths with the relevant folders
outLibraryFoldersPaths.clear();
outLibraryFoldersPaths.push_back("Hlms/Common/" + shaderSyntax);
outLibraryFoldersPaths.push_back("Hlms/Common/Any");
outLibraryFoldersPaths.push_back("Hlms/Pbs/Any");
outLibraryFoldersPaths.push_back("Hlms/Pbs/Any/Main");
//Fill the data folder path
outDataFolderPath = "Hlms/pbs/" + shaderSyntax;
}
static void getAdditionalPaths(Ogre::StringVector& outLibraryFoldersPaths) {
//We need to know what RenderSystem is currently in use, as the
//name of the compatible shading language is part of the path
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingleton().getRenderSystem();
Ogre::String shaderSyntax = "GLSL";
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
shaderSyntax = "HLSL";
else if (renderSystem->getName() == "Metal Rendering Subsystem")
shaderSyntax = "Metal";
//Fill the library folder paths with the relevant folders
outLibraryFoldersPaths.clear();
outLibraryFoldersPaths.push_back("Hlms/Pbs/" + shaderSyntax);
outLibraryFoldersPaths.push_back("Hlms/Pbs/Any");
}
Ogre::uint32 fillBuffersForV1(const Ogre::HlmsCache* cache,
const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer)
{
return fillBuffersFor(cache, queuedRenderable, casterPass,
lastCacheHash, commandBuffer, true);
}
//-----------------------------------------------------------------------------------
Ogre::uint32 fillBuffersForV2(const Ogre::HlmsCache* cache,
const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer)
{
return fillBuffersFor(cache, queuedRenderable, casterPass,
lastCacheHash, commandBuffer, false);
}
//-----------------------------------------------------------------------------------
Ogre::uint32 fillBuffersFor(const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer, bool isV1)
{
// Keep in mind that only the first one has to be correct, as Ogre's auto instancing will increment the base value automatically.
Ogre::uint32 drawId = Ogre::HlmsPbs::fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer, isV1);
// After Ogre::HlmsUnlit::fillBuffersFor const buffer was already filled. There was one
// unused value though at position [3]. Buffer was shifted by 4.
// Beware that const buffer .w may collide with PLANAR_REFLECTIONS if enabled...
Ogre::uint32 * RESTRICT_ALIAS currentMappedConstBuffer = mCurrentMappedConstBuffer;
*(currentMappedConstBuffer-1) = queuedRenderable.renderable->mCustomParameter;
return drawId;
}
public:
void clearMotionBlurData() {
mBlademasterPbsListener.mMotionBlurDataList.clear();
}
void addItemMotionBlur(Ogre::Item* item, const Ogre::Matrix4& prevMatrix, float influence) {
if(mBlademasterPbsListener.mMotionBlurDataList.size() >= HlmsMotionBlurPbsListener::MaxMotionBlurItemCount) {
return;
}
HlmsMotionBlurPbsListener::MotionBlurData motionBlurData;
motionBlurData.mPrevMatrix = prevMatrix;
motionBlurData.mInfluence = influence;
mBlademasterPbsListener.mMotionBlurDataList.push_back(motionBlurData);
int motionBlurDataIndex = (int)mBlademasterPbsListener.mMotionBlurDataList.size()-1;
for (size_t i = 0; i < item->getNumSubItems(); ++i) {
Ogre::SubItem* subItem = item->getSubItem(i);
subItem->mCustomParameter = motionBlurDataIndex;
}
}
};
#endif