summaryrefslogtreecommitdiff
path: root/examples/10.Shaders/main.cpp
diff options
context:
space:
mode:
authorMirrorbot <mirrorbot@cogarr.net>2025-12-27 17:53:06 -0600
committerMirrorbot <mirrorbot@cogarr.net>2025-12-27 17:53:06 -0600
commit71e94ee161447b84c0eaabf6567f8fa62262cd3e (patch)
tree391064cc6173a6fe75069af2fdc1978af12f623e /examples/10.Shaders/main.cpp
downloadirrlicht-71e94ee161447b84c0eaabf6567f8fa62262cd3e.tar.gz
irrlicht-71e94ee161447b84c0eaabf6567f8fa62262cd3e.tar.bz2
irrlicht-71e94ee161447b84c0eaabf6567f8fa62262cd3e.zip
Inital commitHEADmaster
Diffstat (limited to 'examples/10.Shaders/main.cpp')
-rw-r--r--examples/10.Shaders/main.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/examples/10.Shaders/main.cpp b/examples/10.Shaders/main.cpp
new file mode 100644
index 0000000..269c47f
--- /dev/null
+++ b/examples/10.Shaders/main.cpp
@@ -0,0 +1,443 @@
+/** Example 010 Shaders
+
+This tutorial shows how to use shaders for D3D8, D3D9, OpenGL, and Cg with the
+engine and how to create new material types with them. It also shows how to
+disable the generation of mipmaps at texture loading, and how to use text scene
+nodes.
+
+This tutorial does not explain how shaders work. I would recommend to read the
+D3D, OpenGL, or Cg documentation, to search a tutorial, or to read a book about
+this.
+
+At first, we need to include all headers and do the stuff we always do, like in
+nearly all other tutorials:
+*/
+#include <irrlicht.h>
+#include <iostream>
+#include "driverChoice.h"
+
+using namespace irr;
+
+#ifdef _MSC_VER
+#pragma comment(lib, "Irrlicht.lib")
+#endif
+
+/*
+Because we want to use some interesting shaders in this tutorials, we need to
+set some data for them to make them able to compute nice colors. In this
+example, we'll use a simple vertex shader which will calculate the color of the
+vertex based on the position of the camera.
+For this, the shader needs the following data: The inverted world matrix for
+transforming the normal, the clip matrix for transforming the position, the
+camera position and the world position of the object for the calculation of the
+angle of light, and the color of the light. To be able to tell the shader all
+this data every frame, we have to derive a class from the
+IShaderConstantSetCallBack interface and override its only method, namely
+OnSetConstants(). This method will be called every time the material is set.
+The method setVertexShaderConstant() of the IMaterialRendererServices interface
+is used to set the data the shader needs. If the user chose to use a High Level
+shader language like HLSL instead of Assembler in this example, you have to set
+the variable name as parameter instead of the register index.
+*/
+
+IrrlichtDevice* device = 0;
+bool UseHighLevelShaders = false;
+bool UseCgShaders = false;
+
+class MyShaderCallBack : public video::IShaderConstantSetCallBack
+{
+public:
+
+ virtual void OnSetConstants(video::IMaterialRendererServices* services,
+ s32 userData)
+ {
+ video::IVideoDriver* driver = services->getVideoDriver();
+
+ // set inverted world matrix
+ // if we are using highlevel shaders (the user can select this when
+ // starting the program), we must set the constants by name.
+
+ core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
+ invWorld.makeInverse();
+
+ if (UseHighLevelShaders)
+ services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
+ else
+ services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
+
+ // set clip matrix
+
+ core::matrix4 worldViewProj;
+ worldViewProj = driver->getTransform(video::ETS_PROJECTION);
+ worldViewProj *= driver->getTransform(video::ETS_VIEW);
+ worldViewProj *= driver->getTransform(video::ETS_WORLD);
+
+ if (UseHighLevelShaders)
+ services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
+ else
+ services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
+
+ // set camera position
+
+ core::vector3df pos = device->getSceneManager()->
+ getActiveCamera()->getAbsolutePosition();
+
+ if (UseHighLevelShaders)
+ services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&pos), 3);
+ else
+ services->setVertexShaderConstant(reinterpret_cast<f32*>(&pos), 8, 1);
+
+ // set light color
+
+ video::SColorf col(0.0f,1.0f,1.0f,0.0f);
+
+ if (UseHighLevelShaders)
+ services->setVertexShaderConstant("mLightColor",
+ reinterpret_cast<f32*>(&col), 4);
+ else
+ services->setVertexShaderConstant(reinterpret_cast<f32*>(&col), 9, 1);
+
+ // set transposed world matrix
+
+ core::matrix4 world = driver->getTransform(video::ETS_WORLD);
+ world = world.getTransposed();
+
+ if (UseHighLevelShaders)
+ {
+ services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
+
+ // set texture, for textures you can use both an int and a float setPixelShaderConstant interfaces (You need it only for an OpenGL driver).
+ s32 TextureLayerID = 0;
+ if (UseHighLevelShaders)
+ services->setPixelShaderConstant("myTexture", &TextureLayerID, 1);
+ }
+ else
+ services->setVertexShaderConstant(world.pointer(), 10, 4);
+ }
+};
+
+/*
+The next few lines start up the engine just like in most other tutorials
+before. But in addition, we ask the user if he wants to use high level shaders
+in this example, if he selected a driver which is capable of doing so.
+*/
+int main()
+{
+ // ask user for driver
+ video::E_DRIVER_TYPE driverType=driverChoiceConsole();
+ if (driverType==video::EDT_COUNT)
+ return 1;
+
+ // ask the user if we should use high level shaders for this example
+ if (driverType == video::EDT_DIRECT3D9 ||
+ driverType == video::EDT_OPENGL)
+ {
+ char i;
+ printf("Please press 'y' if you want to use high level shaders.\n");
+ std::cin >> i;
+ if (i == 'y')
+ {
+ UseHighLevelShaders = true;
+ printf("Please press 'y' if you want to use Cg shaders.\n");
+ std::cin >> i;
+ if (i == 'y')
+ UseCgShaders = true;
+ }
+ }
+
+ // create device
+ device = createDevice(driverType, core::dimension2d<u32>(640, 480));
+
+ if (device == 0)
+ return 1; // could not create selected driver.
+
+ video::IVideoDriver* driver = device->getVideoDriver();
+ scene::ISceneManager* smgr = device->getSceneManager();
+ gui::IGUIEnvironment* gui = device->getGUIEnvironment();
+
+ // Make sure we don't try Cg without support for it
+ if (UseCgShaders && !driver->queryFeature(video::EVDF_CG))
+ {
+ printf("Warning: No Cg support, disabling.\n");
+ UseCgShaders=false;
+ }
+
+ /*
+ Now for the more interesting parts. If we are using Direct3D, we want
+ to load vertex and pixel shader programs, if we have OpenGL, we want to
+ use ARB fragment and vertex programs. I wrote the corresponding
+ programs down into the files d3d8.ps, d3d8.vs, d3d9.ps, d3d9.vs,
+ opengl.ps and opengl.vs. We only need the right filenames now. This is
+ done in the following switch. Note, that it is not necessary to write
+ the shaders into text files, like in this example. You can even write
+ the shaders directly as strings into the cpp source file, and use later
+ addShaderMaterial() instead of addShaderMaterialFromFiles().
+ */
+
+ io::path vsFileName; // filename for the vertex shader
+ io::path psFileName; // filename for the pixel shader
+
+ switch(driverType)
+ {
+ case video::EDT_DIRECT3D8:
+ psFileName = "../../media/d3d8.psh";
+ vsFileName = "../../media/d3d8.vsh";
+ break;
+ case video::EDT_DIRECT3D9:
+ if (UseHighLevelShaders)
+ {
+ // Cg can also handle this syntax
+ psFileName = "../../media/d3d9.hlsl";
+ vsFileName = psFileName; // both shaders are in the same file
+ }
+ else
+ {
+ psFileName = "../../media/d3d9.psh";
+ vsFileName = "../../media/d3d9.vsh";
+ }
+ break;
+
+ case video::EDT_OPENGL:
+ if (UseHighLevelShaders)
+ {
+ if (!UseCgShaders)
+ {
+ psFileName = "../../media/opengl.frag";
+ vsFileName = "../../media/opengl.vert";
+ }
+ else
+ {
+ // Use HLSL syntax for Cg
+ psFileName = "../../media/d3d9.hlsl";
+ vsFileName = psFileName; // both shaders are in the same file
+ }
+ }
+ else
+ {
+ psFileName = "../../media/opengl.psh";
+ vsFileName = "../../media/opengl.vsh";
+ }
+ break;
+ }
+
+ /*
+ In addition, we check if the hardware and the selected renderer is
+ capable of executing the shaders we want. If not, we simply set the
+ filename string to 0. This is not necessary, but useful in this
+ example: For example, if the hardware is able to execute vertex shaders
+ but not pixel shaders, we create a new material which only uses the
+ vertex shader, and no pixel shader. Otherwise, if we would tell the
+ engine to create this material and the engine sees that the hardware
+ wouldn't be able to fulfill the request completely, it would not
+ create any new material at all. So in this example you would see at
+ least the vertex shader in action, without the pixel shader.
+ */
+
+ if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
+ !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
+ {
+ device->getLogger()->log("WARNING: Pixel shaders disabled "\
+ "because of missing driver/hardware support.");
+ psFileName = "";
+ }
+
+ if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
+ !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
+ {
+ device->getLogger()->log("WARNING: Vertex shaders disabled "\
+ "because of missing driver/hardware support.");
+ vsFileName = "";
+ }
+
+ /*
+ Now lets create the new materials. As you maybe know from previous
+ examples, a material type in the Irrlicht engine is set by simply
+ changing the MaterialType value in the SMaterial struct. And this value
+ is just a simple 32 bit value, like video::EMT_SOLID. So we only need
+ the engine to create a new value for us which we can set there. To do
+ this, we get a pointer to the IGPUProgrammingServices and call
+ addShaderMaterialFromFiles(), which returns such a new 32 bit value.
+ That's all.
+
+ The parameters to this method are the following: First, the names of
+ the files containing the code of the vertex and the pixel shader. If
+ you would use addShaderMaterial() instead, you would not need file
+ names, then you could write the code of the shader directly as string.
+ The following parameter is a pointer to the IShaderConstantSetCallBack
+ class we wrote at the beginning of this tutorial. If you don't want to
+ set constants, set this to 0. The last parameter tells the engine which
+ material it should use as base material.
+
+ To demonstrate this, we create two materials with a different base
+ material, one with EMT_SOLID and one with EMT_TRANSPARENT_ADD_COLOR.
+ */
+
+ // create materials
+
+ video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
+ s32 newMaterialType1 = 0;
+ s32 newMaterialType2 = 0;
+
+ if (gpu)
+ {
+ MyShaderCallBack* mc = new MyShaderCallBack();
+
+ // create the shaders depending on if the user wanted high level
+ // or low level shaders:
+
+ if (UseHighLevelShaders)
+ {
+ // Choose the desired shader type. Default is the native
+ // shader type for the driver, for Cg pass the special
+ // enum value EGSL_CG
+ const video::E_GPU_SHADING_LANGUAGE shadingLanguage =
+ UseCgShaders ? video::EGSL_CG:video::EGSL_DEFAULT;
+
+ // create material from high level shaders (hlsl, glsl or cg)
+
+ newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles(
+ vsFileName, "vertexMain", video::EVST_VS_1_1,
+ psFileName, "pixelMain", video::EPST_PS_1_1,
+ mc, video::EMT_SOLID, 0, shadingLanguage);
+
+ newMaterialType2 = gpu->addHighLevelShaderMaterialFromFiles(
+ vsFileName, "vertexMain", video::EVST_VS_1_1,
+ psFileName, "pixelMain", video::EPST_PS_1_1,
+ mc, video::EMT_TRANSPARENT_ADD_COLOR, 0 , shadingLanguage);
+ }
+ else
+ {
+ // create material from low level shaders (asm or arb_asm)
+
+ newMaterialType1 = gpu->addShaderMaterialFromFiles(vsFileName,
+ psFileName, mc, video::EMT_SOLID);
+
+ newMaterialType2 = gpu->addShaderMaterialFromFiles(vsFileName,
+ psFileName, mc, video::EMT_TRANSPARENT_ADD_COLOR);
+ }
+
+ mc->drop();
+ }
+
+ /*
+ Now it's time for testing the materials. We create a test cube and set
+ the material we created. In addition, we add a text scene node to the
+ cube and a rotation animator to make it look more interesting and
+ important.
+ */
+
+ // create test scene node 1, with the new created material type 1
+
+ scene::ISceneNode* node = smgr->addCubeSceneNode(50);
+ node->setPosition(core::vector3df(0,0,0));
+ node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
+ node->setMaterialFlag(video::EMF_LIGHTING, false);
+ node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);
+
+ smgr->addTextSceneNode(gui->getBuiltInFont(),
+ L"PS & VS & EMT_SOLID",
+ video::SColor(255,255,255,255), node);
+
+ scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
+ core::vector3df(0,0.3f,0));
+ node->addAnimator(anim);
+ anim->drop();
+
+ /*
+ Same for the second cube, but with the second material we created.
+ */
+
+ // create test scene node 2, with the new created material type 2
+
+ node = smgr->addCubeSceneNode(50);
+ node->setPosition(core::vector3df(0,-10,50));
+ node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
+ node->setMaterialFlag(video::EMF_LIGHTING, false);
+ node->setMaterialFlag(video::EMF_BLEND_OPERATION, true);
+ node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);
+
+ smgr->addTextSceneNode(gui->getBuiltInFont(),
+ L"PS & VS & EMT_TRANSPARENT",
+ video::SColor(255,255,255,255), node);
+
+ anim = smgr->createRotationAnimator(core::vector3df(0,0.3f,0));
+ node->addAnimator(anim);
+ anim->drop();
+
+ /*
+ Then we add a third cube without a shader on it, to be able to compare
+ the cubes.
+ */
+
+ // add a scene node with no shader
+
+ node = smgr->addCubeSceneNode(50);
+ node->setPosition(core::vector3df(0,50,25));
+ node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
+ node->setMaterialFlag(video::EMF_LIGHTING, false);
+ smgr->addTextSceneNode(gui->getBuiltInFont(), L"NO SHADER",
+ video::SColor(255,255,255,255), node);
+
+ /*
+ And last, we add a skybox and a user controlled camera to the scene.
+ For the skybox textures, we disable mipmap generation, because we don't
+ need mipmaps on it.
+ */
+
+ // add a nice skybox
+
+ driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
+
+ smgr->addSkyBoxSceneNode(
+ driver->getTexture("../../media/irrlicht2_up.jpg"),
+ driver->getTexture("../../media/irrlicht2_dn.jpg"),
+ driver->getTexture("../../media/irrlicht2_lf.jpg"),
+ driver->getTexture("../../media/irrlicht2_rt.jpg"),
+ driver->getTexture("../../media/irrlicht2_ft.jpg"),
+ driver->getTexture("../../media/irrlicht2_bk.jpg"));
+
+ driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
+
+ // add a camera and disable the mouse cursor
+
+ scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
+ cam->setPosition(core::vector3df(-100,50,100));
+ cam->setTarget(core::vector3df(0,0,0));
+ device->getCursorControl()->setVisible(false);
+
+ /*
+ Now draw everything. That's all.
+ */
+
+ int lastFPS = -1;
+
+ while(device->run())
+ if (device->isWindowActive())
+ {
+ driver->beginScene(true, true, video::SColor(255,0,0,0));
+ smgr->drawAll();
+ driver->endScene();
+
+ int fps = driver->getFPS();
+
+ if (lastFPS != fps)
+ {
+ core::stringw str = L"Irrlicht Engine - Vertex and pixel shader example [";
+ str += driver->getName();
+ str += "] FPS:";
+ str += fps;
+
+ device->setWindowCaption(str.c_str());
+ lastFPS = fps;
+ }
+ }
+
+ device->drop();
+
+ return 0;
+}
+
+/*
+Compile and run this, and I hope you have fun with your new little shader
+writing tool :).
+**/