Transition texture shader

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Noiecity
Posts: 410
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Transition texture shader

Post by Noiecity »

Image

Blend with 2 textures, shader model 3.0, d3d9, c++98, irrlicht 1.9.0:

Code: Select all

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <irrlicht.h>

#pragma comment(lib, "Irrlicht.lib")
#pragma comment(lib, "winmm.lib")

using namespace irr;

namespace
{
    const u32 TRANSITION_DURATION_MS = 1000;

    const c8* CROSSFADE_VS =
        "float4x4 WorldMatrix;                       \n"
        "float4x4 ViewMatrix;                        \n"
        "float4x4 ProjectionMatrix;                  \n"
        "                                             \n"
        "struct VS_INPUT                             \n"
        "{                                            \n"
        "    float4 Position : POSITION0;             \n"
        "    float2 TexCoord : TEXCOORD0;             \n"
        "};                                           \n"
        "                                             \n"
        "struct VS_OUTPUT                            \n"
        "{                                            \n"
        "    float4 Position : POSITION0;             \n"
        "    float2 TexCoordA : TEXCOORD0;            \n"
        "    float2 TexCoordB : TEXCOORD1;            \n"
        "};                                           \n"
        "                                             \n"
        "VS_OUTPUT vertexMain(VS_INPUT input)         \n"
        "{                                            \n"
        "    VS_OUTPUT output;                        \n"
        "                                             \n"
        "    float4 worldPosition =                   \n"
        "        mul(input.Position, WorldMatrix);    \n"
        "                                             \n"
        "    float4 viewPosition =                    \n"
        "        mul(worldPosition, ViewMatrix);      \n"
        "                                             \n"
        "    output.Position =                        \n"
        "        mul(viewPosition, ProjectionMatrix); \n"
        "                                             \n"
        "    output.TexCoordA = input.TexCoord;       \n"
        "    output.TexCoordB = input.TexCoord;       \n"
        "                                             \n"
        "    return output;                           \n"
        "}                                            \n";

    const c8* CROSSFADE_PS =
        "sampler2D SamplerA : register(s0);           \n"
        "sampler2D SamplerB : register(s1);           \n"
        "                                             \n"
        "float CrossfadeFactor;                       \n"
        "                                             \n"
        "float4 pixelMain(                            \n"
        "    float2 texCoordA : TEXCOORD0,            \n"
        "    float2 texCoordB : TEXCOORD1) : COLOR0   \n"
        "{                                            \n"
        "    float4 colorA =                          \n"
        "        tex2D(SamplerA, texCoordA);          \n"
        "                                             \n"
        "    float4 colorB =                          \n"
        "        tex2D(SamplerB, texCoordB);          \n"
        "                                             \n"
        "    return lerp(                             \n"
        "        colorA,                              \n"
        "        colorB,                              \n"
        "        saturate(CrossfadeFactor));          \n"
        "}                                            \n";

    class CrossfadeShaderCallback : public video::IShaderConstantSetCallBack
    {
    public:
        explicit CrossfadeShaderCallback(video::IVideoDriver* driver)
            : Driver(driver),
            Factor(0.0f)
        {}

        void OnSetConstants(
            video::IMaterialRendererServices* services,
            s32 userData)
        {
            if (!services || !Driver)
                return;

            const core::matrix4& world =
                Driver->getTransform(video::ETS_WORLD);

            const core::matrix4& view =
                Driver->getTransform(video::ETS_VIEW);

            const core::matrix4& projection =
                Driver->getTransform(video::ETS_PROJECTION);

            services->setVertexShaderConstant(
                "WorldMatrix",
                world.pointer(),
                16);

            services->setVertexShaderConstant(
                "ViewMatrix",
                view.pointer(),
                16);

            services->setVertexShaderConstant(
                "ProjectionMatrix",
                projection.pointer(),
                16);

            services->setPixelShaderConstant(
                "CrossfadeFactor",
                &Factor,
                1);
        }

        void setFactor(f32 factor)
        {
            Factor = core::clamp(factor, 0.0f, 1.0f);
        }

    private:
        video::IVideoDriver* Driver;
        f32 Factor;
    };

    s32 CrossfadeMaterialType = -1;
    CrossfadeShaderCallback* ShaderCallback = 0;

    bool createCrossfadeMaterial(video::IVideoDriver* driver)
    {
        if (!driver)
            return false;

        video::IGPUProgrammingServices* gpu =
            driver->getGPUProgrammingServices();

        if (!gpu)
            return false;

        ShaderCallback = new CrossfadeShaderCallback(driver);

        CrossfadeMaterialType =
            gpu->addHighLevelShaderMaterial(
                CROSSFADE_VS,
                "vertexMain",
                video::EVST_VS_3_0,

                CROSSFADE_PS,
                "pixelMain",
                video::EPST_PS_3_0,

                ShaderCallback,
                video::EMT_SOLID,
                0);

        if (CrossfadeMaterialType < 0)
        {
            ShaderCallback->drop();
            ShaderCallback = 0;
            return false;
        }

        return true;
    }

