[Shader]Specular color + Gouraud + Normalmap

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: 324
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

[Shader]Specular color + Gouraud + Normalmap

Post by Noiecity »

Image

Code: Select all

#include <irrlicht.h>
#include <cmath>

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// --- HLSL Shaders ---
const char* vertexShaderCode = 
"struct VS_INPUT\n"
"{\n"
"    float4 Pos : POSITION;\n"
"    float3 Normal : NORMAL;\n"
"    float2 TexCoord : TEXCOORD0;\n"
"    float3 Tangent : TEXCOORD1;\n"
"    float3 Binormal : TEXCOORD2;\n"
"};\n"
"\n"
"struct VS_OUTPUT\n"
"{\n"
"    float4 Pos : POSITION;\n"
"    float2 TexCoord0 : TEXCOORD0;\n"
"    float2 TexCoord1 : TEXCOORD1;\n"
"    float3 LightVec : TEXCOORD2;\n"
"    float3 ViewVec : TEXCOORD3;\n"
"    float3 GouraudColor : TEXCOORD4;  // NEW: Color for Gouraud shading\n"
"};\n"
"\n"
"float4x4 mWorldViewProj : register(c0);\n"
"float4x4 mWorld : register(c4);\n"
"float3 mLightPos : register(c8);\n"
"float3 mEyePos : register(c9);\n"
"float3 mLightColor : register(c10);  // NEW: Light color\n"
"float3 mAmbientColor : register(c11); // NEW: Ambient color\n"
"\n"
"VS_OUTPUT main(VS_INPUT input)\n"
"{\n"
"    VS_OUTPUT output;\n"
"    \n"
"    // Transform position to clip space\n"
"    output.Pos = mul(input.Pos, mWorldViewProj);\n"
"    output.TexCoord0 = input.TexCoord;\n"
"    output.TexCoord1 = input.TexCoord;\n"
"    \n"
"    // Calculate world position\n"
"    float4 worldPos = mul(input.Pos, mWorld);\n"
"    \n"
"    // Transform normal, tangent, binormal to world space\n"
"    float3 worldNormal = normalize(mul(input.Normal, (float3x3)mWorld));\n"
"    float3 worldTangent = normalize(mul(input.Tangent, (float3x3)mWorld));\n"
"    float3 worldBinormal = normalize(mul(input.Binormal, (float3x3)mWorld));\n"
"    \n"
"    // Create TBN matrix\n"
"    float3x3 TBN = float3x3(worldTangent, worldBinormal, worldNormal);\n"
"    \n"
"    // Calculate light and view vectors in world space\n"
"    float3 lightVec = mLightPos - worldPos.xyz;\n"
"    float3 viewVec = mEyePos - worldPos.xyz;\n"
"    \n"
"    // Transform to tangent space\n"
"    output.LightVec = mul(lightVec, TBN);\n"
"    output.ViewVec = mul(viewVec, TBN);\n"
"    \n"
"    // --- GOURAUD SHADING CALCULATION ---\n"
"    // Normalize light vector for Gouraud\n"
"    float3 normalizedLightVec = normalize(lightVec);\n"
"    \n"
"    // Calculate diffuse component (Gouraud)\n"
"    float NdotL = max(dot(worldNormal, normalizedLightVec), 0.0);\n"
"    float3 diffuseGouraud = NdotL * mLightColor;\n"
"    \n"
"    // Calculate specular component (Gouraud - Blinn-Phong)\n"
"    float3 viewDir = normalize(mEyePos - worldPos.xyz);\n"
"    float3 halfDir = normalize(normalizedLightVec + viewDir);\n"
"    float NdotH = max(dot(worldNormal, halfDir), 0.0);\n"
"    float specularGouraud = pow(NdotH, 16.0) * 0.5; // Base values for Gouraud\n"
"    \n"
"    // Combine Gouraud lighting\n"
"    output.GouraudColor = mAmbientColor + diffuseGouraud + specularGouraud;\n"
"    // --- END GOURAUD SHADING ---\n"
"    \n"
"    return output;\n"
"}\n";

