~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to examples/10.Shaders/main.cpp

  • Committer: Mantas Kriaučiūnas
  • Date: 2011-07-18 13:06:25 UTC
  • Revision ID: mantas@akl.lt-20110718130625-c5pvifp61e7kj1ol
Included whole irrlicht SVN libraries to work around launchpad recipe issue with quilt, see https://answers.launchpad.net/launchpad/+question/165193

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** Example 010 Shaders
 
2
 
 
3
This tutorial shows how to use shaders for D3D8, D3D9, and OpenGL with the
 
4
engine and how to create new material types with them. It also shows how to
 
5
disable the generation of mipmaps at texture loading, and how to use text scene
 
6
nodes.
 
7
 
 
8
This tutorial does not explain how shaders work. I would recommend to read the
 
9
D3D or OpenGL documentation, to search a tutorial, or to read a book about
 
10
this.
 
11
 
 
12
At first, we need to include all headers and do the stuff we always do, like in
 
13
nearly all other tutorials:
 
14
*/
 
15
#include <irrlicht.h>
 
16
#include <iostream>
 
17
#include "driverChoice.h"
 
18
 
 
19
using namespace irr;
 
20
 
 
21
#ifdef _MSC_VER
 
22
#pragma comment(lib, "Irrlicht.lib")
 
23
#endif
 
24
 
 
25
/*
 
26
Because we want to use some interesting shaders in this tutorials, we need to
 
27
set some data for them to make them able to compute nice colors. In this
 
28
example, we'll use a simple vertex shader which will calculate the color of the
 
29
vertex based on the position of the camera.
 
30
For this, the shader needs the following data: The inverted world matrix for
 
31
transforming the normal, the clip matrix for transforming the position, the
 
32
camera position and the world position of the object for the calculation of the
 
33
angle of light, and the color of the light. To be able to tell the shader all
 
34
this data every frame, we have to derive a class from the
 
35
IShaderConstantSetCallBack interface and override its only method, namely
 
36
OnSetConstants(). This method will be called every time the material is set.
 
37
The method setVertexShaderConstant() of the IMaterialRendererServices interface
 
38
is used to set the data the shader needs. If the user chose to use a High Level
 
39
shader language like HLSL instead of Assembler in this example, you have to set
 
40
the variable name as parameter instead of the register index.
 
41
*/
 
42
 
 
43
IrrlichtDevice* device = 0;
 
44
bool UseHighLevelShaders = false;
 
45
 
 
46
class MyShaderCallBack : public video::IShaderConstantSetCallBack
 
47
{
 
48
public:
 
49
 
 
50
        virtual void OnSetConstants(video::IMaterialRendererServices* services,
 
51
                        s32 userData)
 
52
        {
 
53
                video::IVideoDriver* driver = services->getVideoDriver();
 
54
 
 
55
                // set inverted world matrix
 
56
                // if we are using highlevel shaders (the user can select this when
 
57
                // starting the program), we must set the constants by name.
 
58
 
 
59
                core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
 
60
                invWorld.makeInverse();
 
61
 
 
62
                if (UseHighLevelShaders)
 
63
                        services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
 
64
                else
 
65
                        services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
 
66
 
 
67
                // set clip matrix
 
68
 
 
69
                core::matrix4 worldViewProj;
 
70
                worldViewProj = driver->getTransform(video::ETS_PROJECTION);
 
71
                worldViewProj *= driver->getTransform(video::ETS_VIEW);
 
72
                worldViewProj *= driver->getTransform(video::ETS_WORLD);
 
73
 
 
74
                if (UseHighLevelShaders)
 
75
                        services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
 
76
                else
 
77
                        services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
 
78
 
 
79
                // set camera position
 
80
 
 
81
                core::vector3df pos = device->getSceneManager()->
 
82
                        getActiveCamera()->getAbsolutePosition();
 
83
 
 
84
                if (UseHighLevelShaders)
 
85
                        services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&pos), 3);
 
86
                else
 
87
                        services->setVertexShaderConstant(reinterpret_cast<f32*>(&pos), 8, 1);
 
88
 
 
89
                // set light color
 
90
 
 
91
                video::SColorf col(0.0f,1.0f,1.0f,0.0f);
 
92
 
 
93
                if (UseHighLevelShaders)
 
94
                        services->setVertexShaderConstant("mLightColor",
 
95
                                        reinterpret_cast<f32*>(&col), 4);
 
96
                else
 
