
main.cpp
Code: Select all
#include <irrlicht.h>
#include <vector>
#include "denoise_reduction.hpp"
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
static const int STRENGTH = 7;
static const float OPACITY = 1.0f;
// Helper to get original animation speed from an MD2 mesh (fallback 24 fps)
f32 getOriginalAnimationSpeed(IAnimatedMeshSceneNode* node)
{
if (!node) return 24.0f;
IAnimatedMesh* mesh = node->getMesh();
if (!mesh) return 24.0f;
return 24.0f; // safe default for dwarf.x
}
int main()
{
IrrlichtDevice* device = createDevice(
EDT_OPENGL,
dimension2d<u32>(800,600),
16, false, true, false, 0);
if (!device) return 1;
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* gui = device->getGUIEnvironment();
ITimer* timer = device->getTimer();
driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
if (!driver->queryFeature(EVDF_RENDER_TO_TARGET))
{
device->drop();
return 1;
}
IAnimatedMesh* mesh = smgr->getMesh("../../media/dwarf.x");
if (!mesh)
{
device->drop();
return 1;
}
std::vector<IShadowVolumeSceneNode*> shadowNodes;
std::vector<IAnimatedMeshSceneNode*> animatedNodes;
// Helper lambda (not used, we'll just do it manually for C++98)
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
if (node)
{
node->setMaterialFlag(EMF_LIGHTING, true);
node->setMaterialFlag(EMF_BILINEAR_FILTER, false);
node->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
node->setMD2Animation(scene::EMAT_STAND);
node->setAnimationSpeed(0);
node->setCurrentFrame(0);
node->setPosition(vector3df(0,-9,0));
IShadowVolumeSceneNode* shadow = node->addShadowVolumeSceneNode();
if (shadow)
{
shadow->setFreeze(ESF_FREEZE);
shadowNodes.push_back(shadow);
}
animatedNodes.push_back(node);
}
for (int i = 0; i < 3; ++i)
{
IAnimatedMeshSceneNode* newNode = smgr->addAnimatedMeshSceneNode(mesh);
if (newNode)
{
newNode->setMaterialFlag(EMF_LIGHTING, true);
newNode->setMaterialFlag(EMF_BILINEAR_FILTER, false);
newNode->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
newNode->setMD2Animation(scene::EMAT_STAND);
newNode->setAnimationSpeed(0);
newNode->setCurrentFrame(0);
f32 angle = i * (2 * PI / 8);
f32 radius = 60.0f;
newNode->setPosition(vector3df(radius*cos(angle), -9, radius*sin(angle)));
IShadowVolumeSceneNode* shadow = newNode->addShadowVolumeSceneNode();
if (shadow)
{
shadow->setFreeze(ESF_FREEZE);
shadowNodes.push_back(shadow);
}
animatedNodes.push_back(newNode);
}
}
// --- Apply reflection material to the FIRST material of all dwarves ---
// Load the reflection texture once
ITexture* reflectionTex = driver->getTexture("../../media/irrlicht2_rt.jpg");
for (size_t idx = 0; idx < animatedNodes.size(); ++idx)
{
IAnimatedMeshSceneNode* animNode = animatedNodes[idx];
if (animNode && animNode->getMaterialCount() > 0)
{
video::SMaterial& mat = animNode->getMaterial(0); // first material
mat.MaterialType = video::EMT_REFLECTION_2_LAYER;
mat.setTexture(1, reflectionTex); // second texture layer for reflection
// Optionally set the first texture as well (original diffuse)
// The original diffuse texture is already there at layer 0, we keep it.
}
}
// Get original animation speed (same for all)
f32 originalSpeed = 24.0f;
if (!animatedNodes.empty())
originalSpeed = getOriginalAnimationSpeed(animatedNodes[0]);
const f32 targetUpdateRate = 8.0f; // Hz
const u32 updateIntervalMs = (u32)(1000.0f / targetUpdateRate); // 125 ms
f32 framesPerStep = originalSpeed / targetUpdateRate;
u32 lastUpdateTime = timer->getRealTime();
f32 animationTime = 0.0f;
smgr->setShadowColor(video::SColor(230,0,0,0));
ILightSceneNode* sun = smgr->addLightSceneNode(0,
vector3df(90,150,-45), SColorf(1.0f,1.0f,1.0f), 700.0f);
video::SLight lightData = sun->getLightData();
lightData.Attenuation = core::vector3df(1.0f, 0.000001f, 0.000001f); // quadratic
sun->setLightData(lightData);
IMeshSceneNode* floor = smgr->addCubeSceneNode(1.0f);
if (floor)
{
floor->setScale(vector3df(200,1,200));
floor->setPosition(vector3df(0,-10,0));
floor->setMaterialFlag(EMF_LIGHTING, true);
floor->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
floor->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
}
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(vector3df(0,20,-40));
camera->setTarget(vector3df(0,5,0));
device->getCursorControl()->setVisible(false);
// --- RTT setup ---
ITexture* rtt = driver->addRenderTargetTexture(dimension2d<u32>(128,128), "RTT_Scene");
if (!rtt) { device->drop(); return 1; }
ITexture* rttA = driver->addRenderTargetTexture(dimension2d<u32>(128,128), "RTT_A");
ITexture* rttB = driver->addRenderTargetTexture(dimension2d<u32>(128,128), "RTT_B");
if (!rttA || !rttB) { device->drop(); return 1; }
S3DVertex vertices[4];
vertices[0] = S3DVertex(-1, 1,0, 0,0,1, SColor(255,255,255,255), 0,0);
vertices[1] = S3DVertex( 1, 1,0, 0,0,1, SColor(255,255,255,255), 1,0);
vertices[2] = S3DVertex( 1,-1,0, 0,0,1, SColor(255,255,255,255), 1,1);
vertices[3] = S3DVertex(-1,-1,0, 0,0,1, SColor(255,255,255,255), 0,1);
u16 indices[6] = {0,1,2, 0,2,3};
// --- Shaders ---
IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
if (!gpu) { device->drop(); return 1; }
io::IReadFile* file = device->getFileSystem()->createAndOpenFile("../../media/denoise_reduction.glsl");
if (!file) { device->drop(); return 1; }
irr::c8* buf = new irr::c8[file->getSize() + 1];
file->read(buf, file->getSize());
buf[file->getSize()] = 0;
file->drop();
io::path noiseFragStr = buf;
delete[] buf;
NoiseReductionCallback* noiseCB = new NoiseReductionCallback();
noiseCB->setSize(128, 128);
s32 noiseMatType = gpu->addHighLevelShaderMaterial(
FULLSCREEN_VERT_SHADER, noiseFragStr.c_str(),
noiseCB, EMT_SOLID, 0);
noiseCB->drop();
if (noiseMatType == -1) { device->drop(); return 1; }
SMaterial noiseMat;
noiseMat.Lighting = false;
noiseMat.ZBuffer = false;
noiseMat.ZWriteEnable = EZW_OFF;
noiseMat.MaterialType = (E_MATERIAL_TYPE)noiseMatType;
BlendCallback* blendCB = new BlendCallback();
blendCB->opacity = OPACITY;
s32 blendMatType = gpu->addHighLevelShaderMaterial(
FULLSCREEN_VERT_SHADER, BLEND_FRAG_SHADER,
blendCB, EMT_SOLID, 0);
blendCB->drop();
if (blendMatType == -1) { device->drop(); return 1; }
SMaterial blendMat;
blendMat.Lighting = false;
blendMat.ZBuffer = false;
blendMat.ZWriteEnable = EZW_OFF;
blendMat.MaterialType = (E_MATERIAL_TYPE)blendMatType;
// --- Initial shadow update (once) ---
for (size_t i = 0; i < shadowNodes.size(); ++i)
{
IShadowVolumeSceneNode* shadow = shadowNodes[i];
if (shadow)
{
shadow->setFreeze(ESF_RUN);
shadow->updateShadowVolumes();
shadow->setFreeze(ESF_FREEZE);
}
}
int lastFPS = -1;
while (device->run())
{
if (!device->isWindowActive()) continue;
u32 now = timer->getRealTime();
// --- Single timer for both animation and shadow updates (8 Hz) ---
if (now - lastUpdateTime >= updateIntervalMs)
{
u32 elapsedMs = now - lastUpdateTime;
lastUpdateTime = now;
// 1) Update animation frames
animationTime += elapsedMs / 1000.0f;
f32 totalFrames = animationTime * originalSpeed;
s32 frameCount = 0;
if (!animatedNodes.empty())
{
IAnimatedMesh* animMesh = animatedNodes[0]->getMesh();
if (animMesh)
{
frameCount = animatedNodes[0]->getEndFrame() - animatedNodes[0]->getStartFrame();
if (frameCount <= 0) frameCount = 40;
}
}
if (frameCount <= 0) frameCount = 40;
s32 frame = (s32)fmodf(totalFrames, (f32)frameCount);
frame = animatedNodes[0]->getStartFrame() + frame;
for (size_t i = 0; i < animatedNodes.size(); ++i)
{
IAnimatedMeshSceneNode* animNode = animatedNodes[i];
if (animNode)
animNode->setCurrentFrame((f32)frame);
}
// 2) Update shadow volumes (same interval)
for (size_t i = 0; i < shadowNodes.size(); ++i)
{
IShadowVolumeSceneNode* shadow = shadowNodes[i];
if (shadow)
{
shadow->setFreeze(ESF_RUN);
shadow->updateShadowVolumes();
shadow->setFreeze(ESF_FREEZE);
}
}
}
// --- Render to RTT and apply denoise ---
driver->setRenderTarget(rtt, ECBF_COLOR | ECBF_DEPTH, SColor(0,0,0,255));
smgr->drawAll();
driver->setRenderTarget(rttA, ECBF_COLOR | ECBF_DEPTH, SColor(0,0,0,255));
{
SMaterial copyMat;
copyMat.Lighting = false;
copyMat.ZBuffer = false;
copyMat.ZWriteEnable = EZW_OFF;
copyMat.setFlag(EMF_BILINEAR_FILTER, true);
copyMat.setTexture(0, rtt);
driver->setMaterial(copyMat);
driver->setTransform(ETS_WORLD, IdentityMatrix);
driver->setTransform(ETS_VIEW, IdentityMatrix);
driver->setTransform(ETS_PROJECTION, IdentityMatrix);
driver->drawIndexedTriangleList(vertices, 4, indices, 2);
}
for (int i = 0; i < STRENGTH; ++i)
{
ITexture* input = (i % 2 == 0) ? rttA : rttB;
ITexture* output = (i % 2 == 0) ? rttB : rttA;
driver->setRenderTarget(output, ECBF_COLOR | ECBF_DEPTH, SColor(0,0,0,255));
noiseMat.setTexture(0, input);
driver->setMaterial(noiseMat);
driver->setTransform(ETS_WORLD, IdentityMatrix);
driver->setTransform(ETS_VIEW, IdentityMatrix);
driver->setTransform(ETS_PROJECTION, IdentityMatrix);
driver->drawIndexedTriangleList(vertices, 4, indices, 2);
}
ITexture* filteredTex = (STRENGTH % 2 == 0) ? rttA : rttB;
if (STRENGTH == 0) filteredTex = rtt;
driver->setRenderTarget(0, ECBF_COLOR | ECBF_DEPTH, SColor(0,100,100,100));
driver->beginScene(false, false, SColor(0,0,0,0));
blendMat.setTexture(0, rtt);
blendMat.setTexture(1, filteredTex);
blendMat.setFlag(EMF_BILINEAR_FILTER, true);
driver->setMaterial(blendMat);
driver->setTransform(ETS_WORLD, IdentityMatrix);
driver->setTransform(ETS_VIEW, IdentityMatrix);
driver->setTransform(ETS_PROJECTION, IdentityMatrix);
driver->drawIndexedTriangleList(vertices, 4, indices, 2);
driver->endScene();
int fps = driver->getFPS();
if (fps != lastFPS)
{
stringw str = L"RTT + Denoise (";
str += STRENGTH;
str += L" passes) FPS: ";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
denoise_reduction.hpp
Code: Select all
// denoise_reduction.hpp
#ifndef DENOISE_REDUCTION_HPP
#define DENOISE_REDUCTION_HPP
#include <irrlicht.h>
// Vertex shader with Y correction for RTT in OpenGL
static const char* FULLSCREEN_VERT_SHADER =
"#version 120\n"
"void main() {\n"
" gl_Position = ftransform();\n"
" gl_TexCoord[0] = vec4(gl_MultiTexCoord0.x, 1.0 - gl_MultiTexCoord0.y, 0.0, 1.0);\n"
"}";
// Final blend fragment shader
static const char* BLEND_FRAG_SHADER =
"#version 120\n"
"uniform sampler2D texOriginal;\n"
"uniform sampler2D texFiltered;\n"
"uniform float opacity;\n"
"void main() {\n"
" vec2 uv = gl_TexCoord[0].st;\n"
" vec3 orig = texture2D(texOriginal, uv).rgb;\n"
" vec3 filt = texture2D(texFiltered, uv).rgb;\n"
" vec3 mixed = mix(orig, filt, opacity);\n"
" gl_FragColor = vec4(mixed, 1.0);\n"
"}";
// Callback for noise reduction shader
struct NoiseReductionCallback : public irr::video::IShaderConstantSetCallBack
{
NoiseReductionCallback() : texelSizeID(-1), width(0), height(0) {}
void setSize(int w, int h)
{
width = w;
height = h;
}
virtual void OnCreate(irr::video::IMaterialRendererServices* services, irr::s32 userData)
{
texelSizeID = services->getPixelShaderConstantID("texelSize");
}
virtual void OnSetConstants(irr::video::IMaterialRendererServices* services, irr::s32 userData)
{
if (texelSizeID == -1) return;
irr::f32 ts[2] = { 1.0f / width, 1.0f / height };
services->setPixelShaderConstant(texelSizeID, ts, 2);
}
virtual void OnSetMaterial(const irr::video::SMaterial& material) {}
private:
irr::s32 texelSizeID;
int width, height;
};
// Callback for final blending (assigns texture units)
struct BlendCallback : public irr::video::IShaderConstantSetCallBack
{
BlendCallback() : opacity(1.0f), opacityID(-1), texOriginalID(-1), texFilteredID(-1) {}
irr::f32 opacity;
virtual void OnCreate(irr::video::IMaterialRendererServices* services, irr::s32 userData)
{
opacityID = services->getPixelShaderConstantID("opacity");
texOriginalID = services->getPixelShaderConstantID("texOriginal");
texFilteredID = services->getPixelShaderConstantID("texFiltered");
}
virtual void OnSetConstants(irr::video::IMaterialRendererServices* services, irr::s32 userData)
{
services->setPixelShaderConstant(opacityID, &opacity, 1);
// Assign texture units
irr::s32 unit0 = 0;
irr::s32 unit1 = 1;
services->setPixelShaderConstant(texOriginalID, &unit0, 1);
services->setPixelShaderConstant(texFilteredID, &unit1, 1);
}
virtual void OnSetMaterial(const irr::video::SMaterial& material) {}
private:
irr::s32 opacityID;
irr::s32 texOriginalID;
irr::s32 texFilteredID;
};
#endif
Code: Select all
#version 120
uniform sampler2D tex;
uniform vec2 texelSize;
void main()
{
vec2 uv = gl_TexCoord[0].st;
vec3 c = texture2D(tex, uv).rgb;
vec3 n0 = texture2D(tex, uv + vec2(-texelSize.x, -texelSize.y)).rgb;
vec3 n1 = texture2D(tex, uv + vec2( 0.0, -texelSize.y)).rgb;
vec3 n2 = texture2D(tex, uv + vec2( texelSize.x, -texelSize.y)).rgb;
vec3 n3 = texture2D(tex, uv + vec2(-texelSize.x, 0.0)).rgb;
vec3 n4 = texture2D(tex, uv + vec2( texelSize.x, 0.0)).rgb;
vec3 n5 = texture2D(tex, uv + vec2(-texelSize.x, texelSize.y)).rgb;
vec3 n6 = texture2D(tex, uv + vec2( 0.0, texelSize.y)).rgb;
vec3 n7 = texture2D(tex, uv + vec2( texelSize.x, texelSize.y)).rgb;
vec3 ref0 = 2.0*c - n0 - n7; vec3 metric0 = ref0 * ref0;
vec3 ref1 = 2.0*c - n1 - n6; vec3 metric1 = ref1 * ref1;
vec3 ref2 = 2.0*c - n2 - n5; vec3 metric2 = ref2 * ref2;
vec3 ref3 = 2.0*c - n3 - n4; vec3 metric3 = ref3 * ref3;
vec3 sum = c;
float count = 1.0;
// Neighbor 0
{
vec3 v = (c + n0) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 1
{
vec3 v = (c + n1) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 2
{
vec3 v = (c + n2) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 3
{
vec3 v = (c + n3) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 4
{
vec3 v = (c + n4) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 5
{
vec3 v = (c + n5) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 6
{
vec3 v = (c + n6) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
// Neighbor 7
{
vec3 v = (c + n7) * 0.5;
vec3 d0 = 2.0*v - n0 - n7; bvec3 p0 = lessThanEqual(d0*d0, metric0);
vec3 d1 = 2.0*v - n1 - n6; bvec3 p1 = lessThanEqual(d1*d1, metric1);
vec3 d2 = 2.0*v - n2 - n5; bvec3 p2 = lessThanEqual(d2*d2, metric2);
vec3 d3 = 2.0*v - n3 - n4; bvec3 p3 = lessThanEqual(d3*d3, metric3);
if (all(p0) && all(p1) && all(p2) && all(p3)) { sum += v; count += 1.0; }
}
vec3 result = sum / count;
gl_FragColor = vec4(result, 1.0);
}