const char* pixelShaderCode =
"struct PS_INPUT\n"
"{\n"
"    float2 TexCoord0 : TEXCOORD0;\n"
"    float2 TexCoord1 : TEXCOORD1;\n"
"    float3 LightVec : TEXCOORD2;\n"
"    float3 ViewVec : TEXCOORD3;\n"
"    float3 GouraudColor : TEXCOORD4;  // NEW: Receives Gouraud color\n"
"};\n"
"\n"
"sampler2D DiffuseMap : register(s0);\n"
"sampler2D NormalMap : register(s1);\n"
"sampler2D SpecularIntensityMap : register(s2);\n"
"sampler2D SpecularColorMap : register(s3);\n"
"\n"
"float4 mSpecularColor : register(c0);\n"
"float mSpecularIntensity : register(c1);\n"
"float mSpecularHardness : register(c2);\n"
"float mGouraudMix : register(c3);  // NEW: Mix between Gouraud and Phong\n"
"\n"
"float4 main(PS_INPUT input) : COLOR0\n"
"{\n"
"    // Sample textures\n"
"    float4 diffuse = tex2D(DiffuseMap, input.TexCoord0);\n"
"    float3 normalMap = tex2D(NormalMap, input.TexCoord0).rgb;\n"
"    float specIntensity = tex2D(SpecularIntensityMap, input.TexCoord0).r;\n"
"    float3 specColor = tex2D(SpecularColorMap, input.TexCoord1).rgb;\n"
"    \n"
"    // Unpack normal from normal map\n"
"    float3 normal = normalize(normalMap * 2.0 - 1.0);\n"
"    \n"
"    // Normalize vectors\n"
"    float3 lightVec = normalize(input.LightVec);\n"
"    float3 viewVec = normalize(input.ViewVec);\n"
"    \n"
"    // Diffuse lighting (Phong - per pixel)\n"
"    float NdotL = max(dot(normal, lightVec), 0.0);\n"
"    float3 diffuseColorPhong = diffuse.rgb * NdotL;\n"
"    \n"
"    // Specular lighting (Blinn-Phong) with hardness\n"
"    float3 halfVec = normalize(lightVec + viewVec);\n"
"    float NdotH = max(dot(normal, halfVec), 0.0);\n"
"    \n"
"    // Apply hardness: high values = small specular, low values = large specular\n"
"    float specular = pow(NdotH, mSpecularHardness) * mSpecularIntensity * specIntensity;\n"
"    \n"
"    // MIX: Combine Gouraud and Phong\n"
"    float3 specularColor = mSpecularColor.rgb * specular * specColor;\n"
"    \n"
"    // IMPROVEMENT: Add minimum ambient lighting\n"
"    float3 ambient = diffuse.rgb * 0.1;\n"
"    \n"
"    // --- COMBINE GOURAUD AND PHONG ---\n"
"    // Complete Phong (diffuse + specular)\n"
"    float3 phongColor = ambient + diffuseColorPhong + specularColor;\n"
"    \n"
"    // Gouraud (already calculated in vertex shader) with additional specular\n"
"    float3 gouraudColor = input.GouraudColor * diffuse.rgb + specularColor * 0.5;\n"
"    \n"
"    // Mix according to mGouraudMix parameter (0 = Phong only, 1 = Gouraud only)\n"
"    float3 finalColor = lerp(phongColor, gouraudColor, mGouraudMix);\n"
"    \n"
"    return float4(finalColor, diffuse.a);\n"
"}\n";

