1
// Copyright (C) 2002-2011 Nikolaus Gebhardt
2
// This file is part of the "Irrlicht Engine".
3
// For conditions of distribution and use, see copyright notice in irrlicht.h
5
#include "IrrCompileConfig.h"
6
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
8
#include "CD3D9ParallaxMapRenderer.h"
9
#include "IMaterialRendererServices.h"
10
#include "IVideoDriver.h"
14
//#define SHADER_EXTERNAL_DEBUG
16
#ifdef SHADER_EXTERNAL_DEBUG
17
#include "CReadFile.h"
24
// 1.1/1.4 Shaders with two lights and vertex based attenuation
26
// Irrlicht Engine D3D9 render path normal map vertex shader
27
const char D3D9_PARALLAX_MAP_VSH[] =
28
";Irrlicht Engine 0.10 D3D9 render path parallax mapping vertex shader\n"\
29
"; c0-3: Transposed world matrix \n"\
30
"; c4: Eye position \n"\
31
"; c8-11: Transposed worldViewProj matrix (Projection * View * World) \n"\
32
"; c12: Light01 position \n"\
33
"; c13: x,y,z: Light01 color; .w: 1/LightRadius� \n"\
34
"; c14: Light02 position \n"\
35
"; c15: x,y,z: Light02 color; .w: 1/LightRadius� \n"\
37
"dcl_position v0 ; position \n"\
38
"dcl_normal v1 ; normal \n"\
39
"dcl_color v2 ; color \n"\
40
"dcl_texcoord0 v3 ; texture coord \n"\
41
"dcl_texcoord1 v4 ; tangent \n"\
42
"dcl_texcoord2 v5 ; binormal \n"\
44
"def c95, 0.5, 0.5, 0.5, 0.5 ; used for moving light vector to ps \n"\
45
"def c96, -1, 1, 1, 1 ; somewhere I've got a bug. flipping the vectors with this fixes it. \n"\
47
"m4x4 oPos, v0, c8 ; transform position to clip space with worldViewProj matrix\n"\
49
"m3x3 r5, v4, c0 ; transform tangent U\n"\
50
"m3x3 r7, v1, c0 ; transform normal W\n"\
51
"m3x3 r6, v5, c0 ; transform binormal V\n"\
53
"m4x4 r4, v0, c0 ; vertex into world position\n"\
54
"add r2, c12, -r4 ; vtxpos - light1 pos\n"\
55
"add r3, c14, -r4 ; vtxpos - light2 pos\n"\
56
"add r1, -c4, r4 ; eye - vtxpos \n"\
58
"dp3 r8.x, r5, r2 ; transform the light1 vector with U, V, W\n"\
59
"dp3 r8.y, r6, r2 \n"\
60
"dp3 r8.z, r7, r2 \n"\
61
"dp3 r9.x, r5, r3 ; transform the light2 vector with U, V, W\n"\
62
"dp3 r9.y, r6, r3 \n"\
63
"dp3 r9.z, r7, r3 \n"\
64
"dp3 r10.x, r5, r1 ; transform the eye vector with U, V, W\n"\
65
"dp3 r10.y, r6, r1 \n"\
66
"dp3 r10.z, r7, r1 \n"\
68
"dp3 r8.w, r8, r8 ; normalize light vector 1 (r8)\n"\
70
"mul r8, r8, r8.w \n"\
71
";mul r8, r8, c96 \n"\
72
"dp3 r9.w, r9, r9 ; normalize light vector 2 (r9)\n"\
74
"mul r9, r9, r9.w \n"\
75
";mul r9, r9, c96 \n"\
76
"dp3 r10.w, r10, r10 ; normalize eye vector (r10)\n"\
77
"rsq r10.w, r10.w \n"\
78
"mul r10, r10, r10.w \n"\
79
"mul r10, r10, c96 \n"\
82
"mad oT2.xyz, r8.xyz, c95, c95 ; move light vector 1 from -1..1 into 0..1 \n"\
83
"mad oT3.xyz, r9.xyz, c95, c95 ; move light vector 2 from -1..1 into 0..1 \n"\
84
"mad oT4.xyz, r10.xyz, c95, c95 ; move eye vector from -1..1 into 0..1 \n"\
86
" ; calculate attenuation of light 1 \n"\
87
"dp3 r2.x, r2.xyz, r2.xyz ; r2.x = r2.x� + r2.y� + r2.z� \n"\
88
"mul r2.x, r2.x, c13.w ; r2.x * attenutation \n"\
89
"rsq r2, r2.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\
90
"mul oD0, r2, c13 ; resulting light color = lightcolor * attenuation \n"\
92
" ; calculate attenuation of light 2 \n"\
93
"dp3 r3.x, r3.xyz, r3.xyz ; r3.x = r3.x� + r3.y� + r3.z� \n"\
94
"mul r3.x, r3.x, c15.w ; r2.x * attenutation \n"\
95
"rsq r3, r3.x ; r2.xyzw = 1/sqrt(r2.x * attenutation)\n"\
96
"mul oD1, r3, c15 ; resulting light color = lightcolor * attenuation \n"\
98
"mov oT0.xy, v3.xy ; move out texture coordinates 1\n"\
99
"mov oT1.xy, v3.xy ; move out texture coordinates 2\n"\
100
"mov oD0.a, v2.a ; move out original alpha value \n"\
104
// Irrlicht Engine D3D9 render path normal map pixel shader version 1.4
105
const char D3D9_PARALLAX_MAP_PSH[] =
106
";Irrlicht Engine 0.10 D3D9 render path parallax mapping pixel shader \n"\
108
";t0: color map texture coord \n"\
109
";t1: normal map texture coords \n"\
110
";t2: light 1 vector in tangent space \n"\
111
";t4: eye vector in tangent space \n"\
112
";v0: light 1 color \n"\
113
";t3: light 2 vector in tangent space \n"\
114
";v1: light 2 color \n"\
115
";v0.a: vertex alpha value \n"\
119
";def c6, 0.02f, 0.02f, 0.02f, 0.0f ; scale factor, now set in callback \n"\
120
"def c5, 0.5f, 0.5f, 0.5f, 0.0f ; for specular division \n"\
122
"texld r1, t1 ; sample (normal.x, normal.y, normal.z, height) \n"\
123
"texcrd r4.xyz, t4 ; fetch eye vector \n"\
124
"texcrd r0.xyz, t0 ; color map \n"\
126
"; original parallax mapping: \n"\
127
";mul r3, r1_bx2.wwww, c6; ; r3 = (height, height, height) * scale \n"\
128
";mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
130
"; modified parallax mapping to reduce swimming effect: \n"\
131
"mul r3, r1_bx2.wwww, r1_bx2.zzzz ; (nh,nh,nh,nh) = (h,h,h,h) * (n.z,n.z,n.z,n.z,) \n"\
132
"mul r3, r3, c6; ; r3 = (nh, nh, nh) * scale \n"\
133
"mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
137
"texld r0, r2 ; load diffuse texture with new tex coord \n"\
138
"texld r1, r2 ; sample normal map \n"\
139
"texcrd r2.xyz, t2 ; fetch light vector 1 \n"\
140
"texcrd r3.xyz, t3 ; fetch light vector 2 \n"\
142
"dp3_sat r5, r1_bx2, r2_bx2 ; normal dot light 1 (_bx2 because moved into 0..1) \n"\
143
"mul r5, r5, v0 ; luminance1 * light color 1 \n"\
145
"dp3_sat r3, r1_bx2, r3_bx2 ; normal dot light 2 (_bx2 because moved into 0..1) \n"\
146
"mad r3, r3, v1, r5 ; (luminance2 * light color 2) + luminance1 \n"\
148
"mul r0.xyz, r0, r3 ; total luminance * base color \n"\
149
"+mov r0.a, v0.a ; write original alpha value \n"\
152
// Irrlicht Engine D3D9 render path normal map pixel shader version 2.0
153
const char D3D9_PARALLAX_MAP_PSH_20[] =
154
";Irrlicht Engine D3D9 render path parallax mapping pixel shader \n"\
157
";t0: color map texture coord \n"\
158
";t1: normal map texture coords \n"\
159
";t2: light 1 vector in tangent space \n"\
160
";t4: eye vector in tangent space \n"\
161
";v0: light 1 color \n"\
162
";t3: light 2 vector in tangent space \n"\
163
";v1: light 2 color \n"\
164
";v0.a: vertex alpha value \n"\
168
"dcl_2d s0 ; Declare the s0 register to be the sampler for stage 0 \n"\
169
"dcl t0.xy ; Declare t0 to have 2D texture coordinates from stage 0 \n"\
170
"dcl t1.xy ; Declare t0 to have 2D texture coordinates from stage 0 \n"\
171
"dcl_2d s1 ; Declare the s1 register to be the sampler for stage 1 \n"\
179
"def c0, -1.0f, -1.0f, -1.0f, -1.0f ; for _bx2 emulation \n"\
180
"def c1, 2.0f, 2.0f, 2.0f, 2.0f ; for _bx2 emulation \n"\
183
"texld r1, t1, s1 ; sample (normal.x, normal.y, normal.z, height) \n"\
184
"mov r4.xyz, t4 ; fetch eye vector \n"\
185
"mov r0.xy, t0 ; color map \n"\
187
"; original parallax mapping: \n"\
188
"; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\
189
"mad r1.xyz, r1, r11, c0; \n"\
191
"mul r3, r1.wwww, c6; ; r3 = (height, height, height) * scale \n"\
193
"; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\
194
"mad r4.xyz, r4, r11, c0; \n"\
196
"mad r2.xy, r3, r4, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
198
"; modified parallax mapping to avoid swimming: \n"\
199
";mul r3, r1_bx2.wwww, r1_bx2.zzzz ; r3 = (h,h,h,h) * (n.z, n.z, n.z, n.z,) \n"\
200
";mul r3, r3, c6; ; r3 = (nh, nh, nh) * scale \n"\
201
";mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
203
"texld r0, r2, s0 ; load diffuse texture with new tex coord \n"\
204
"texld r1, r2, s1 ; sample normal map \n"\
205
"mov r2.xyz, t2 ; fetch light vector 1 \n"\
206
"mov r3.xyz, t3 ; fetch light vector 2 \n"\
208
"; emulate ps1x _bx2, so substract 0.5f and multiply by 2 \n"\
209
"mad r1.xyz, r1, r11, c0; \n"\
210
"mad r2.xyz, r2, r11, c0; \n"\
211
"mad r3.xyz, r3, r11, c0; \n"\
213
"dp3_sat r2, r1, r2 ; normal dot light 1 (_bx2 because moved into 0..1) \n"\
214
"mul r2, r2, v0 ; luminance1 * light color 1 \n"\
216
"dp3_sat r3, r1, r3 ; normal dot light 2 (_bx2 because moved into 0..1) \n"\
217
"mad r3, r3, v1, r2 ; (luminance2 * light color 2) + luminance1 \n"\
219
"mul r0.xyz, r0, r3 ; total luminance * base color \n"\
220
"mov r0.a, v0.a ; write original alpha value \n"\
224
CD3D9ParallaxMapRenderer::CD3D9ParallaxMapRenderer(
225
IDirect3DDevice9* d3ddev, video::IVideoDriver* driver,
226
s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial)
227
: CD3D9ShaderMaterialRenderer(d3ddev, driver, 0, baseMaterial),
232
setDebugName("CD3D9ParallaxMapRenderer");
235
// set this as callback. We could have done this in
236
// the initialization list, but some compilers don't like it.
240
// basicly, this thing simply compiles these hardcoded shaders if the
241
// hardware is able to do them, otherwise it maps to the base material
243
if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_4) ||
244
!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1))
246
// this hardware is not able to do shaders. Fall back to
248
outMaterialTypeNr = driver->addMaterialRenderer(this);
252
// check if already compiled parallax map shaders are there.
254
video::IMaterialRenderer* renderer = driver->getMaterialRenderer(EMT_PARALLAX_MAP_SOLID);
257
// use the already compiled shaders
258
video::CD3D9ParallaxMapRenderer* nmr = (video::CD3D9ParallaxMapRenderer*)renderer;
259
VertexShader = nmr->VertexShader;
261
VertexShader->AddRef();
263
PixelShader = nmr->PixelShader;
265
PixelShader->AddRef();
267
outMaterialTypeNr = driver->addMaterialRenderer(this);
271
#ifdef SHADER_EXTERNAL_DEBUG
273
// quickly load shader from external file
274
io::CReadFile* file = new io::CReadFile("parallax.psh");
275
s32 sz = file->getSize();
276
char* s = new char[sz+1];
280
init(outMaterialTypeNr, D3D9_PARALLAX_MAP_VSH, s);
287
// compile shaders on our own
288
init(outMaterialTypeNr, D3D9_PARALLAX_MAP_VSH, D3D9_PARALLAX_MAP_PSH);
290
#endif // SHADER_EXTERNAL_DEBUG
292
// something failed, use base material
293
if (-1==outMaterialTypeNr)
294
driver->addMaterialRenderer(this);
298
CD3D9ParallaxMapRenderer::~CD3D9ParallaxMapRenderer()
300
if (CallBack == this)
304
bool CD3D9ParallaxMapRenderer::OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype)
306
if (vtxtype != video::EVT_TANGENTS)
308
os::Printer::log("Error: Parallax map renderer only supports vertices of type EVT_TANGENTS", ELL_ERROR);
312
return CD3D9ShaderMaterialRenderer::OnRender(service, vtxtype);
316
void CD3D9ParallaxMapRenderer::OnSetMaterial(const video::SMaterial& material,
317
const video::SMaterial& lastMaterial,
318
bool resetAllRenderstates, video::IMaterialRendererServices* services)
320
CD3D9ShaderMaterialRenderer::OnSetMaterial(material, lastMaterial,
321
resetAllRenderstates, services);
323
CurrentScale = material.MaterialTypeParam;
327
//! Returns the render capability of the material.
328
s32 CD3D9ParallaxMapRenderer::getRenderCapability() const
330
if (Driver->queryFeature(video::EVDF_PIXEL_SHADER_1_4) &&
331
Driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1))
338
//! Called by the engine when the vertex and/or pixel shader constants
339
//! for an material renderer should be set.
340
void CD3D9ParallaxMapRenderer::OnSetConstants(IMaterialRendererServices* services, s32 userData)
342
video::IVideoDriver* driver = services->getVideoDriver();
344
// set transposed world matrix
345
services->setVertexShaderConstant(driver->getTransform(video::ETS_WORLD).getTransposed().pointer(), 0, 4);
349
// The viewpoint is at (0., 0., 0.) in eye space.
350
// Turning this into a vector [0 0 0 1] and multiply it by
351
// the inverse of the view matrix, the resulting vector is the
352
// object space location of the camera.
354
f32 floats[4] = {0,0,0,1};
355
core::matrix4 minv = driver->getTransform(video::ETS_VIEW);
357
minv.multiplyWith1x4Matrix(floats);
358
services->setVertexShaderConstant(floats, 4, 1);
360
// set transposed worldViewProj matrix
361
core::matrix4 worldViewProj;
362
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
363
worldViewProj *= driver->getTransform(video::ETS_VIEW);
364
worldViewProj *= driver->getTransform(video::ETS_WORLD);
365
services->setVertexShaderConstant(worldViewProj.getTransposed().pointer(), 8, 4);
367
// here we've got to fetch the fixed function lights from the
368
// driver and set them as constants
370
const u32 cnt = driver->getDynamicLightCount();
372
for (u32 i=0; i<2; ++i)
377
light = driver->getDynamicLight(i);
380
light.DiffuseColor.set(0,0,0); // make light dark
384
light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation
386
services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.Position), 12+(i*2), 1);
387
services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1);
390
// this is not really necessary in d3d9 (used a def instruction), but to be sure:
391
f32 c95[] = {0.5f, 0.5f, 0.5f, 0.5f};
392
services->setVertexShaderConstant(c95, 95, 1);
393
f32 c96[] = {-1, 1, 1, 1};
394
services->setVertexShaderConstant(c96, 96, 1);
397
f32 factor = 0.02f; // default value
398
if (CurrentScale != 0)
399
factor = CurrentScale;
401
f32 c6[] = {factor, factor, factor, 0};
402
services->setPixelShaderConstant(c6, 6, 1);
406
} // end namespace video
407
} // end namespace irr
409
#endif // _IRR_COMPILE_WITH_DIRECT3D_9_