    bool drawMeshCrossfade(
        video::IVideoDriver* driver,
        scene::ISceneManager* sceneManager,
        scene::IMesh* mesh,
        video::ITexture* textureA,
        video::ITexture* textureB,
        f32 factor,
        const core::matrix4& worldMatrix)
    {
        if (!driver ||
            !sceneManager ||
            !mesh ||
            !textureA ||
            !textureB ||
            !ShaderCallback ||
            CrossfadeMaterialType < 0)
        {
            return false;
        }

        scene::ICameraSceneNode* camera =
            sceneManager->getActiveCamera();

        if (!camera)
            return false;

        ShaderCallback->setFactor(factor);

        driver->setTransform(
            video::ETS_WORLD,
            worldMatrix);

        driver->setTransform(
            video::ETS_VIEW,
            camera->getViewMatrix());

        driver->setTransform(
            video::ETS_PROJECTION,
            camera->getProjectionMatrix());

        video::SMaterial material;
        material.MaterialType =
            static_cast<video::E_MATERIAL_TYPE>(
                CrossfadeMaterialType);

        material.Lighting = false;
        material.ZWriteEnable = video::EZW_ON;
        material.ZBuffer = video::ECFN_LESSEQUAL;
        material.BackfaceCulling = true;
        material.Wireframe = false;

        material.setTexture(0, textureA);
        material.setTexture(1, textureB);

        material.TextureLayer[0].TextureWrapU =
            video::ETC_CLAMP;

        material.TextureLayer[0].TextureWrapV =
            video::ETC_CLAMP;

        material.TextureLayer[1].TextureWrapU =
            video::ETC_CLAMP;

        material.TextureLayer[1].TextureWrapV =
            video::ETC_CLAMP;

        driver->setMaterial(material);

        const u32 bufferCount =
            mesh->getMeshBufferCount();

        for (u32 i = 0; i < bufferCount; ++i)
        {
            scene::IMeshBuffer* meshBuffer =
                mesh->getMeshBuffer(i);

            if (meshBuffer)
                driver->drawMeshBuffer(meshBuffer);
        }

        return true;
    }

    class EventReceiver : public IEventReceiver
    {
    public:
        EventReceiver()
        {
            for (u32 i = 0; i < KEY_KEY_CODES_COUNT; ++i)
            {
                KeyDown[i] = false;
                PreviousKeyDown[i] = false;
            }
        }

        bool OnEvent(const SEvent& event)
        {
            if (event.EventType != EET_KEY_INPUT_EVENT)
                return false;

            const EKEY_CODE key =
                event.KeyInput.Key;

            if (key < 0 || key >= KEY_KEY_CODES_COUNT)
                return false;

            KeyDown[key] =
                event.KeyInput.PressedDown;

            return false;
        }

        bool isKeyDown(EKEY_CODE key) const
        {
            if (key < 0 || key >= KEY_KEY_CODES_COUNT)
                return false;

            return KeyDown[key];
        }

        bool wasKeyPressed(EKEY_CODE key)
        {
            if (key < 0 || key >= KEY_KEY_CODES_COUNT)
                return false;

            const bool currentlyDown =
                KeyDown[key];

            const bool pressed =
                currentlyDown &&
                !PreviousKeyDown[key];

            PreviousKeyDown[key] =
                currentlyDown;

            return pressed;
        }

    private:
        bool KeyDown[KEY_KEY_CODES_COUNT];
        bool PreviousKeyDown[KEY_KEY_CODES_COUNT];
    };
}