class MyShaderCallBack : public IShaderConstantSetCallBack
{
public:
    MyShaderCallBack(ILightSceneNode* lightNode, ISceneManager* smgr)
        : LightNode(lightNode), Smgr(smgr), 
          WorldViewProjID(-1), WorldID(-1), LightPosID(-1), EyePosID(-1),
          SpecularColorID(-1), SpecularIntensityID(-1), SpecularHardnessID(-1),
          LightColorID(-1), AmbientColorID(-1), GouraudMixID(-1), // NEW IDs
          SpecularIntensity(2.0f), SpecularHardness(16.0f),
          GouraudMix(0.0f) // NEW: Initial mix (0 = Phong only)
    {
    }

    virtual void OnCreate(IMaterialRendererServices* services, s32 userData)
    {
        // Get constant IDs
        WorldViewProjID = services->getVertexShaderConstantID("mWorldViewProj");
        WorldID = services->getVertexShaderConstantID("mWorld");
        LightPosID = services->getVertexShaderConstantID("mLightPos");
        EyePosID = services->getVertexShaderConstantID("mEyePos");
        LightColorID = services->getVertexShaderConstantID("mLightColor"); // NEW
        AmbientColorID = services->getVertexShaderConstantID("mAmbientColor"); // NEW
        
        SpecularColorID = services->getPixelShaderConstantID("mSpecularColor");
        SpecularIntensityID = services->getPixelShaderConstantID("mSpecularIntensity");
        SpecularHardnessID = services->getPixelShaderConstantID("mSpecularHardness");
        GouraudMixID = services->getPixelShaderConstantID("mGouraudMix"); // NEW
        
        printf("Shader CallBack created - IDs obtained\n");
    }

    virtual void OnSetConstants(IMaterialRendererServices* services, s32 userData)
    {
        IVideoDriver* driver = services->getVideoDriver();
        
        // Set matrices
        matrix4 worldViewProj = driver->getTransform(ETS_PROJECTION);
        worldViewProj *= driver->getTransform(ETS_VIEW);
        worldViewProj *= driver->getTransform(ETS_WORLD);
        services->setVertexShaderConstant(WorldViewProjID, worldViewProj.pointer(), 16);
        
        matrix4 world = driver->getTransform(ETS_WORLD);
        services->setVertexShaderConstant(WorldID, world.pointer(), 16);
        
        // Light position
        if (LightNode)
        {
            vector3df lightPos = LightNode->getAbsolutePosition();
            services->setVertexShaderConstant(LightPosID, reinterpret_cast<const f32*>(&lightPos), 3);
            
            // NEW: Light color
            SColorf lightColor = LightNode->getLightData().DiffuseColor;
            f32 lightColorArray[] = {lightColor.r, lightColor.g, lightColor.b};
            services->setVertexShaderConstant(LightColorID, lightColorArray, 3);
        }
        
        // Eye position
        if (ICameraSceneNode* camera = Smgr->getActiveCamera())
        {
            vector3df eyePos = camera->getAbsolutePosition();
            services->setVertexShaderConstant(EyePosID, reinterpret_cast<const f32*>(&eyePos), 3);
        }
        
        // NEW: Ambient color
        f32 ambientColor[] = {0.1f, 0.1f, 0.1f}; // Base ambient color
        services->setVertexShaderConstant(AmbientColorID, ambientColor, 3);
        
        // Pixel shader constants
        f32 specularColor[] = {1.5f, 1.5f, 1.5f, 1.0f};
        services->setPixelShaderConstant(SpecularColorID, specularColor, 4);
        services->setPixelShaderConstant(SpecularIntensityID, &SpecularIntensity, 1);
        services->setPixelShaderConstant(SpecularHardnessID, &SpecularHardness, 1);
        
        // NEW: Gouraud/Phong mix
        services->setPixelShaderConstant(GouraudMixID, &GouraudMix, 1);
        
        // Debug: Show current values
        static int frameCount = 0;
        if (frameCount % 60 == 0) {
            printf("Shader Constants - Intensity: %.2f, Hardness: %.2f, GouraudMix: %.2f\n", 
                   SpecularIntensity, SpecularHardness, GouraudMix);
        }
        frameCount++;
    }

