Lighting Demo Sample Demo
1. Demo Introduction
In our light demonstration program, we implemented 3 different light sources: 1 parallel lights, one point light, and one spotlight. The position of the parallel light is fixed, the light revolves around the terrain, and the spotlight follows the camera and points to the camera's viewing direction. The lighting demo program just made some changes to the water-wave demo program in the previous chapter. 2. Effect File
=============================================================================//Lighting.fx by Frank Luna (C) 2011
All rights Reserved.
Transforms and lights geometry. ============================================================================= #include "LightHelper.fx" Cbuffer
cbperframe {directionallight gdirlight;
Pointlight Gpointlight;
SpotLight Gspotlight;
FLOAT3 GEYEPOSW;
};
Cbuffer Cbperobject {float4x4 gworld;
float4x4 Gworldinvtranspose;
float4x4 gworldviewproj;
Material gmaterial;
};
struct Vertexin {FLOAT3 posl:position;
FLOAT3 Normall:normal;
};
struct Vertexout {float4 posh:sv_position;
FLOAT3 posw:position;
FLOAT3 Normalw:normal;
};
Vertexout VS (Vertexin vin) {vertexout vout; Convert to World Space Vout. PosW = Mul (Float4 (VIN).
POSL, 1.0f), Gworld). xyz; Vout. NORMALW = Mul (vin.
Normall, (float3x3) gworldinvtranspose); Convert to homogeneous trim space vout. PosH = Mul (Float4 (VIN). POSL, 1.0f), gworldviewproj);
return vout; } float4 PS (vertexout pin): sv_target {//interpolated normals require a re-normalized pin. NORMALW = Normalize (pin.
NORMALW); FLOAT3 Toeyew = Normalize (geyeposw-pin.
PosW);
Initialize illumination variable float4 ambient = FLOAT4 (0.0f, 0.0f, 0.0f, 0.0f);
FLOAT4 diffuse = FLOAT4 (0.0f, 0.0f, 0.0f, 0.0f);
FLOAT4 spec = FLOAT4 (0.0f, 0.0f, 0.0f, 0.0f);
Contribution of each light source Float4 A, D, S; Computedirectionallight (gmaterial, Gdirlight, pin.
NORMALW, Toeyew, A, D, S);
Ambient + = A;
Diffuse + = D;
Spec + = S; Computepointlight (gmaterial, Gpointlight, pin. PosW, PIN.
NORMALW, Toeyew, A, D, S);
Ambient + = A;
Diffuse + = D;
Spec + = S; Computespotlight (gmaterial, Gspotlight, pin. PosW, PIN.
NORMALW, Toeyew, A, D, S);
Ambient + = A;
Diffuse + = D;
Spec + = S;
FLOAT4 Litcolor = ambient + diffuse + spec;
The Alpha Litcolor.a = Gmaterial.diffuse.a is usually extracted from the diffuse material;
return litcolor;
} technique11 Lighttech {pass P0 {SetVertexShader (Compileshader (Vs_5_0, vs ()));
Setgeometryshader (NULL);
SetPixelShader (Compileshader (PS_5_0, PS ())); }
}
3. C + + program code
The illumination calculation requires the information of the surface normals. We define the normals at the vertex level, and then interpolate the normals for pixel-wise illumination calculations. In addition, it is now not necessary to specify the vertex color, the color of the surface can be the light equation to be obtained by pixel. The input layout description looks like this:
D3d11_input_element_desc vertexdesc[] =
{
{"POSITION", 0, dxgi_format_r32g32b32_float, 0, 0,
d3d11_input _per_vertex_data, 0},
{"NORMAL", 0, dxgi_format_r32g32b32_float, 0, D3d11_input_per_vertex_data,
0}
};
In the application, we have defined three light sources and two materials.
DirectionalLight Mdirlight;
Pointlight Mpointlight;
SpotLight Mspotlight;
Material Mlandmat;
Material Mwavesmat;
They are initialized in the constructor:
Lightingapp::lightingapp (hinstance hinstance): D3dapp (HINSTANCE), MLANDVB (0), Mlandib (0), MWAVESVB (0), MWavesIB (0), MFX (0), MTech (0), Mfxworld (0), Mfxworldinvtranspose (0), MFXEYEPOSW (0), mfxdirlight (0), mfxpointlight (0), Mfxspotlight (0), mfxmaterial (0), mfxworldviewproj (0), minputlayout (0), MEYEPOSW (0.0f, 0.0f, 0.0f), Mtheta (1.5f*mathhelper::P i), MP
Hi (0.1f*mathhelper::P i), Mradius (80.0f) {...//directional light mdirlight.ambient = XMFLOAT4 (0.2f, 0.2f, 0.2f, 1.0f);
Mdirlight.diffuse = XMFLOAT4 (0.5f, 0.5f, 0.5f, 1.0f);
Mdirlight.specular = XMFLOAT4 (0.5f, 0.5f, 0.5f, 1.0f);
Mdirlight.direction = XMFLOAT3 (0.57735f, -0.57735f, 0.57735f);
The location of the light is constantly changing position in each frame with the update scene function mpointlight.ambient = XMFLOAT4 (0.3f, 0.3f, 0.3f, 1.0f);
Mpointlight.diffuse = XMFLOAT4 (0.7f, 0.7f, 0.7f, 1.0f);
Mpointlight.specular = XMFLOAT4 (0.7f, 0.7f, 0.7f, 1.0f);
Mpointlight.att = XMFLOAT3 (0.0f, 0.1f, 0.0f);
Mpointlight.range = 25.0f; The position and orientation of the spotlight as you update the scene function at every frameTo change position mspotlight.ambient = XMFLOAT4 (0.0f, 0.0f, 0.0f, 1.0f);
Mspotlight.diffuse = XMFLOAT4 (1.0f, 1.0f, 0.0f, 1.0f);
Mspotlight.specular = XMFLOAT4 (1.0f, 1.0f, 1.0f, 1.0f);
Mspotlight.att = XMFLOAT3 (1.0f, 0.0f, 0.0f);
Mspotlight.spot = 96.0f;
Mspotlight.range = 10000.0f;
Mlandmat.ambient = XMFLOAT4 (0.48f, 0.77f, 0.46f, 1.0f);
Mlandmat.diffuse = XMFLOAT4 (0.48f, 0.77f, 0.46f, 1.0f);
Mlandmat.specular = XMFLOAT4 (0.2f, 0.2f, 0.2f, 16.0f);
Mwavesmat.ambient = XMFLOAT4 (0.137f, 0.42f, 0.556f, 1.0f);
Mwavesmat.diffuse = XMFLOAT4 (0.137f, 0.42f, 0.556f, 1.0f);
Mwavesmat.specular = XMFLOAT4 (0.8f, 0.8f, 0.8f, 96.0f); }
When using multiple light sources, care must be taken not to let the color go out of range. So you need to test the intensity of ambient light, diffuse light, and specular light, as well as to test the illumination range and attenuation. Sometimes it's easy to forget to use colored lights, but they can be used to produce different effects. For example, if you use a parallel light on a solar model, the sun itself is set to orange, and you can also set the color of the light to orange, so that the objects in the scene will be orange-coloured. The alien spacecraft explodes with light blue and red light usually indicates an emergency.
As mentioned earlier, point lights and spotlights are active; This work is implemented in the Updatescene method:
void Lightingapp::updatescene (float dt)
{
...
//light source animation////
Let point light in the ground above the circle
mpointlight.position.x = 70.0F*COSF (0.2f*mtimer.totaltime ());
Mpointlight.position.z = 70.0f*sinf (0.2f*mtimer.totaltime ());
MPOINTLIGHT.POSITION.Y = Mathhelper::max (Gethillheight (mpointlight.position.x,
mpointlight.position.z), -3.0f ) + 10.0f;
The position of the spotlight is the same as that of the observer, and the direction of illumination is the same as the direction of observation;
//The effect is as if the observer took a flashlight and shone it in the scene.
mspotlight.position = MEYEPOSW;
XMSTOREFLOAT3 (&mspotlight.direction, Xmvector3normalize (Target-pos));
}
Basically, the point Light will move along a circular trajectory on the XZ plane, except that it always stays above the surface and above the water. The position of the spotlight is the same as that of the observer, and the illumination direction is the same as the observation direction; the effect is as if the observer took a flashlight and shone it in the scene.
Finally, we want to assign lights and materials to effect before rendering:
void Lightingapp::D rawscene ()
{
...
Set per frame constants.
Mfxdirlight->setrawvalue (&mdirlight, 0, sizeof (mdirlight));
Mfxpointlight->setrawvalue (&mpointlight, 0, sizeof (mpointlight));
Mfxspotlight->setrawvalue (&mspotlight, 0, sizeof (mspotlight));
Mfxeyeposw->setrawvalue (&MEYEPOSW, 0, sizeof (MEYEPOSW));
..... D3dx11_technique_desc Techdesc;
Mtech->getdesc (&TECHDESC);
for (UINT p = 0; p < techdesc.passes; ++p)
{
...
Mfxmaterial->setrawvalue (&mlandmat, 0, sizeof (Mlandmat));
/* Draw Valleys ... */
...
Mfxmaterial->setrawvalue (&mwavesmat, 0, sizeof (Mwavesmat));
/* Draw water waves ... *
/} HR (mswapchain->present (0, 0));
}
4. Calculate Terrain normals
Because our terrain surface is generated by a function y = f (x,z), we can calculate the normal vector directly using calculus instead of the normal average technique described in section 7.2.1. To calculate the normal vector for each point on the surface, we must generate two tangent vectors in the direction of +x and +z by partial derivatives:
Because our terrain surface is generated by a function y = f (x,z), we can calculate the normal vector directly using calculus instead of the normal average technique described in section 7.2.1. To calculate the normal vector for each point on the surface, we must generate two tangent vectors in the direction of +x and +z by partial derivatives:
The functions we use to generate ground grids are:
The partial derivative is:
Surface normals of surface points (x,f (x,z), z) are:
Note that the surface normal is not a normalized vector and must be normalized before performing a light calculation.
We only perform the above normal calculations on each vertex to get the vertex normals:
XMFLOAT3 Lightingapp::gethillnormal (float x, float z) const
{
//n = (-df/dx, 1,-df/dz)
XMFLOAT3 N (
- 0.03F*Z*COSF (0.1f*x)-0.3F*COSF (0.1f*z),
1.0f,
-0.3f*sinf (0.1f*x) + 0.03f*x*sinf (0.1f*z));
Xmvector unitnormal = xmvector3normalize (XMLOADFLOAT3 (&n));
XMSTOREFLOAT3 (&n, unitnormal);
return n;
}
For the surface of a water body, the normal vector is basically the same, except that we do not have a formula for generating water. However, readers can estimate the tangent vector on each vertex using the finite difference graph (finite difference scheme) (see [Lengyel02] or any book on numerical analysis for details).
Note: If your calculus is wasted, don't worry, because its role in this book is not very important. We use calculus here just to mathematically generate a curved surface geometry so that we can render some interesting objects in the demo program. At the end of this book, we will explain how to load and use 3D mesh files exported by 3D modeling software. 5. Screenshot of program running result
Project complete source code, please go to DirectX11 Dragon Book official website to download, suggest use vs reading source code.