97
                        services->setVertexShaderConstant(reinterpret_cast<f32*>(&col), 9, 1);
 
98
 
 
99
                // set transposed world matrix
 
100
 
 
101
                core::matrix4 world = driver->getTransform(video::ETS_WORLD);
 
102
                world = world.getTransposed();
 
103
 
 
104
                if (UseHighLevelShaders)
 
105
                        services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
 
106
                else
 
107
                        services->setVertexShaderConstant(world.pointer(), 10, 4);
 
108
        }
 
109
};
 
110
 
 
111
/*
 
112
The next few lines start up the engine just like in most other tutorials
 
113
before. But in addition, we ask the user if he wants to use high level shaders
 
114
in this example, if he selected a driver which is capable of doing so.
 
115
*/
 
116
int main()
 
117
{
 
118
        // ask user for driver
 
119
        video::E_DRIVER_TYPE driverType=driverChoiceConsole();
 
120
        if (driverType==video::EDT_COUNT)
 
121
                return 1;
 
122
 
 
123
        // ask the user if we should use high level shaders for this example
 
124
        if (driverType == video::EDT_DIRECT3D9 ||
 
125
                 driverType == video::EDT_OPENGL)
 
126
        {
 
127
                char i;
 
128
                printf("Please press 'y' if you want to use high level shaders.\n");
 
129
                std::cin >> i;
 
130
                if (i == 'y')
 
131
                        UseHighLevelShaders = true;
 
132
        }
 
133
 
 
134
        // create device
 
135
 
 
136
        device = createDevice(driverType, core::dimension2d<u32>(640, 480));
 
137
 
 
138
        if (device == 0)
 
139
                return 1; // could not create selected driver.
 
140
 
 
141
 
 
142
        video::IVideoDriver* driver = device->getVideoDriver();
 
143
        scene::ISceneManager* smgr = device->getSceneManager();
 
144
        gui::IGUIEnvironment* gui = device->getGUIEnvironment();
 
145
 
 
146
        /*
 
147
        Now for the more interesting parts. If we are using Direct3D, we want
 
148
        to load vertex and pixel shader programs, if we have OpenGL, we want to
 
149
        use ARB fragment and vertex programs. I wrote the corresponding
 
150
        programs down into the files d3d8.ps, d3d8.vs, d3d9.ps, d3d9.vs,
 
151
        opengl.ps and opengl.vs. We only need the right filenames now. This is
 
152
        done in the following switch. Note, that it is not necessary to write
 
153
        the shaders into text files, like in this example. You can even write
 
154
        the shaders directly as strings into the cpp source file, and use later
 
155
        addShaderMaterial() instead of addShaderMaterialFromFiles().
 
156
        */
 
157
 
 
158
        io::path vsFileName; // filename for the vertex shader
 
159
        io::path psFileName; // filename for the pixel shader
 
160
 
 
161
        switch(driverType)
 
162
        {
 
163
        case video::EDT_DIRECT3D8:
 
164
                psFileName = "../../media/d3d8.psh";
 
165
                vsFileName = "../../media/d3d8.vsh";
 
166
                break;
 
167
        case video::EDT_DIRECT3D9:
 
168
                if (UseHighLevelShaders)
 
169
                {
 
170
                        psFileName = "../../media/d3d9.hlsl";
 
171
                        vsFileName = psFileName; // both shaders are in the same file
 
172
                }
 
173
                else
 
174
                {
 
175
                        psFileName = "../../media/d3d9.psh";
 
176
                        vsFileName = "../../media/d3d9.vsh";
 
177
                }
 
178
                break;
 
179
 
 
180
        case video::EDT_OPENGL:
 
181
                if (UseHighLevelShaders)
 
182
                {
 
183
                        psFileName = "../../media/opengl.frag";
 
184
                        vsFileName = "../../media/opengl.vert";
 
185
                }
 
186
                else
 
187
                {
 
188
                        psFileName = "../../media/opengl.psh";
 
189
                        vsFileName = "../../media/opengl.vsh";
 
190
                }
 
191
                break;
 
192
        }
 
193
 
 
194
        /*
 
195
        In addition, we check if the hardware and the selected renderer is
 
196
        capable of executing the shaders we want. If not, we simply set the
 
197
        filename string to 0. This is not necessary, but useful in this
 
198
        example: For example, if the hardware is able to execute vertex shaders
 
199
        but not pixel shaders, we create a new material which only uses the
 
200
        vertex shader, and no pixel shader. Otherwise, if we would tell the
 
201
        engine to create this material and the engine sees that the hardware
 
202
        wouldn't be able to fullfill the request completely, it would not
 
203
        create any new material at all. So in this example you would see at
 
204
        least the vertex shader in action, without the pixel shader.
 
205
        */
 
206
 
 
207
        if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
 
208
                !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
 
209
        {
 
210
                device->getLogger()->log("WARNING: Pixel shaders disabled "\
 
211
                        "because of missing driver/hardware support.");
 
212
                psFileName = "";
 
213
        }
 
214
 
 
215
        if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
 
216
                !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
 
217
        {
 
218
                device->getLogger()->log("WARNING: Vertex shaders disabled "\
 
219
                        "because of missing driver/hardware support.");
 
220
                vsFileName = "";
 
221
        }
 
222
 
 
223
        /*
 
224
        Now lets create the new materials. As you maybe know from previous
 
225
        examples, a material type in the Irrlicht engine is set by simply
 
226
        changing the MaterialType value in the SMaterial struct. And this value
 
227
        is just a simple 32 bit value, like video::EMT_SOLID. So we only need
 
228
        the engine to create a new value for us which we can set there. To do
 
229
        this, we get a pointer to the IGPUProgrammingServices and call
 
230
        addShaderMaterialFromFiles(), which returns such a new 32 bit value.
 
231
        That's all.
 
232
 
 
233
        The parameters to this method are the following: First, the names of
 
234
        the files containing the code of the vertex and the pixel shader. If
 
235
        you would use addShaderMaterial() instead, you would not need file
 
236
        names, then you could write the code of the shader directly as string.
 
237
        The following parameter is a pointer to the IShaderConstantSetCallBack
 
238
        class we wrote at the beginning of this tutorial. If you don't want to
 
239
        set constants, set this to 0. The last paramter tells the engine which
 
240
        material it should use as base material.
 
241
 
 
242
        To demonstrate this, we create two materials with a different base
 
243
        material, one with EMT_SOLID and one with EMT_TRANSPARENT_ADD_COLOR.
 
244
        */
 
245
 
 
246
        // create materials
 
247
 
 
248
        video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
 
249
        s32 newMaterialType1 = 0;
 
250
        s32 newMaterialType2 = 0;
 
251
 
 
252
        if (gpu)
 
253
        {
 
254
                MyShaderCallBack* mc = new MyShaderCallBack();
 
255
 
 
256
                // create the shaders depending on if the user wanted high level
 
257
                // or low level shaders:
 
258
 
 
259
                if (UseHighLevelShaders)
 
260
                {
 
261
                        // create material from high level shaders (hlsl or glsl)
 
262
 
 
263
                        newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles(
 
264
                                vsFileName, "vertexMain", video::EVST_VS_1_1,
 
265
                                psFileName, "pixelMain", video::EPST_PS_1_1,
 
266
                                mc, video::EMT_SOLID);
 
267
 
 
268
                        newMaterialType2 = gpu->addHighLevelShaderMaterialFromFiles(
 
269
                                vsFileName, "vertexMain", video::EVST_VS_1_1,
 
270
                                psFileName, "pixelMain", video::EPST_PS_1_1,
 
271
                                mc, video::EMT_TRANSPARENT_ADD_COLOR);
 
272
                }
 
273
                else
 
274
                {
 
275
                        // create material from low level shaders (asm or arb_asm)
 
276
 
 
277
                        newMaterialType1 = gpu->addShaderMaterialFromFiles(vsFileName,
 
278
                                psFileName, mc, video::EMT_SOLID);
 
279
 
 
280
                        newMaterialType2 = gpu->addShaderMaterialFromFiles(vsFileName,
 
281
                                psFileName, mc, video::EMT_TRANSPARENT_ADD_COLOR);
 
282
                }
 
283
 
 
284
                mc->drop();
 
285
        }
 
286
 
 
287
        /*
 
288
        Now it's time for testing the materials. We create a test cube and set
 
289
        the material we created. In addition, we add a text scene node to the
 
290
        cube and a rotation animator to make it look more interesting and
 
291
        important.
 
292
        */
 
293
 
 
294
        // create test scene node 1, with the new created material type 1
 
295
 
 
296
        scene::ISceneNode* node = smgr->addCubeSceneNode(50);
 
297
        node->setPosition(core::vector3df(0,0,0));
 
298
        node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
 
299
        node->setMaterialFlag(video::EMF_LIGHTING, false);
 
300
        node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);
 