    void setSpecularIntensity(f32 intensity) { 
        SpecularIntensity = intensity; 
        printf("Specular intensity changed to: %.2f\n", intensity);
    }
    
    void setSpecularHardness(f32 hardness) { 
        SpecularHardness = hardness; 
        printf("Hardness changed to: %.2f\n", hardness);
    }
    
    // NEW: Gouraud mix control
    void setGouraudMix(f32 mix) { 
        GouraudMix = core::clamp(mix, 0.0f, 1.0f);
        printf("Gouraud mix changed to: %.2f\n", GouraudMix);
    }

private:
    ILightSceneNode* LightNode;
    ISceneManager* Smgr;
    
    // Constant IDs
    s32 WorldViewProjID;
    s32 WorldID;
    s32 LightPosID;
    s32 EyePosID;
    s32 LightColorID;    // NEW
    s32 AmbientColorID;  // NEW
    s32 SpecularColorID;
    s32 SpecularIntensityID;
    s32 SpecularHardnessID;
    s32 GouraudMixID;    // NEW
    
    f32 SpecularIntensity;
    f32 SpecularHardness;
    f32 GouraudMix;      // NEW
};

class LightAnimator : public ISceneNodeAnimator
{
public:
    LightAnimator(f32 radius, f32 speed) : Radius(radius), Speed(speed), Time(0.0f), LastTime(0) {}

    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {
        if (!node) return;

        f32 delta = 0.0f;
        if (LastTime != 0) 
            delta = (timeMs - LastTime) * 0.001f;
        LastTime = timeMs;

        Time += delta * Speed;
        if (Time > 2.0f * 3.14159f) 
            Time -= 2.0f * 3.14159f;

        f32 x = cosf(Time) * Radius;
        f32 z = sinf(Time) * Radius;
        node->setPosition(vector3df(x, 5.0f, z));
    }

    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager = 0)
    {
        return new LightAnimator(Radius, Speed);
    }

private:
    f32 Radius;
    f32 Speed;
    f32 Time;
    u32 LastTime;
};

class SimpleUI
{
public:
    SimpleUI(IrrlichtDevice* device, MyShaderCallBack* shaderCallback)
        : Device(device), Callback(shaderCallback), 
          SpecularIntensity(2.0f), SpecularHardness(16.0f), GouraudMix(0.0f)
    {
        GuiEnv = device->getGUIEnvironment();
        createUI();
    }

    void createUI()
    {
        IGUIWindow* window = GuiEnv->addWindow(rect<s32>(10, 250, 350, 580), false, L"Shader Controls - DEBUG");

        // Specular intensity control - EXPANDED RANGE
        GuiEnv->addStaticText(L"Specular Intensity (0-5):", rect<s32>(10, 30, 200, 50), false, false, window);
        IGUIScrollBar* intensityScroll = GuiEnv->addScrollBar(true, rect<s32>(10, 50, 250, 70), window, 100);
        intensityScroll->setMax(250); // 0-250 -> 0-5.0
        intensityScroll->setPos(100); // 100/50 = 2.0

        // Hardness control - EXPANDED RANGE
        GuiEnv->addStaticText(L"Hardness (1-200):", rect<s32>(10, 80, 150, 100), false, false, window);
        IGUIScrollBar* hardnessScroll = GuiEnv->addScrollBar(true, rect<s32>(10, 100, 250, 120), window, 102);
        hardnessScroll->setMax(200);
        hardnessScroll->setPos(16); // Initial value 16

        // NEW: Gouraud mix control
        GuiEnv->addStaticText(L"Gouraud Mix (0-1):", rect<s32>(10, 130, 200, 150), false, false, window);
        IGUIScrollBar* gouraudScroll = GuiEnv->addScrollBar(true, rect<s32>(10, 150, 250, 170), window, 103);
        gouraudScroll->setMax(100); // 0-100 -> 0.0-1.0
        gouraudScroll->setPos(0); // Initial value 0

        const wchar_t* controlsText = 
            L"DEBUG MODE:\n"
            L"Current values:\n"
            L"- Intensity: 2.0\n"
            L"- Hardness: 16\n"
            L"- Gouraud Mix: 0.0\n\n"
            L"Gouraud Mix:\n"
            L"0.0 = Phong only (pixel)\n"
            L"1.0 = Gouraud only (vertex)\n\n"
            L"Loaded textures:\n"
            L"✓ Albedo\n"
            L"✓ NormalMap\n"
            L"✓ Specular Intensity\n"
            L"✓ Specular Color\n\n"
            L"CONTROLS:\n"
            L"TAB: Toggle between\n"
            L"movement and UI";

        GuiEnv->addStaticText(controlsText, rect<s32>(10, 180, 330, 410), false, true, window);

        // Current value labels
        IntensityText = GuiEnv->addStaticText(L"Intensity: 2.00", rect<s32>(260, 50, 330, 70), false, false, window);
        HardnessText = GuiEnv->addStaticText(L"Hardness: 16", rect<s32>(260, 100, 330, 120), false, false, window);
        GouraudText = GuiEnv->addStaticText(L"Gouraud: 0.00", rect<s32>(260, 150, 330, 170), false, false, window); // NEW
        
        // Mode indicator
        ModeText = GuiEnv->addStaticText(L"Mode: CAMERA", rect<s32>(10, 430, 200, 450), false, false, window);
    }

    bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_GUI_EVENT)
        {
            s32 id = event.GUIEvent.Caller->getID();
            
            if (event.GUIEvent.EventType == EGET_SCROLL_BAR_CHANGED)
            {
                IGUIScrollBar* scroll = (IGUIScrollBar*)event.GUIEvent.Caller;

                if (id == 100) // Intensity - range 0-5
                {
                    SpecularIntensity = (f32)scroll->getPos() / 50.0f;
                    Callback->setSpecularIntensity(SpecularIntensity);
                    
                    core::stringw text = L"Intensity: ";
                    text += core::stringw(SpecularIntensity);
                    IntensityText->setText(text.c_str());
                    return true;
                }
                else if (id == 102) // Hardness - range 1-200
                {
                    SpecularHardness = 1.0f + (f32)scroll->getPos();
                    Callback->setSpecularHardness(SpecularHardness);
                    
                    core::stringw text = L"Hardness: ";
                    text += core::stringw((int)SpecularHardness);
                    HardnessText->setText(text.c_str());
                    return true;
                }
                else if (id == 103) // NEW: Gouraud Mix - range 0-1
                {
                    GouraudMix = (f32)scroll->getPos() / 100.0f;
                    Callback->setGouraudMix(GouraudMix);
                    
                    core::stringw text = L"Gouraud: ";
                    GouraudText->setText(text.c_str());
                    return true;
                }
            }
        }
        return false;
    }
    
    void setUIMode(bool uiMode) {
        if (ModeText) {
            if (uiMode) {
                ModeText->setText(L"Mode: UI (TAB to switch)");
            } else {
                ModeText->setText(L"Mode: CAMERA (TAB to switch)");
            }
        }
    }

private:
    IrrlichtDevice* Device;
    IGUIEnvironment* GuiEnv;
    MyShaderCallBack* Callback;
    f32 SpecularIntensity;
    f32 SpecularHardness;
    f32 GouraudMix; // NEW
    IGUIStaticText* IntensityText;
    IGUIStaticText* HardnessText;
    IGUIStaticText* GouraudText; // NEW
    IGUIStaticText* ModeText;
};

// Event Receiver to handle mode switching
class MainEventReceiver : public IEventReceiver
{
public:
    MainEventReceiver(SimpleUI* ui, ICameraSceneNode* camera, IrrlichtDevice* device)
        : UI(ui), Camera(camera), Device(device), UIMode(false)
    {
        // Initially in camera mode
        updateCursorState();
        if (UI) {
            UI->setUIMode(UIMode);
        }
    }