int main()
{
    EventReceiver receiver;

    SIrrlichtCreationParameters params;
    params.DriverType = video::EDT_DIRECT3D9;
    params.WindowSize = core::dimension2du(1280, 720);
    params.Bits = 32;
    params.Fullscreen = false;
    params.Stencilbuffer = true;
    params.Vsync = true;
    params.AntiAlias = 0;
    params.EventReceiver = &receiver;

    IrrlichtDevice* device =
        createDeviceEx(params);

    if (!device)
    {
        MessageBoxA(
            0,
            "Could not create Irrlicht with Direct3D 9.",
            "Error",
            MB_OK | MB_ICONERROR);

        return 1;
    }

    device->setWindowCaption(
        L"Irrlicht - Crossfade Shader Model 3.0 (Infinite Loop)");

    video::IVideoDriver* driver =
        device->getVideoDriver();

    scene::ISceneManager* sceneManager =
        device->getSceneManager();

    gui::IGUIEnvironment* gui =
        device->getGUIEnvironment();

    if (!driver || !sceneManager || !gui)
    {
        device->drop();
        return 1;
    }

    if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_3_0) ||
        !driver->queryFeature(video::EVDF_PIXEL_SHADER_3_0))
    {
        MessageBoxA(
            0,
            "GPU does not support Shader Model 3.0.\n"
            "Vertex Shader 3.0 and Pixel Shader 3.0 are required.",
            "Incompatible Hardware",
            MB_OK | MB_ICONERROR);

        device->drop();
        return 1;
    }

    if (!createCrossfadeMaterial(driver))
    {
        MessageBoxA(
            0,
            "Could not compile or create the Shader Model 3.0 material.",
            "Shader Error",
            MB_OK | MB_ICONERROR);

        device->drop();
        return 1;
    }

    video::ITexture* textureA =
        driver->getTexture("../../media/particle.bmp");

    video::ITexture* textureB =
        driver->getTexture("../../media/particlegreen.jpg");

    if (!textureA || !textureB)
    {
        MessageBoxA(
            0,
            "Could not find particle.bmp and/or particlegreen.jpg.",
            "Missing Textures",
            MB_OK | MB_ICONERROR);

        ShaderCallback->drop();
        ShaderCallback = 0;

        device->drop();
        return 1;
    }

    const scene::IGeometryCreator* geometryCreator =
        sceneManager->getGeometryCreator();

    scene::IMesh* mesh =
        geometryCreator->createCubeMesh(
            core::vector3df(15.0f, 15.0f, 15.0f));

    if (!mesh)
    {
        ShaderCallback->drop();
        ShaderCallback = 0;

        device->drop();
        return 1;
    }

    scene::ICameraSceneNode* camera =
        sceneManager->addCameraSceneNode(
            0,
            core::vector3df(0.0f, 20.0f, -70.0f),
            core::vector3df(0.0f, 0.0f, 0.0f));

    if (!camera)
    {
        mesh->drop();

        ShaderCallback->drop();
        ShaderCallback = 0;

        device->drop();
        return 1;
    }

    camera->setNearValue(0.1f);
    camera->setFarValue(1000.0f);
    camera->setFOV(53.13f * core::DEGTORAD);

    gui::IGUIFont* font =
        gui->getBuiltInFont();

    u32 transitionStart =
        device->getTimer()->getTime();

    f32 factor = 0.0f;
    bool goingForward = true;

    while (device->run())
    {
        if (!device->isWindowActive())
        {
            device->yield();
            continue;
        }

        if (receiver.wasKeyPressed(KEY_SPACE))
        {
            transitionStart =
                device->getTimer()->getTime();

            goingForward = true;
            factor = 0.0f;
        }

        const u32 now =
            device->getTimer()->getTime();

        const u32 elapsed =
            now - transitionStart;

        const f32 rawFactor =
            static_cast<f32>(elapsed) /
            static_cast<f32>(TRANSITION_DURATION_MS);

        if (goingForward)
        {
            factor = rawFactor;

            if (factor >= 1.0f)
            {
                factor = 1.0f;
                goingForward = false;
                transitionStart = now;
            }
        }
        else
        {
            factor = 1.0f - rawFactor;

            if (factor <= 0.0f)
            {
                factor = 0.0f;
                goingForward = true;
                transitionStart = now;
            }
        }

        const f32 scaleValue =
            1.0f +
            0.35f *
            static_cast<f32>(
                sin(static_cast<f64>(now) * 0.002));

        core::matrix4 scaleMatrix;
        scaleMatrix.setScale(
            core::vector3df(
                scaleValue,
                scaleValue,
                scaleValue));

        core::matrix4 rotationMatrix;
        rotationMatrix.setRotationDegrees(
            core::vector3df(
                static_cast<f32>(now) * 0.015f,
                static_cast<f32>(now) * 0.030f,
                0.0f));

        core::matrix4 translationMatrix;
        translationMatrix.setTranslation(
            core::vector3df(0.0f, 0.0f, 0.0f));

        core::matrix4 worldMatrix =
            scaleMatrix *
            rotationMatrix *
            translationMatrix;

        sceneManager->setActiveCamera(camera);
        camera->render();

        driver->beginScene(
            true,
            true,
            video::SColor(255, 35, 40, 50));

        const bool rendered =
            drawMeshCrossfade(
                driver,
                sceneManager,
                mesh,
                textureA,
                textureB,
                factor,
                worldMatrix);

        wchar_t status[512];

        swprintf(
            status,
            512,
            L"Crossfade: %.3f   Direction: %s   Scale: %.2f   [SPACE: restart] [ESC: exit]",
            factor,
            goingForward ? L"A->B" : L"B->A",
            scaleValue);

        if (font)
        {
            font->draw(
                status,
                core::rect<s32>(
                    20,
                    20,
                    1200,
                    60),

                rendered
                ? video::SColor(
                    255, 255, 255, 255)
                : video::SColor(
                    255, 255, 80, 80));
        }

        driver->endScene();

        if (receiver.isKeyDown(KEY_ESCAPE))
            device->closeDevice();

        Sleep(1);
    }

    mesh->drop();

    if (ShaderCallback)
    {
        ShaderCallback->drop();
        ShaderCallback = 0;
    }

    device->drop();
    return 0;
}
Irrlicht is love, Irrlicht is life, long live to Irrlicht
Post Reply