301
 
 
302
        smgr->addTextSceneNode(gui->getBuiltInFont(),
 
303
                        L"PS & VS & EMT_SOLID",
 
304
                        video::SColor(255,255,255,255), node);
 
305
 
 
306
        scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
 
307
                        core::vector3df(0,0.3f,0));
 
308
        node->addAnimator(anim);
 
309
        anim->drop();
 
310
 
 
311
        /*
 
312
        Same for the second cube, but with the second material we created.
 
313
        */
 
314
 
 
315
        // create test scene node 2, with the new created material type 2
 
316
 
 
317
        node = smgr->addCubeSceneNode(50);
 
318
        node->setPosition(core::vector3df(0,-10,50));
 
319
        node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
 
320
        node->setMaterialFlag(video::EMF_LIGHTING, false);
 
321
        node->setMaterialFlag(video::EMF_BLEND_OPERATION, true);
 
322
        node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);
 
323
 
 
324
        smgr->addTextSceneNode(gui->getBuiltInFont(),
 
325
                        L"PS & VS & EMT_TRANSPARENT",
 
326
                        video::SColor(255,255,255,255), node);
 
327
 
 
328
        anim = smgr->createRotationAnimator(core::vector3df(0,0.3f,0));
 
329
        node->addAnimator(anim);
 
