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