    virtual bool OnEvent(const SEvent& event)
    {
        // Switch mode with TAB
        if (event.EventType == EET_KEY_INPUT_EVENT && 
            event.KeyInput.Key == KEY_TAB && 
            event.KeyInput.PressedDown)
        {
            UIMode = !UIMode;
            updateCursorState();
            if (UI) {
                UI->setUIMode(UIMode);
            }
            return true;
        }

        // If we're in UI mode, process UI events
        if (UIMode && UI) {
            return UI->OnEvent(event);
        }

        return false;
    }

    void updateCursorState()
    {
        if (Device) {
            Device->getCursorControl()->setVisible(UIMode);
            if (Camera) {
                // In camera mode, disable cursor and enable camera input
                Camera->setInputReceiverEnabled(!UIMode);
            }
        }
    }

    bool isUIMode() const { return UIMode; }

private:
    SimpleUI* UI;
    ICameraSceneNode* Camera;
    IrrlichtDevice* Device;
    bool UIMode;
};

int main()
{
    IrrlichtDevice* device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(1024, 768),
        16, false, false, false, 0);

    if (!device) 
        return 1;

    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
    device->setWindowCaption(L"Stone Wall Test - Gouraud Shading Added (TAB to switch mode)");
	
    driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY, true);
    
    // Camera - closer to see details better
    ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.003f);
    camera->setPosition(vector3df(0, 3, -8));
    camera->setTarget(vector3df(0, 2, 0));
    device->getCursorControl()->setVisible(false);
	camera->setFOV(90.0f* core::DEGTORAD);
    
    // Light - brighter and closer
    ILightSceneNode* light = smgr->addLightSceneNode(0, vector3df(3, 6, 0),
        SColorf(1.2f, 1.2f, 1.2f, 1.0f), 30.0f); // Brighter

    LightAnimator* lightAnimator = new LightAnimator(6.0f, 0.3f); // Slower for debug
    light->addAnimator(lightAnimator);
    lightAnimator->drop();

    // Shader callback
    MyShaderCallBack* shaderCallback = new MyShaderCallBack(light, smgr);

    // Create shader material
    IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
    s32 materialType = EMT_SOLID;
    
    if (gpu)
    {
        materialType = gpu->addHighLevelShaderMaterial(
            vertexShaderCode,
            "main",
            EVST_VS_2_0,
            pixelShaderCode,
            "main",
            EPST_PS_2_0,
            shaderCallback,
            EMT_SOLID,
            0
        );
        printf("Shader material created: %d\n", materialType);
    }

    // Load stonewalltest.obj model
    IAnimatedMesh* mesh = smgr->getMesh("../../media/stonewalltest.obj");
    IMeshSceneNode* node = 0;

    if (mesh)
    {
        printf("=== DEBUG: Model loading ===\n");
        printf("Model stonewalltest.obj loaded successfully\n");
        
        // Generate tangents for the model
        IMesh* tangentMesh = smgr->getMeshManipulator()->createMeshWithTangents(mesh->getMesh(0));
        
        if (tangentMesh)
        {
            printf("Tangents generated for the model\n");
            
            node = smgr->addMeshSceneNode(tangentMesh);
            if (node)
            {
                printf("Scene node created\n");
                
                node->setMaterialType((E_MATERIAL_TYPE)materialType);
                node->setMaterialFlag(EMF_LIGHTING, false);
                node->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
				node->setMaterialFlag(EMF_BILINEAR_FILTER, false);
                
                // Load specific textures for stonewall
                ITexture* albedo = driver->getTexture("../../media/PavingStones024_4K_Color.jpg");
                ITexture* normal = driver->getTexture("../../media/PavingStones024_4K_NormalDX2.jpg");
                ITexture* specIntensity = driver->getTexture("../../media/PavingStones024_4K_SPECULAR-I.jpg");
                ITexture* specColor = driver->getTexture("../../media/PavingStones024_4K_Specular-Color.jpg");

                // Assign textures
                node->setMaterialTexture(0, albedo);
                node->setMaterialTexture(1, normal);
                node->setMaterialTexture(2, specIntensity);
                node->setMaterialTexture(3, specColor);
                
                // Verify texture loading
                bool tex1 = (albedo != 0);
                bool tex2 = (normal != 0);
                bool tex3 = (specIntensity != 0);
                bool tex4 = (specColor != 0);
                
                printf("=== DEBUG: Textures ===\n");
                printf("Albedo: %s\n", tex1 ? "LOADED" : "FAILED");
                printf("Normal: %s\n", tex2 ? "LOADED" : "FAILED");
                printf("Spec Intensity: %s\n", tex3 ? "LOADED" : "FAILED");
                printf("Spec Color: %s\n", tex4 ? "LOADED" : "FAILED");
                
                if (!tex4) {
                    printf("ERROR: Specular Color not loaded! This is the main problem.\n");
                }
                
                // Adjust model scale and position
                node->setScale(vector3df(2.8f, 2.8f, 2.8f)); // Increased scale
                node->setPosition(vector3df(0, 0, 0));
                node->setRotation(vector3df(0, -90, 0));
                
                printf("Model configured\n");
            }
            
            tangentMesh->drop();
        }
    }
    else
    {
        printf("ERROR: Could not load model\n");
        return 1;
    }

    // Create UI
    SimpleUI ui(device, shaderCallback);

    // Main event receiver
    MainEventReceiver mainReceiver(&ui, camera, device);
    device->setEventReceiver(&mainReceiver);

    // Add informational text
    guienv->addStaticText(L"GOURAUD SHADING ADDED - TAB to switch between Camera/UI", 
                         rect<s32>(10, 10, 450, 30), true);

    printf("\n=== INSTRUCTIONS ===\n");
    printf("TAB: Toggle between Camera mode and UI mode\n");
    printf("Camera mode: WASD to move, Mouse to look\n");
    printf("UI mode: Use sliders to adjust values\n");
    printf("Gouraud Mix: 0.0 = Phong (quality), 1.0 = Gouraud (performance)\n");
    printf("Values are shown in console every second\n\n");

    // Main loop
    while (device->run())
    {
        if (device->isWindowActive())
        {
            driver->beginScene(true, true, SColor(255, 40, 40, 40)); // Darker background
            smgr->drawAll();
            guienv->drawAll();
            driver->endScene();
        }
    }

    shaderCallback->drop();
    device->drop();
    return 0;
}
This is my "first" functional shader. I was going to implement shadow buffering, but I couldn't...

https://drive.google.com/file/d/13F38wI ... sp=sharing

Currently only works for d3d9.
EVST_VS_2_0
EPST_PS_2_0

You enter and exit the interface with the tab key, and it requires clicking; you move around with the arrow keys.
:mrgreen: :mrgreen:
Last edited by Noiecity on Thu Nov 20, 2025 1:54 pm, edited 2 times in total.
Irrlicht is love, Irrlicht is life, long live to Irrlicht
Sho_VT
Posts: 5
Joined: Sun Sep 07, 2025 8:50 am

Re: [Shader]Specular color + Gouraud + Normalmap

Post by Sho_VT »

Wow, that looks so amazing!
Noiecity
Posts: 324
Joined: Wed Aug 23, 2023 7:22 pm
Contact:

Re: [Shader]Specular color + Gouraud + Normalmap

Post by Noiecity »

Sho_VT wrote: Wed Nov 19, 2025 7:23 am Wow, that looks so amazing!
:idea: If you want the same result but without this shader, you can achieve it by playing with ambient light and a model and shadow volume (with a closed topology model to take advantage of Irrlicht optimizations), you can have a model with more polygons and assign a low poly model for the shadow volume cast
Irrlicht is love, Irrlicht is life, long live to Irrlicht
Post Reply