330
        anim->drop();
 
331
 
 
332
        /*
 
333
        Then we add a third cube without a shader on it, to be able to compare
 
334
        the cubes.
 
335
        */
 
336
 
 
337
        // add a scene node with no shader
 
338
 
 
339
        node = smgr->addCubeSceneNode(50);
 
340
        node->setPosition(core::vector3df(0,50,25));
 
341
        node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
 
342
        node->setMaterialFlag(video::EMF_LIGHTING, false);
 
343
        smgr->addTextSceneNode(gui->getBuiltInFont(), L"NO SHADER",
 
344
                video::SColor(255,255,255,255), node);
 
345
 
 
346
        /*
 
347
        And last, we add a skybox and a user controlled camera to the scene.
 
348
        For the skybox textures, we disable mipmap generation, because we don't
 
349
        need mipmaps on it.
 
350
        */
 
351
 
 
352
        // add a nice skybox
 
353
 
 
354
        driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
 
355
 
 
356
        smgr->addSkyBoxSceneNode(
 
357
                driver->getTexture("../../media/irrlicht2_up.jpg"),
 
358
                driver->getTexture("../../media/irrlicht2_dn.jpg"),
 
359
                driver->getTexture("../../media/irrlicht2_lf.jpg"),
 
360
                driver->getTexture("../../media/irrlicht2_rt.jpg"),
 
361
                driver->getTexture("../../media/irrlicht2_ft.jpg"),
 
362
                driver->getTexture("../../media/irrlicht2_bk.jpg"));
 
363
 
 
364
        driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
 
365
 
 
366
        // add a camera and disable the mouse cursor
 
367
 
 
368
        scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
 
369
        cam->setPosition(core::vector3df(-100,50,100));
 
370
        cam->setTarget(core::vector3df(0,0,0));
 
371
        device->getCursorControl()->setVisible(false);
 
372
 
 
373
        /*
 
374
        Now draw everything. That's all.
 
375
        */
 
376
 
 
377
        int lastFPS = -1;
 
378
 
 
379
        while(device->run())
 
380
                if (device->isWindowActive())
 
381
        {
 
382
                driver->beginScene(true, true, video::SColor(255,0,0,0));
 
383
                smgr->drawAll();
 
384
                driver->endScene();
 
385
 
 
386
                int fps = driver->getFPS();
 
387
 
 
388
                if (lastFPS != fps)
 
389
                {
 
390
                        core::stringw str = L"Irrlicht Engine - Vertex and pixel shader example [";
 
391
                        str += driver->getName();
 
392
                        str += "] FPS:";
 
393
                        str += fps;
 
394
 
 
395
                        device->setWindowCaption(str.c_str());
 
396
                        lastFPS = fps;
 
397
                }
 
398
        }
 
399
 
 
400
        device->drop();
 
401
 
 
402
        return 0;
 
403
}
 
404
 
 
405
/*
 
406
Compile and run this, and I hope you have fun with your new little shader
 
407
writing tool :).
 
408
**/