
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;
}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.